From 34b3e4cfcec862740fcadefc5e7926c23cae548a Mon Sep 17 00:00:00 2001 From: Sergii <1159966+stalniy@users.noreply.github.com> Date: Fri, 23 Oct 2026 15:26:12 +0200 Subject: [PATCH] feat: adds ucast+drizzle example --- .github/workflows/blog-api-tests.yml | 5 + README.md | 2 +- packages/fastify-ucast-sql-blog/README.md | 22 ++- packages/fastify-ucast-sql-blog/package.json | 7 +- .../fastify-ucast-sql-blog/src/createApp.ts | 4 +- .../src/repositories/drizzle-v1.ts | 159 ++++++++++++++++ .../src/repositories/types.ts | 2 +- packages/fastify-ucast-sql-blog/src/schema.ts | 2 +- packages/fastify-ucast-sql-blog/src/seed.ts | 2 +- packages/fastify-ucast-sql-blog/src/server.ts | 5 +- .../src/services/orm.ts | 5 +- pnpm-lock.yaml | 170 +++++++++++++++++- 12 files changed, 366 insertions(+), 19 deletions(-) create mode 100644 packages/fastify-ucast-sql-blog/src/repositories/drizzle-v1.ts diff --git a/.github/workflows/blog-api-tests.yml b/.github/workflows/blog-api-tests.yml index 2641146..f6a8ab5 100644 --- a/.github/workflows/blog-api-tests.yml +++ b/.github/workflows/blog-api-tests.yml @@ -51,6 +51,11 @@ jobs: mongodb: false implementation: objection database: ./blog-objection.sqlite + - name: fastify-ucast-sql-blog (Drizzle 1.0.0-rc.5) + package: fastify-ucast-sql-blog + mongodb: false + implementation: drizzle-v1 + database: ./blog-drizzle-v1.sqlite env: DATABASE_URL: ${{ matrix.database }} AUTH_SECRET: ci-blog-secret diff --git a/README.md b/README.md index 7fd5bf7..e2a759f 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Currently this repository contains the next examples: | [CASL Vue blog](./packages/vue-blog) | ACL in Blog app with REST API | | | [CASL Express API](./packages/express-blog) | ACL in REST API for Blog app | | | [CASL Fastify+Prisma API](./packages/fastify-prisma-blog) | ACL in REST API for Blog app | | -| [CASL Fastify+UCAST SQL API](./packages/fastify-ucast-sql-blog) | ACL in REST API for Blog app with MikroORM, TypeORM, Sequelize and Objection | | +| [CASL Fastify+UCAST SQL API](./packages/fastify-ucast-sql-blog) | ACL in REST API for Blog app with MikroORM, TypeORM, Sequelize, Objection and Drizzle ORM (1.x RC) | | [CASL HelloWorld]: https://codesandbox.io/s/github/stalniy/casl-examples/tree/master/packages/hello-world [CASL React Todo]: https://codesandbox.io/s/github/stalniy/casl-examples/tree/master/packages/react-todo diff --git a/packages/fastify-ucast-sql-blog/README.md b/packages/fastify-ucast-sql-blog/README.md index f015436..70a249e 100644 --- a/packages/fastify-ucast-sql-blog/README.md +++ b/packages/fastify-ucast-sql-blog/README.md @@ -1,6 +1,6 @@ # Fastify blog with UCAST SQL -This example shares the Fastify blog API while selecting MikroORM 7.x, TypeORM 1.x, Sequelize 6.x, or Objection 3.x before startup. All repositories run CASL's effective rule AST through the matching `@ucast/sql` ORM interpreter. SQLite files are independent: selecting another implementation does not move or migrate data. Sequelize uses the `sqlite3` driver; Objection uses Knex with `better-sqlite3`. +This example shares the Fastify blog API while selecting MikroORM 7.x, TypeORM 1.x, Sequelize 6.x, Objection 3.x, or Drizzle ORM 1.0.0-rc.5 before startup. All repositories run CASL's effective rule AST through the matching `@ucast/sql` ORM interpreter. SQLite files are independent: selecting another implementation does not move or migrate data. Sequelize uses the `sqlite3` driver; Objection uses Knex with `better-sqlite3`. Drizzle uses `better-sqlite3`. ## Local setup @@ -14,8 +14,8 @@ Pick one implementation and prepare its SQLite file. The schema command is expli ```sh cd packages/fastify-ucast-sql-blog -pnpm schema:mikro # or schema:typeorm or schema:sequelize or schema:objection -pnpm seed:mikro # or schema:typeorm or schema:sequelize or schema:objection +pnpm schema:mikro # or schema:typeorm or schema:sequelize or schema:objection or schema:drizzle-v1 +pnpm seed:mikro # or seed:typeorm or seed:sequelize or seed:objection or seed:drizzle-v1 ``` it will automatically create `blog-${orm}.sqlite` database file in the current directory. It's also possible to configure this by specifying `DATABASE_URL` environment variable, for example: @@ -31,7 +31,7 @@ To start the matching implementation. `DATABASE_URL` accepts a relative or absol IMPLEMENTATION=mikro pnpm start ``` -`IMPLEMENTATION` variable can be one of `mikro`, `typeorm`, `sequelize`, or `objection`. The default is `mikro`; the default database filename is `blog-${IMPLEMENTATION}$.sqlite`, also possible to set `DATABASE_URL` to provide custom database path. The server never creates or clears schema/data on startup. It closes the selected ORM connection when Fastify closes. +`IMPLEMENTATION` variable can be one of `mikro`, `typeorm`, `sequelize`, `objection`, or `drizzle-v1`. The default is `mikro`; the default database filename is `blog-${IMPLEMENTATION}.sqlite`, also possible to set `DATABASE_URL` to provide custom database path. The server never creates or clears schema/data on startup. It closes the selected ORM connection when Fastify closes. ## API and rules @@ -39,12 +39,22 @@ The API stays under `/api`: `POST /session`, user create/read/update routes, and `src/createApp.ts` composes the app and registers the `auth`, `users`, and `articles` modules under `src/modules`, matching the Fastify Prisma example. Each module exposes its routes through `index.ts`; authentication, ability definitions, and token services live in the auth module. Shared entity types remain in `src/domain.ts`, and app configuration is defined in `src/config.ts`. -`src/services/orm.ts` initializes the selected ORM and returns a `MikroBlogRepository`, `TypeOrmBlogRepository`, `SequelizeBlogRepository`, or `ObjectionBlogRepository` instance to `createApp.ts`. The app injects that repository into `BlogService` and exposes the service to routes and authentication. `src/services/blog.ts` owns record permissions, create and publish checks, and missing/forbidden results. It derives query filters from CASL rules through `src/services/authorization.ts` so lists and counts include only accessible records. +`src/services/orm.ts` initializes the selected ORM and returns a `MikroBlogRepository`, `TypeOrmBlogRepository`, `SequelizeBlogRepository`, `ObjectionBlogRepository`, or `DrizzleV1BlogRepository` instance to `createApp.ts`. The app injects that repository into `BlogService` and exposes the service to routes and authentication. `src/services/blog.ts` owns record permissions, create and publish checks, and missing/forbidden results. It derives query filters from CASL rules through `src/services/authorization.ts` so lists and counts include only accessible records. Entity schemas live alongside their corresponding repository classes in `src/repositories`. Repositories only perform database operations: metadata inspection, query construction, UCAST interpretation of supplied filters, and writes. They do not receive abilities or decide permissions. Their metadata keeps property names and physical column names identical, including `author`, `createdAt`, and `updatedAt`. Updates and deletes apply the service's filters in the changing SQL statement. MikroORM creates its interpreter once with the ORM metadata and platform, then adds a fresh predicate to each query. TypeORM adds the interpreter's `Brackets` predicate with `andWhere`. Sequelize combines the interpreter's `where` expression with the requested identifier using `Op.and` and escapes its replacements with Sequelize's SQL-aware replacement utility before composing the query, because Sequelize 6 updates generate binds that cannot coexist with replacements. It selects the model alias for reads and the table name for writes. Objection passes its model to the interpreter and adds the returned predicate with `where`. It binds its models to each repository's Knex connection. Both expose the author through `createdBy` while retaining the scalar `author` column, and normalize timestamps to domain `Date` values. +The Drizzle repository lives in `src/repositories/drizzle-v1.ts` and uses exactly `drizzle-orm@1.0.0-rc.5`. It defines native relations with `defineRelations()` and uses the `@ucast/sql/drizzle-orm/v1.x` interpreter. Predicates are combined with identifiers in reads, updates, and deletes. An explicit join loads only the author's ID and email as `createdBy`; text timestamps are converted to domain `Date` values. + +For example, to run Drizzle v1: + +```sh +pnpm schema:drizzle-v1 +pnpm seed:drizzle-v1 +IMPLEMENTATION=drizzle-v1 pnpm start +``` + ## Testing -To test api contracts, run server running on port 3000 (default), and then run `npm run test:server` from `packages/express-blog`. +CI runs the shared API contract suite against every implementation, including Drizzle 1.0.0-rc.5. To test API contracts locally, run server running on port 3000 (default), and then run `npm run test:server` from `packages/express-blog`. diff --git a/packages/fastify-ucast-sql-blog/package.json b/packages/fastify-ucast-sql-blog/package.json index 9894062..4bc1dad 100644 --- a/packages/fastify-ucast-sql-blog/package.json +++ b/packages/fastify-ucast-sql-blog/package.json @@ -13,7 +13,9 @@ "schema:sequelize": "tsx src/schema.ts sequelize", "seed:sequelize": "tsx src/seed.ts sequelize", "schema:objection": "tsx src/schema.ts objection", - "seed:objection": "tsx src/seed.ts objection" + "seed:objection": "tsx src/seed.ts objection", + "schema:drizzle-v1": "tsx src/schema.ts drizzle-v1", + "seed:drizzle-v1": "tsx src/seed.ts drizzle-v1" }, "dependencies": { "@casl/ability": "^7.0.0", @@ -29,7 +31,8 @@ "reflect-metadata": "^0.2.2", "sequelize": "^6.37.8", "sqlite3": "^5.1.7", - "typeorm": "^1.0.0" + "typeorm": "^1.0.0", + "drizzle-orm": "1.0.0-rc.5" }, "devDependencies": { "@types/better-sqlite3": "^7.6.13", diff --git a/packages/fastify-ucast-sql-blog/src/createApp.ts b/packages/fastify-ucast-sql-blog/src/createApp.ts index a9aa105..0c3c5c6 100644 --- a/packages/fastify-ucast-sql-blog/src/createApp.ts +++ b/packages/fastify-ucast-sql-blog/src/createApp.ts @@ -8,8 +8,8 @@ import { usersRoutes } from './modules/users/index.js'; import { articlesRoutes } from './modules/articles/index.js'; export async function createApp(config: AppConfig, injectedRepository?: BlogRepository): Promise { - if (!['mikro', 'typeorm', 'sequelize', 'objection'].includes(config.IMPLEMENTATION)) { - throw new Error(`Unknown implementation "${config.IMPLEMENTATION}". Use mikro, typeorm, sequelize or objection.`); + if (!['mikro', 'typeorm', 'sequelize', 'objection', 'drizzle-v1'].includes(config.IMPLEMENTATION)) { + throw new Error(`Unknown implementation "${config.IMPLEMENTATION}". Use mikro, typeorm, sequelize, objection or drizzle-v1.`); } const repository = injectedRepository ?? await initializeOrm(config.IMPLEMENTATION, config.DATABASE_URL); const fastify = Fastify({ logger: true }); diff --git a/packages/fastify-ucast-sql-blog/src/repositories/drizzle-v1.ts b/packages/fastify-ucast-sql-blog/src/repositories/drizzle-v1.ts new file mode 100644 index 0000000..56fdac1 --- /dev/null +++ b/packages/fastify-ucast-sql-blog/src/repositories/drizzle-v1.ts @@ -0,0 +1,159 @@ +import Database from 'better-sqlite3'; +import { drizzle } from 'drizzle-orm/better-sqlite3'; +import { and, asc, count, desc, eq, getColumns, defineRelations, sql } from 'drizzle-orm'; +import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'; +import { createDrizzleInterpreter } from '@ucast/sql/drizzle-orm/v1.x'; +import type { Article, Role, User } from '../domain.js'; +import type { ArticlePatch, BlogRepository, QueryFilter, UserPatch } from './types.js'; + +export const users = sqliteTable('users', { + id: text('id').primaryKey().notNull(), + name: text('name'), + email: text('email').notNull().unique(), + password: text('password').notNull(), + role: text('role').$type().notNull(), + createdAt: text('createdAt').notNull(), + updatedAt: text('updatedAt').notNull(), +}); + +export const articles = sqliteTable('articles', { + id: text('id').primaryKey().notNull(), + author: text('author').notNull().references(() => users.id), + title: text('title').notNull(), + body: text('body').notNull(), + published: integer('published', { mode: 'boolean' }).notNull().default(false), + createdAt: text('createdAt').notNull(), + updatedAt: text('updatedAt').notNull(), +}); + +export const articleRelations = defineRelations({ users, articles }, r => ({ + articles: { + createdBy: r.one.users({ from: r.articles.author, to: r.users.id, optional: false }), + }, +})); +const schema = { users, articles, articleRelations }; +const interpret = createDrizzleInterpreter({ schema }); + +type UserRow = typeof users.$inferSelect; +type ArticleRow = typeof articles.$inferSelect & { createdBy: Pick }; + +export class DrizzleV1BlogRepository implements BlogRepository { + readonly kind = 'drizzle-v1'; + private readonly connection; + private readonly db; + + constructor(filename: string) { + const connection = new Database(filename); + try { + connection.pragma('foreign_keys = ON'); + this.db = drizzle({ client: connection, relations: articleRelations }); + this.connection = connection; + } catch (error) { + connection.close(); + throw error; + } + } + + schemaMetadata() { + return ([[users, 'User'], [articles, 'Article']] as const).map(([table, entity]) => ({ + entity, + properties: Object.entries(getColumns(table)) + .map(([propertyName, column]) => ({ propertyName, columnName: column.name })), + })); + } + + async close(): Promise { + this.connection.close(); + } + + async findUserByEmail(email: string): Promise { + const row = this.db.select().from(users).where(eq(users.email, email)).get(); + return row ? this.#toUser(row) : null; + } + + async userExists(id: string): Promise { + return Boolean(this.db.select({ id: users.id }).from(users).where(eq(users.id, id)).get()); + } + + async articleExists(id: string): Promise { + return Boolean(this.db.select({ id: articles.id }).from(articles).where(eq(articles.id, id)).get()); + } + + async findUserById(id: string, filter: QueryFilter = { kind: 'all' }): Promise { + const row = this.db.select().from(users).where(and(eq(users.id, id), this.#filter('users', filter))).get(); + return row ? this.#toUser(row) : null; + } + + async createUser(value: Omit & { id?: string }): Promise { + const row = this.db.insert(users).values({ + ...value, id: value.id ?? crypto.randomUUID(), + createdAt: value.createdAt.toISOString(), updatedAt: value.updatedAt.toISOString(), + }).returning().get(); + return this.#toUser(row); + } + + async updateUser(id: string, patch: UserPatch, filter: QueryFilter): Promise { + const row = this.db.update(users).set({ ...patch, updatedAt: new Date().toISOString() }) + .where(and(eq(users.id, id), this.#filter('users', filter))).returning().get(); + return row ? this.#toUser(row) : null; + } + + async findArticle(id: string, filter: QueryFilter = { kind: 'all' }): Promise
{ + const row = this.#articleQuery().where(and(eq(articles.id, id), this.#filter('articles', filter))).get(); + return row ? this.#toArticle(row) : null; + } + + async listArticles(filter: QueryFilter, page: number, pageSize: number): Promise<{ items: Article[]; count: number }> { + const predicate = this.#filter('articles', filter); + const total = this.db.select({ count: count() }).from(articles).where(predicate).get()!; + const rows = this.#articleQuery().where(predicate).orderBy(desc(articles.createdAt), asc(articles.id)) + .limit(pageSize).offset((page - 1) * pageSize).all(); + return { items: rows.map(row => this.#toArticle(row)), count: total.count }; + } + + async createArticle(value: Omit & { id?: string }): Promise
{ + const { createdBy, ...attributes } = value; + const row = this.db.insert(articles).values({ + ...attributes, id: value.id ?? crypto.randomUUID(), + createdAt: value.createdAt.toISOString(), updatedAt: value.updatedAt.toISOString(), + }).returning().get(); + const created = await this.findArticle(row.id); + if (!created) throw new Error('Created article could not be loaded with its author'); + return created; + } + + async updateArticle(id: string, patch: ArticlePatch, filter: QueryFilter): Promise
{ + const current = await this.findArticle(id, filter); + if (!current) return null; + const updatedAt = new Date(); + const result = this.db.update(articles).set({ ...patch, updatedAt: updatedAt.toISOString() }) + .where(and(eq(articles.id, id), this.#filter('articles', filter))).run(); + return result.changes ? { ...current, ...patch, updatedAt } : null; + } + + async deleteArticle(id: string, filter: QueryFilter): Promise
{ + const current = await this.findArticle(id, filter); + if (!current) return null; + const result = this.db.delete(articles) + .where(and(eq(articles.id, id), this.#filter('articles', filter))).run(); + return result.changes ? current : null; + } + + #filter(table: 'users' | 'articles', filter: QueryFilter) { + if (filter.kind === 'none') return sql`0 = 1`; + return filter.kind === 'ast' ? interpret(filter.ast, table) : undefined; + } + + #articleQuery() { + return this.db.select({ ...getColumns(articles), createdBy: { id: users.id, email: users.email } }) + .from(articles).innerJoin(users, eq(articles.author, users.id)); + } + + #toUser(row: UserRow): User { + return { ...row, createdAt: new Date(row.createdAt), updatedAt: new Date(row.updatedAt) }; + } + + #toArticle(row: ArticleRow): Article { + return { ...row, createdAt: new Date(row.createdAt), updatedAt: new Date(row.updatedAt) }; + } +} diff --git a/packages/fastify-ucast-sql-blog/src/repositories/types.ts b/packages/fastify-ucast-sql-blog/src/repositories/types.ts index cebb7c0..946ae92 100644 --- a/packages/fastify-ucast-sql-blog/src/repositories/types.ts +++ b/packages/fastify-ucast-sql-blog/src/repositories/types.ts @@ -6,7 +6,7 @@ export type UserPatch = Partial>; export type QueryFilter = { kind: 'all' } | { kind: 'none' } | { kind: 'ast'; ast: any }; export interface BlogRepository { - readonly kind: 'mikro' | 'typeorm' | 'sequelize' | 'objection'; + readonly kind: 'mikro' | 'typeorm' | 'sequelize' | 'objection' | 'drizzle-v1'; schemaMetadata(): Array<{ entity: string; properties: Array<{ propertyName: string; columnName: string }> }>; close(): Promise; findUserByEmail(email: string): Promise; diff --git a/packages/fastify-ucast-sql-blog/src/schema.ts b/packages/fastify-ucast-sql-blog/src/schema.ts index 87f6457..f2e0224 100644 --- a/packages/fastify-ucast-sql-blog/src/schema.ts +++ b/packages/fastify-ucast-sql-blog/src/schema.ts @@ -4,7 +4,7 @@ import { dirname, resolve } from 'node:path'; import { ensureSqliteSchema } from './sqlite-schema.js'; const implementation = process.argv[2]; -if (!['mikro', 'typeorm', 'sequelize', 'objection'].includes(implementation)) throw new Error('Usage: tsx src/schema.ts mikro|typeorm|sequelize|objection'); +if (!['mikro', 'typeorm', 'sequelize', 'objection', 'drizzle-v1'].includes(implementation)) throw new Error('Usage: tsx src/schema.ts mikro|typeorm|sequelize|objection|drizzle-v1'); const configuredPath = process.env.DATABASE_URL ?? `./blog-${implementation}.sqlite`; const filename = resolve(configuredPath.startsWith('file:') ? configuredPath.slice(5) : configuredPath); mkdirSync(dirname(filename), { recursive: true }); diff --git a/packages/fastify-ucast-sql-blog/src/seed.ts b/packages/fastify-ucast-sql-blog/src/seed.ts index 0b6ab76..0c12588 100644 --- a/packages/fastify-ucast-sql-blog/src/seed.ts +++ b/packages/fastify-ucast-sql-blog/src/seed.ts @@ -4,7 +4,7 @@ import { initializeOrm } from './services/orm.js'; async function seed() { const implementation = process.argv[2]; -if (!['mikro', 'typeorm', 'sequelize', 'objection'].includes(implementation)) throw new Error('Usage: tsx src/seed.ts mikro|typeorm|sequelize|objection'); +if (!['mikro', 'typeorm', 'sequelize', 'objection', 'drizzle-v1'].includes(implementation)) throw new Error('Usage: tsx src/seed.ts mikro|typeorm|sequelize|objection|drizzle-v1'); const configuredPath = process.env.DATABASE_URL ?? `./blog-${implementation}.sqlite`; const filename = resolve(configuredPath.startsWith('file:') ? configuredPath.slice(5) : configuredPath); const repo = await initializeOrm(implementation, filename); diff --git a/packages/fastify-ucast-sql-blog/src/server.ts b/packages/fastify-ucast-sql-blog/src/server.ts index 95b1526..e057364 100644 --- a/packages/fastify-ucast-sql-blog/src/server.ts +++ b/packages/fastify-ucast-sql-blog/src/server.ts @@ -2,9 +2,10 @@ import 'reflect-metadata'; import { createApp } from './createApp.js'; import type { AppConfig } from './config.js'; +const implementation = process.env.IMPLEMENTATION ?? 'mikro'; const config: AppConfig = { - IMPLEMENTATION: process.env.IMPLEMENTATION ?? 'mikro', - DATABASE_URL: process.env.DATABASE_URL ?? './blog-mikro.sqlite', + IMPLEMENTATION: implementation, + DATABASE_URL: process.env.DATABASE_URL ?? `./blog-${implementation}.sqlite`, SERVER_PORT: Number(process.env.SERVER_PORT ?? 3000), AUTH_SECRET: process.env.AUTH_SECRET ?? 'development-only-secret', AUTH_ISSUER: process.env.AUTH_ISSUER ?? 'CASL.Fastify.UcastSql', diff --git a/packages/fastify-ucast-sql-blog/src/services/orm.ts b/packages/fastify-ucast-sql-blog/src/services/orm.ts index 320ae79..1c78a6b 100644 --- a/packages/fastify-ucast-sql-blog/src/services/orm.ts +++ b/packages/fastify-ucast-sql-blog/src/services/orm.ts @@ -2,6 +2,7 @@ import { MikroORM, SqliteDriver } from '@mikro-orm/sqlite'; import { DataSource } from 'typeorm'; import { Sequelize } from 'sequelize'; import knex from 'knex'; +import { DrizzleV1BlogRepository } from '../repositories/drizzle-v1.js'; import type { BlogRepository } from '../repositories/types.js'; import { MikroBlogRepository, MikroUsers, MikroArticles } from '../repositories/mikro.js'; import { TypeOrmBlogRepository, TypeOrmUsers, TypeOrmArticles } from '../repositories/typeorm.js'; @@ -67,7 +68,9 @@ export async function initializeOrm(implementation: string, databaseUrl: string) throw error; } } + case 'drizzle-v1': + return new DrizzleV1BlogRepository(filename); default: - throw new Error(`Unknown implementation "${implementation}". Use mikro, typeorm, sequelize or objection.`); + throw new Error(`Unknown implementation "${implementation}". Use mikro, typeorm, sequelize, objection or drizzle-v1.`); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f96d92..ae8962b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -191,10 +191,13 @@ importers: version: 2.0.0 '@ucast/sql': specifier: ~0.3.0 - version: 0.3.0(@mikro-orm/core@7.2.5)(objection@3.8.0(knex@3.3.0(better-sqlite3@12.11.1)(sqlite3@5.1.7)))(sequelize@6.37.8(sqlite3@5.1.7))(typeorm@1.1.2(better-sqlite3@12.11.1)(mongodb@7.2.0(socks@2.8.7))(ts-node@10.9.2(@types/node@24.0.15)(typescript@5.9.3))) + version: 0.3.0(@mikro-orm/core@7.2.5)(drizzle-orm@1.0.0-rc.5(@types/better-sqlite3@7.6.13)(better-sqlite3@12.11.1)(sqlite3@5.1.7)(zod@4.3.6))(objection@3.8.0(knex@3.3.0(better-sqlite3@12.11.1)(sqlite3@5.1.7)))(sequelize@6.37.8(sqlite3@5.1.7))(typeorm@1.1.2(better-sqlite3@12.11.1)(mongodb@7.2.0(socks@2.8.7))(ts-node@10.9.2(@types/node@24.0.15)(typescript@5.9.3))) better-sqlite3: specifier: ^12.11.1 version: 12.11.1 + drizzle-orm: + specifier: 1.0.0-rc.5 + version: 1.0.0-rc.5(@types/better-sqlite3@7.6.13)(better-sqlite3@12.11.1)(sqlite3@5.1.7)(zod@4.3.6) fastify: specifier: ^5.0.0 version: 5.12.5 @@ -3954,6 +3957,161 @@ packages: resolution: {integrity: sha512-7lAK2A0b3zZr3UC5aE69CPdCFR4RHW1o2Dr74TqFykxkUCBXSRJum/yPc7g8zRHJqWKomPLHwFLLoUnn8PXXRg==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + drizzle-orm@1.0.0-rc.5: + resolution: {integrity: sha512-7BMYTsAZbQ9DcQCtLG4G202Hq6nc/e8o1EBKN6nBweRI8EGPFe/VdyvWQCsU9fuZSiRjNpELolUSi//JFBq+lw==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@aws/aurora-dsql-node-postgres-connector': '>=0.1.9' + '@aws/aurora-dsql-postgresjs-connector': '>=0.3.0' + '@cloudflare/workers-types': '>=4' + '@drizzle-team/minipg': '>=0.4.2' + '@effect/sql-d1': '>=4.0.0' + '@effect/sql-libsql': '>=4.0.0' + '@effect/sql-mysql2': '>=4.0.0' + '@effect/sql-pg': '>=4.0.0' + '@effect/sql-pglite': '>=4.0.0' + '@effect/sql-sqlite-bun': '>=4.0.0' + '@effect/sql-sqlite-do': '>=4.0.0' + '@effect/sql-sqlite-node': '>=4.0.0' + '@effect/sql-sqlite-wasm': '>=4.0.0' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=17' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@sinclair/typebox': '>=0.34.8' + '@sqlitecloud/drivers': '>=1.0.653' + '@tidbcloud/serverless': '*' + '@tursodatabase/database': '>=0.7.1' + '@tursodatabase/database-common': '>=0.7.1' + '@tursodatabase/database-wasm': '>=0.7.1' + '@tursodatabase/serverless': '>=1.4.0' + '@tursodatabase/sync': '>=0.7.1' + '@types/better-sqlite3': '*' + '@types/mssql': ^9.1.4 + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + arktype: '>=2.0.0' + better-sqlite3: '>=9.3.0' + bun-types: '*' + effect: '>=4.0.0' + expo-sqlite: '>=14.0.0' + mssql: ^11.0.1 + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + sql.js: '>=1' + sqlite3: '>=5' + typebox: '>=1.2.0' + valibot: '>=1.0.0-beta.7' + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@aws/aurora-dsql-node-postgres-connector': + optional: true + '@aws/aurora-dsql-postgresjs-connector': + optional: true + '@cloudflare/workers-types': + optional: true + '@drizzle-team/minipg': + optional: true + '@effect/sql-d1': + optional: true + '@effect/sql-libsql': + optional: true + '@effect/sql-mysql2': + optional: true + '@effect/sql-pg': + optional: true + '@effect/sql-pglite': + optional: true + '@effect/sql-sqlite-bun': + optional: true + '@effect/sql-sqlite-do': + optional: true + '@effect/sql-sqlite-node': + optional: true + '@effect/sql-sqlite-wasm': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@sinclair/typebox': + optional: true + '@sqlitecloud/drivers': + optional: true + '@tidbcloud/serverless': + optional: true + '@tursodatabase/database': + optional: true + '@tursodatabase/database-common': + optional: true + '@tursodatabase/database-wasm': + optional: true + '@tursodatabase/serverless': + optional: true + '@tursodatabase/sync': + optional: true + '@types/better-sqlite3': + optional: true + '@types/mssql': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + arktype: + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + effect: + optional: true + expo-sqlite: + optional: true + mssql: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + typebox: + optional: true + valibot: + optional: true + zod: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -9602,11 +9760,12 @@ snapshots: dependencies: '@ucast/core': 2.0.0 - '@ucast/sql@0.3.0(@mikro-orm/core@7.2.5)(objection@3.8.0(knex@3.3.0(better-sqlite3@12.11.1)(sqlite3@5.1.7)))(sequelize@6.37.8(sqlite3@5.1.7))(typeorm@1.1.2(better-sqlite3@12.11.1)(mongodb@7.2.0(socks@2.8.7))(ts-node@10.9.2(@types/node@24.0.15)(typescript@5.9.3)))': + '@ucast/sql@0.3.0(@mikro-orm/core@7.2.5)(drizzle-orm@1.0.0-rc.5(@types/better-sqlite3@7.6.13)(better-sqlite3@12.11.1)(sqlite3@5.1.7)(zod@4.3.6))(objection@3.8.0(knex@3.3.0(better-sqlite3@12.11.1)(sqlite3@5.1.7)))(sequelize@6.37.8(sqlite3@5.1.7))(typeorm@1.1.2(better-sqlite3@12.11.1)(mongodb@7.2.0(socks@2.8.7))(ts-node@10.9.2(@types/node@24.0.15)(typescript@5.9.3)))': dependencies: '@ucast/core': 2.0.0 optionalDependencies: '@mikro-orm/core': 7.2.5 + drizzle-orm: 1.0.0-rc.5(@types/better-sqlite3@7.6.13)(better-sqlite3@12.11.1)(sqlite3@5.1.7)(zod@4.3.6) objection: 3.8.0(knex@3.3.0(better-sqlite3@12.11.1)(sqlite3@5.1.7)) sequelize: 6.37.8(sqlite3@5.1.7) typeorm: 1.1.2(better-sqlite3@12.11.1)(mongodb@7.2.0(socks@2.8.7))(ts-node@10.9.2(@types/node@24.0.15)(typescript@5.9.3)) @@ -10590,6 +10749,13 @@ snapshots: dottie@2.0.7: {} + drizzle-orm@1.0.0-rc.5(@types/better-sqlite3@7.6.13)(better-sqlite3@12.11.1)(sqlite3@5.1.7)(zod@4.3.6): + optionalDependencies: + '@types/better-sqlite3': 7.6.13 + better-sqlite3: 12.11.1 + sqlite3: 5.1.7 + zod: 4.3.6 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2