@fragno-dev/db 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +202 -140
- package/CHANGELOG.md +35 -0
- package/README.md +30 -9
- package/dist/adapters/adapters.d.ts +23 -21
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/generic-sql/driver-config.d.ts +16 -1
- package/dist/adapters/generic-sql/driver-config.d.ts.map +1 -1
- package/dist/adapters/generic-sql/driver-config.js +23 -1
- package/dist/adapters/generic-sql/driver-config.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts +27 -9
- package/dist/adapters/generic-sql/generic-sql-adapter.d.ts.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-adapter.js +55 -16
- package/dist/adapters/generic-sql/generic-sql-adapter.js.map +1 -1
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js +129 -3
- package/dist/adapters/generic-sql/generic-sql-uow-executor.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/mysql.js +24 -5
- package/dist/adapters/generic-sql/migration/dialect/mysql.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/postgres.js +6 -5
- package/dist/adapters/generic-sql/migration/dialect/postgres.js.map +1 -1
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js +21 -10
- package/dist/adapters/generic-sql/migration/dialect/sqlite.js.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.d.ts.map +1 -1
- package/dist/adapters/generic-sql/migration/prepared-migrations.js +8 -8
- package/dist/adapters/generic-sql/migration/prepared-migrations.js.map +1 -1
- package/dist/adapters/generic-sql/migration/sql-generator.js +74 -51
- package/dist/adapters/generic-sql/migration/sql-generator.js.map +1 -1
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js +6 -5
- package/dist/adapters/generic-sql/query/create-sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/cursor-utils.js +42 -4
- package/dist/adapters/generic-sql/query/cursor-utils.js.map +1 -1
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js +25 -17
- package/dist/adapters/generic-sql/query/generic-sql-uow-operation-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/select-builder.js +5 -3
- package/dist/adapters/generic-sql/query/select-builder.js.map +1 -1
- package/dist/adapters/generic-sql/query/sql-query-compiler.js +15 -12
- package/dist/adapters/generic-sql/query/sql-query-compiler.js.map +1 -1
- package/dist/adapters/generic-sql/query/where-builder.js +39 -29
- package/dist/adapters/generic-sql/query/where-builder.js.map +1 -1
- package/dist/adapters/generic-sql/sqlite-storage.d.ts +13 -0
- package/dist/adapters/generic-sql/sqlite-storage.d.ts.map +1 -0
- package/dist/adapters/generic-sql/sqlite-storage.js +15 -0
- package/dist/adapters/generic-sql/sqlite-storage.js.map +1 -0
- package/dist/adapters/generic-sql/uow-decoder.js +7 -3
- package/dist/adapters/generic-sql/uow-decoder.js.map +1 -1
- package/dist/adapters/generic-sql/uow-encoder.js +28 -8
- package/dist/adapters/generic-sql/uow-encoder.js.map +1 -1
- package/dist/adapters/in-memory/condition-evaluator.js +131 -0
- package/dist/adapters/in-memory/condition-evaluator.js.map +1 -0
- package/dist/adapters/in-memory/errors.d.ts +13 -0
- package/dist/adapters/in-memory/errors.d.ts.map +1 -0
- package/dist/adapters/in-memory/errors.js +23 -0
- package/dist/adapters/in-memory/errors.js.map +1 -0
- package/dist/adapters/in-memory/in-memory-adapter.d.ts +27 -0
- package/dist/adapters/in-memory/in-memory-adapter.d.ts.map +1 -0
- package/dist/adapters/in-memory/in-memory-adapter.js +176 -0
- package/dist/adapters/in-memory/in-memory-adapter.js.map +1 -0
- package/dist/adapters/in-memory/in-memory-uow.js +648 -0
- package/dist/adapters/in-memory/in-memory-uow.js.map +1 -0
- package/dist/adapters/in-memory/index.d.ts +4 -0
- package/dist/adapters/in-memory/index.js +4 -0
- package/dist/adapters/in-memory/options.d.ts +28 -0
- package/dist/adapters/in-memory/options.d.ts.map +1 -0
- package/dist/adapters/in-memory/options.js +61 -0
- package/dist/adapters/in-memory/options.js.map +1 -0
- package/dist/adapters/in-memory/reference-resolution.js +26 -0
- package/dist/adapters/in-memory/reference-resolution.js.map +1 -0
- package/dist/adapters/in-memory/sorted-array-index.js +129 -0
- package/dist/adapters/in-memory/sorted-array-index.js.map +1 -0
- package/dist/adapters/in-memory/store.js +71 -0
- package/dist/adapters/in-memory/store.js.map +1 -0
- package/dist/adapters/in-memory/value-comparison.js +28 -0
- package/dist/adapters/in-memory/value-comparison.js.map +1 -0
- package/dist/adapters/shared/from-unit-of-work-compiler.js.map +1 -1
- package/dist/adapters/shared/uow-operation-compiler.js +11 -11
- package/dist/adapters/shared/uow-operation-compiler.js.map +1 -1
- package/dist/adapters/sql/index.d.ts +5 -0
- package/dist/adapters/sql/index.js +4 -0
- package/dist/db-fragment-definition-builder.d.ts +18 -7
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +116 -54
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/dispatchers/cloudflare-do/index.d.ts +26 -0
- package/dist/dispatchers/cloudflare-do/index.d.ts.map +1 -0
- package/dist/dispatchers/cloudflare-do/index.js +63 -0
- package/dist/dispatchers/cloudflare-do/index.js.map +1 -0
- package/dist/dispatchers/node/index.d.ts +17 -0
- package/dist/dispatchers/node/index.d.ts.map +1 -0
- package/dist/dispatchers/node/index.js +59 -0
- package/dist/dispatchers/node/index.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +79 -2
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +150 -32
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/fragments/internal-fragment.routes.js +29 -0
- package/dist/fragments/internal-fragment.routes.js.map +1 -0
- package/dist/fragments/internal-fragment.schema.d.ts +9 -0
- package/dist/fragments/internal-fragment.schema.d.ts.map +1 -0
- package/dist/fragments/internal-fragment.schema.js +22 -0
- package/dist/fragments/internal-fragment.schema.js.map +1 -0
- package/dist/hooks/durable-hooks-processor.d.ts +14 -0
- package/dist/hooks/durable-hooks-processor.d.ts.map +1 -0
- package/dist/hooks/durable-hooks-processor.js +32 -0
- package/dist/hooks/durable-hooks-processor.js.map +1 -0
- package/dist/hooks/hooks.d.ts +42 -1
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +72 -6
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/migration-engine/auto-from-schema.js +14 -11
- package/dist/migration-engine/auto-from-schema.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts +16 -10
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +72 -33
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +15 -8
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +14 -8
- package/dist/mod.js.map +1 -1
- package/dist/naming/sql-naming.d.ts +19 -0
- package/dist/naming/sql-naming.d.ts.map +1 -0
- package/dist/naming/sql-naming.js +116 -0
- package/dist/naming/sql-naming.js.map +1 -0
- package/dist/node_modules/.pnpm/{rou3@0.7.10 → rou3@0.7.12}/node_modules/rou3/dist/index.js +8 -5
- package/dist/node_modules/.pnpm/rou3@0.7.12/node_modules/rou3/dist/index.js.map +1 -0
- package/dist/outbox/outbox-builder.js +156 -0
- package/dist/outbox/outbox-builder.js.map +1 -0
- package/dist/outbox/outbox.d.ts +52 -0
- package/dist/outbox/outbox.d.ts.map +1 -0
- package/dist/outbox/outbox.js +37 -0
- package/dist/outbox/outbox.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +3 -2
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -1
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +164 -20
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -1
- package/dist/packages/fragno/dist/api/request-input-context.js +67 -0
- package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -1
- package/dist/packages/fragno/dist/api/route.js +14 -1
- package/dist/packages/fragno/dist/api/route.js.map +1 -1
- package/dist/packages/fragno/dist/internal/trace-context.js +12 -0
- package/dist/packages/fragno/dist/internal/trace-context.js.map +1 -0
- package/dist/query/column-defaults.js +20 -4
- package/dist/query/column-defaults.js.map +1 -1
- package/dist/query/cursor.d.ts +3 -1
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +45 -14
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/db-now.d.ts +8 -0
- package/dist/query/db-now.d.ts.map +1 -0
- package/dist/query/db-now.js +7 -0
- package/dist/query/db-now.js.map +1 -0
- package/dist/query/serialize/create-sql-serializer.js +3 -2
- package/dist/query/serialize/create-sql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/mysql-serializer.js +12 -6
- package/dist/query/serialize/dialect/mysql-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/postgres-serializer.js +25 -7
- package/dist/query/serialize/dialect/postgres-serializer.js.map +1 -1
- package/dist/query/serialize/dialect/sqlite-serializer.js +55 -11
- package/dist/query/serialize/dialect/sqlite-serializer.js.map +1 -1
- package/dist/query/serialize/sql-serializer.js +2 -2
- package/dist/query/serialize/sql-serializer.js.map +1 -1
- package/dist/query/simple-query-interface.d.ts +6 -1
- package/dist/query/simple-query-interface.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.js +11 -6
- package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.d.ts +50 -14
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +86 -5
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +9 -6
- package/dist/query/value-decoding.js.map +1 -1
- package/dist/query/value-encoding.js +29 -9
- package/dist/query/value-encoding.js.map +1 -1
- package/dist/schema/create.d.ts +38 -14
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +81 -42
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/generate-id.js +2 -2
- package/dist/schema/generate-id.js.map +1 -1
- package/dist/schema/type-conversion/create-sql-type-mapper.js +3 -2
- package/dist/schema/type-conversion/create-sql-type-mapper.js.map +1 -1
- package/dist/schema/type-conversion/dialect/sqlite.js +9 -0
- package/dist/schema/type-conversion/dialect/sqlite.js.map +1 -1
- package/dist/schema/validator.d.ts +10 -0
- package/dist/schema/validator.d.ts.map +1 -0
- package/dist/schema/validator.js +123 -0
- package/dist/schema/validator.js.map +1 -0
- package/dist/schema-output/drizzle.d.ts +30 -0
- package/dist/schema-output/drizzle.d.ts.map +1 -0
- package/dist/{adapters/drizzle/generate.js → schema-output/drizzle.js} +82 -56
- package/dist/schema-output/drizzle.js.map +1 -0
- package/dist/schema-output/prisma.d.ts +17 -0
- package/dist/schema-output/prisma.d.ts.map +1 -0
- package/dist/schema-output/prisma.js +296 -0
- package/dist/schema-output/prisma.js.map +1 -0
- package/dist/util/default-database-adapter.js +61 -0
- package/dist/util/default-database-adapter.js.map +1 -0
- package/dist/with-database.d.ts +1 -1
- package/dist/with-database.d.ts.map +1 -1
- package/dist/with-database.js +12 -3
- package/dist/with-database.js.map +1 -1
- package/package.json +43 -28
- package/src/adapters/adapters.ts +30 -24
- package/src/adapters/drizzle/migrate-drizzle.test.ts +54 -33
- package/src/adapters/drizzle/migration-parity-drizzle-kit.test.ts +599 -0
- package/src/adapters/drizzle/test-utils.ts +12 -8
- package/src/adapters/generic-sql/driver-config.ts +38 -0
- package/src/adapters/generic-sql/generic-sql-adapter.test.ts +5 -5
- package/src/adapters/generic-sql/generic-sql-adapter.ts +110 -24
- package/src/adapters/generic-sql/generic-sql-uow-executor.test.ts +54 -0
- package/src/adapters/generic-sql/generic-sql-uow-executor.ts +231 -3
- package/src/adapters/generic-sql/migration/adapter-migration-parity.test.ts +118 -0
- package/src/adapters/generic-sql/migration/dialect/mysql.test.ts +26 -8
- package/src/adapters/generic-sql/migration/dialect/mysql.ts +46 -8
- package/src/adapters/generic-sql/migration/dialect/postgres.test.ts +25 -7
- package/src/adapters/generic-sql/migration/dialect/postgres.ts +8 -4
- package/src/adapters/generic-sql/migration/dialect/sqlite.test.ts +47 -8
- package/src/adapters/generic-sql/migration/dialect/sqlite.ts +27 -12
- package/src/adapters/generic-sql/migration/prepared-migrations.test.ts +128 -39
- package/src/adapters/generic-sql/migration/prepared-migrations.ts +15 -8
- package/src/adapters/generic-sql/migration/sql-generator.ts +142 -65
- package/src/adapters/generic-sql/query/create-sql-query-compiler.ts +9 -6
- package/src/adapters/generic-sql/query/cursor-utils.test.ts +271 -0
- package/src/adapters/generic-sql/query/cursor-utils.ts +41 -6
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.test.ts +27 -27
- package/src/adapters/generic-sql/query/generic-sql-uow-operation-compiler.ts +38 -24
- package/src/adapters/generic-sql/query/select-builder.test.ts +15 -11
- package/src/adapters/generic-sql/query/select-builder.ts +6 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.test.ts +52 -2
- package/src/adapters/generic-sql/query/sql-query-compiler.ts +50 -15
- package/src/adapters/generic-sql/query/where-builder.test.ts +91 -17
- package/src/adapters/generic-sql/query/where-builder.ts +90 -38
- package/src/adapters/{kysely/kysely-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-migrations.test.ts} +6 -6
- package/src/adapters/generic-sql/sql-adapter-pglite-pagination.test.ts +806 -0
- package/src/adapters/{drizzle/drizzle-adapter-pglite.test.ts → generic-sql/sql-adapter-pglite-queries.test.ts} +11 -11
- package/src/adapters/generic-sql/{test/generic-drizzle-adapter-sqlite3.test.ts → sql-adapter-sqlite3-driver.test.ts} +10 -10
- package/src/adapters/{drizzle/drizzle-adapter-sqlite3.test.ts → generic-sql/sql-adapter-sqlite3-uow.test.ts} +7 -7
- package/src/adapters/{kysely/kysely-adapter-sqlocal.test.ts → generic-sql/sql-adapter-sqlocal.test.ts} +6 -6
- package/src/adapters/generic-sql/sqlite-storage.ts +20 -0
- package/src/adapters/generic-sql/uow-decoder.test.ts +1 -1
- package/src/adapters/generic-sql/uow-decoder.ts +21 -3
- package/src/adapters/generic-sql/uow-encoder.test.ts +33 -2
- package/src/adapters/generic-sql/uow-encoder.ts +50 -11
- package/src/adapters/in-memory/condition-evaluator.test.ts +193 -0
- package/src/adapters/in-memory/condition-evaluator.ts +275 -0
- package/src/adapters/in-memory/errors.ts +20 -0
- package/src/adapters/in-memory/in-memory-adapter.ts +277 -0
- package/src/adapters/in-memory/in-memory-uow.mutations.test.ts +296 -0
- package/src/adapters/in-memory/in-memory-uow.retrieval.test.ts +100 -0
- package/src/adapters/in-memory/in-memory-uow.ts +1348 -0
- package/src/adapters/in-memory/index.ts +3 -0
- package/src/adapters/in-memory/options.test.ts +41 -0
- package/src/adapters/in-memory/options.ts +87 -0
- package/src/adapters/in-memory/reference-resolution.test.ts +50 -0
- package/src/adapters/in-memory/reference-resolution.ts +67 -0
- package/src/adapters/in-memory/sorted-array-index.test.ts +123 -0
- package/src/adapters/in-memory/sorted-array-index.ts +228 -0
- package/src/adapters/in-memory/store.test.ts +68 -0
- package/src/adapters/in-memory/store.ts +145 -0
- package/src/adapters/in-memory/value-comparison.ts +53 -0
- package/src/adapters/in-memory/value-normalization.test.ts +57 -0
- package/src/adapters/prisma/prisma-adapter-sqlite3.test.ts +1163 -0
- package/src/adapters/shared/from-unit-of-work-compiler.ts +3 -1
- package/src/adapters/shared/uow-operation-compiler.ts +26 -16
- package/src/adapters/sql/index.ts +12 -0
- package/src/db-fragment-definition-builder.test.ts +30 -12
- package/src/db-fragment-definition-builder.ts +142 -73
- package/src/db-fragment-instantiator.test.ts +105 -13
- package/src/db-fragment-integration.test.ts +9 -7
- package/src/dispatchers/cloudflare-do/index.test.ts +73 -0
- package/src/dispatchers/cloudflare-do/index.ts +104 -0
- package/src/dispatchers/node/index.test.ts +91 -0
- package/src/dispatchers/node/index.ts +87 -0
- package/src/fragments/internal-fragment.routes.ts +42 -0
- package/src/fragments/internal-fragment.schema.ts +51 -0
- package/src/fragments/internal-fragment.test.ts +458 -8
- package/src/fragments/internal-fragment.ts +322 -63
- package/src/hooks/durable-hooks-processor.test.ts +117 -0
- package/src/hooks/durable-hooks-processor.ts +67 -0
- package/src/hooks/hooks.test.ts +165 -5
- package/src/hooks/hooks.ts +197 -9
- package/src/migration-engine/auto-from-schema.test.ts +14 -14
- package/src/migration-engine/auto-from-schema.ts +5 -2
- package/src/migration-engine/create.test.ts +2 -2
- package/src/migration-engine/generation-engine.test.ts +229 -104
- package/src/migration-engine/generation-engine.ts +94 -64
- package/src/migration-engine/shared.ts +1 -0
- package/src/mod.ts +64 -26
- package/src/naming/sql-naming.ts +180 -0
- package/src/outbox/outbox-builder.ts +241 -0
- package/src/outbox/outbox.test.ts +253 -0
- package/src/outbox/outbox.ts +137 -0
- package/src/query/column-defaults.ts +41 -3
- package/src/query/condition-builder.test.ts +3 -3
- package/src/query/cursor.test.ts +116 -18
- package/src/query/cursor.ts +75 -26
- package/src/query/db-now.ts +6 -0
- package/src/query/query-type.test.ts +2 -2
- package/src/query/serialize/create-sql-serializer.ts +7 -2
- package/src/query/serialize/dialect/mysql-serializer.ts +12 -4
- package/src/query/serialize/dialect/postgres-serializer.ts +34 -4
- package/src/query/serialize/dialect/sqlite-serializer.test.ts +51 -1
- package/src/query/serialize/dialect/sqlite-serializer.ts +92 -9
- package/src/query/serialize/sql-serializer.ts +4 -4
- package/src/query/simple-query-interface.ts +5 -0
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +25 -1
- package/src/query/unit-of-work/execute-unit-of-work.ts +25 -8
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +12 -12
- package/src/query/unit-of-work/unit-of-work-types.test.ts +1 -1
- package/src/query/unit-of-work/unit-of-work.test.ts +168 -37
- package/src/query/unit-of-work/unit-of-work.ts +203 -18
- package/src/query/value-decoding.test.ts +13 -2
- package/src/query/value-decoding.ts +17 -4
- package/src/query/value-encoding.test.ts +85 -2
- package/src/query/value-encoding.ts +56 -6
- package/src/schema/create.test.ts +129 -42
- package/src/schema/create.ts +185 -47
- package/src/schema/generate-id.test.ts +2 -2
- package/src/schema/generate-id.ts +2 -2
- package/src/schema/serialize.test.ts +14 -2
- package/src/schema/type-conversion/create-sql-type-mapper.ts +7 -2
- package/src/schema/type-conversion/dialect/sqlite.ts +18 -0
- package/src/schema/type-conversion/type-mapping.test.ts +25 -1
- package/src/schema/validator.test.ts +197 -0
- package/src/schema/validator.ts +231 -0
- package/src/{adapters/drizzle/generate.test.ts → schema-output/drizzle.test.ts} +179 -129
- package/src/{adapters/drizzle/generate.ts → schema-output/drizzle.ts} +143 -93
- package/src/schema-output/prisma.test.ts +536 -0
- package/src/schema-output/prisma.ts +573 -0
- package/src/util/default-database-adapter.ts +106 -0
- package/src/with-database.ts +22 -3
- package/tsdown.config.ts +6 -4
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +0 -20
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +0 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +0 -27
- package/dist/adapters/drizzle/drizzle-adapter.js.map +0 -1
- package/dist/adapters/drizzle/generate.d.ts +0 -30
- package/dist/adapters/drizzle/generate.d.ts.map +0 -1
- package/dist/adapters/drizzle/generate.js.map +0 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts +0 -19
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +0 -1
- package/dist/adapters/kysely/kysely-adapter.js +0 -17
- package/dist/adapters/kysely/kysely-adapter.js.map +0 -1
- package/dist/adapters/shared/table-name-mapper.d.ts +0 -12
- package/dist/adapters/shared/table-name-mapper.d.ts.map +0 -1
- package/dist/adapters/shared/table-name-mapper.js +0 -43
- package/dist/adapters/shared/table-name-mapper.js.map +0 -1
- package/dist/node_modules/.pnpm/rou3@0.7.10/node_modules/rou3/dist/index.js.map +0 -1
- package/dist/schema-generator/schema-generator.d.ts +0 -15
- package/dist/schema-generator/schema-generator.d.ts.map +0 -1
- package/src/adapters/drizzle/drizzle-adapter.ts +0 -39
- package/src/adapters/kysely/kysely-adapter.ts +0 -27
- package/src/adapters/shared/table-name-mapper.ts +0 -50
- package/src/schema-generator/schema-generator.ts +0 -12
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { column, idColumn, referenceColumn, schema } from "../../schema/create";
|
|
4
|
+
import { writeAndLoadSchema } from "./test-utils";
|
|
5
|
+
import { createPreparedMigrations } from "../generic-sql/migration/prepared-migrations";
|
|
6
|
+
import { createNamingResolver } from "../../naming/sql-naming";
|
|
7
|
+
import { defaultNamingStrategyForDatabase } from "../generic-sql/driver-config";
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const {
|
|
11
|
+
generateDrizzleJson,
|
|
12
|
+
generateMigration,
|
|
13
|
+
generateSQLiteDrizzleJson,
|
|
14
|
+
generateSQLiteMigration,
|
|
15
|
+
generateMySQLDrizzleJson,
|
|
16
|
+
generateMySQLMigration,
|
|
17
|
+
} = require("drizzle-kit/api") as typeof import("drizzle-kit/api");
|
|
18
|
+
|
|
19
|
+
const paritySchema = schema("parity", (s) => {
|
|
20
|
+
return s
|
|
21
|
+
.addTable("users", (t) => {
|
|
22
|
+
return t
|
|
23
|
+
.addColumn("id", idColumn())
|
|
24
|
+
.addColumn("email", column("varchar(255)"))
|
|
25
|
+
.addColumn("name", column("string"))
|
|
26
|
+
.addColumn("status", column("varchar(32)").defaultTo("active"))
|
|
27
|
+
.addColumn("isActive", column("bool").defaultTo(true))
|
|
28
|
+
.addColumn("loginCount", column("integer").defaultTo(0))
|
|
29
|
+
.addColumn("bigCounter", column("bigint"))
|
|
30
|
+
.addColumn("score", column("decimal").defaultTo(0))
|
|
31
|
+
.addColumn("profile", column("json").nullable())
|
|
32
|
+
.addColumn("avatar", column("binary").nullable())
|
|
33
|
+
.addColumn("birthday", column("date").nullable())
|
|
34
|
+
.addColumn(
|
|
35
|
+
"createdAt",
|
|
36
|
+
column("timestamp").defaultTo((b) => b.now()),
|
|
37
|
+
)
|
|
38
|
+
.addColumn(
|
|
39
|
+
"updatedAt",
|
|
40
|
+
column("timestamp").defaultTo$((b) => b.now()),
|
|
41
|
+
)
|
|
42
|
+
.createIndex("users_email_idx", ["email"], { unique: true })
|
|
43
|
+
.createIndex("users_status_idx", ["status"]);
|
|
44
|
+
})
|
|
45
|
+
.addTable("posts", (t) => {
|
|
46
|
+
return t
|
|
47
|
+
.addColumn("id", idColumn())
|
|
48
|
+
.addColumn("title", column("string"))
|
|
49
|
+
.addColumn("slug", column("varchar(64)"))
|
|
50
|
+
.addColumn("body", column("string"))
|
|
51
|
+
.addColumn("published", column("bool").defaultTo(false))
|
|
52
|
+
.addColumn("publishedAt", column("timestamp").nullable())
|
|
53
|
+
.addColumn("authorId", referenceColumn())
|
|
54
|
+
.addColumn("metadata", column("json").nullable())
|
|
55
|
+
.addColumn("rating", column("decimal").nullable())
|
|
56
|
+
.addColumn("contentHash", column("binary").nullable())
|
|
57
|
+
.createIndex("posts_slug_idx", ["slug"], { unique: true })
|
|
58
|
+
.createIndex("posts_author_idx", ["authorId"])
|
|
59
|
+
.createIndex("posts_author_title_idx", ["authorId", "title"], { unique: true });
|
|
60
|
+
})
|
|
61
|
+
.addTable("tags", (t) => {
|
|
62
|
+
return t
|
|
63
|
+
.addColumn("id", idColumn())
|
|
64
|
+
.addColumn("label", column("string"))
|
|
65
|
+
.addColumn("slug", column("varchar(32)"))
|
|
66
|
+
.createIndex("tags_slug_idx", ["slug"], { unique: true });
|
|
67
|
+
})
|
|
68
|
+
.addTable("postTags", (t) => {
|
|
69
|
+
return t
|
|
70
|
+
.addColumn("id", idColumn())
|
|
71
|
+
.addColumn("postId", referenceColumn())
|
|
72
|
+
.addColumn("tagId", referenceColumn())
|
|
73
|
+
.addColumn(
|
|
74
|
+
"addedAt",
|
|
75
|
+
column("timestamp").defaultTo((b) => b.now()),
|
|
76
|
+
)
|
|
77
|
+
.createIndex("post_tags_post_idx", ["postId"])
|
|
78
|
+
.createIndex("post_tags_tag_idx", ["tagId"])
|
|
79
|
+
.createIndex("post_tags_unique_idx", ["postId", "tagId"], { unique: true });
|
|
80
|
+
})
|
|
81
|
+
.addTable("categories", (t) => {
|
|
82
|
+
return t
|
|
83
|
+
.addColumn("id", idColumn())
|
|
84
|
+
.addColumn("name", column("string"))
|
|
85
|
+
.addColumn("parentId", referenceColumn().nullable())
|
|
86
|
+
.createIndex("categories_parent_idx", ["parentId"]);
|
|
87
|
+
})
|
|
88
|
+
.addReference("posts_author", {
|
|
89
|
+
type: "one",
|
|
90
|
+
from: { table: "posts", column: "authorId" },
|
|
91
|
+
to: { table: "users", column: "id" },
|
|
92
|
+
})
|
|
93
|
+
.addReference("post_tags_post", {
|
|
94
|
+
type: "one",
|
|
95
|
+
from: { table: "postTags", column: "postId" },
|
|
96
|
+
to: { table: "posts", column: "id" },
|
|
97
|
+
})
|
|
98
|
+
.addReference("post_tags_tag", {
|
|
99
|
+
type: "one",
|
|
100
|
+
from: { table: "postTags", column: "tagId" },
|
|
101
|
+
to: { table: "tags", column: "id" },
|
|
102
|
+
})
|
|
103
|
+
.addReference("categories_parent", {
|
|
104
|
+
type: "one",
|
|
105
|
+
from: { table: "categories", column: "parentId" },
|
|
106
|
+
to: { table: "categories", column: "id" },
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
type Dialect = "postgresql" | "mysql" | "sqlite";
|
|
111
|
+
|
|
112
|
+
type NormalizationRule = {
|
|
113
|
+
reason: string;
|
|
114
|
+
apply: (sql: string, dialect: Dialect) => string;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
type StatementNormalizationContext = {
|
|
118
|
+
dialect: Dialect;
|
|
119
|
+
uniqueIdTables: Set<string>;
|
|
120
|
+
foreignKeys: Set<string>;
|
|
121
|
+
extraStatements: string[];
|
|
122
|
+
nameMarkers: string[];
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
type StatementNormalizationRule = {
|
|
126
|
+
reason: string;
|
|
127
|
+
apply: (sql: string, ctx: StatementNormalizationContext) => string | null;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const NORMALIZATION_RULES: NormalizationRule[] = [
|
|
131
|
+
{
|
|
132
|
+
reason: "Whitespace/semicolons don't change SQL semantics; normalize for stable comparisons.",
|
|
133
|
+
apply: (sql) => sql.replace(/;\s*$/g, "").trim().replace(/\s+/g, " "),
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
reason: "Identifier quoting style differs by dialect/formatter but is semantically equivalent.",
|
|
137
|
+
apply: (sql) => sql.replace(/["`]/g, ""),
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
reason: "Extra spaces around parentheses are formatting-only.",
|
|
141
|
+
apply: (sql) => sql.replace(/\(\s+/g, "(").replace(/\s+\)/g, ")"),
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
reason: "Case is not semantically meaningful for SQL keywords/identifiers here.",
|
|
145
|
+
apply: (sql) => sql.toLowerCase(),
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
reason: "now() and CURRENT_TIMESTAMP are equivalent default expressions in these dialects.",
|
|
149
|
+
apply: (sql) => sql.replace(/\bnow\(\)/g, "current_timestamp"),
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
reason: "DEFAULT (current_timestamp) is equivalent to DEFAULT current_timestamp.",
|
|
153
|
+
apply: (sql) => sql.replace(/\bdefault\s+\(current_timestamp\)/g, "default current_timestamp"),
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
reason: "Column constraint order doesn't change meaning; standardize ordering.",
|
|
157
|
+
apply: (sql) => sql.replace(/\bprimary key not null\b/g, "not null primary key"),
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
reason: "SQLite autoincrement PK constraint order doesn't change meaning.",
|
|
161
|
+
apply: (sql) =>
|
|
162
|
+
sql.replace(/\bprimary key autoincrement not null\b/g, "not null primary key autoincrement"),
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
reason: "Default value and NOT NULL order is equivalent in DDL.",
|
|
166
|
+
apply: (sql) => sql.replace(/\bdefault\s+([^\s,]+)\s+not null\b/g, "not null default $1"),
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
reason: "MySQL allows reordering AUTO_INCREMENT/NOT NULL/PRIMARY KEY without meaning change.",
|
|
170
|
+
apply: (sql) =>
|
|
171
|
+
sql.replace(
|
|
172
|
+
/\bauto_increment not null primary key\b/g,
|
|
173
|
+
"not null primary key auto_increment",
|
|
174
|
+
),
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
// In PostgreSQL, NO ACTION and RESTRICT behave the same for immediate constraints,
|
|
178
|
+
// but they can differ with deferrable constraints: NO ACTION can be deferred, RESTRICT is always enforced
|
|
179
|
+
// immediately. So treating them as equivalent is only safe if we’re not using deferrable constraints (which we
|
|
180
|
+
// aren’t in these migrations).
|
|
181
|
+
reason:
|
|
182
|
+
"Postgres FK action defaults to NO ACTION; Fragno uses RESTRICT. Treat as equivalent for DDL parity.",
|
|
183
|
+
apply: (sql) =>
|
|
184
|
+
sql
|
|
185
|
+
.replace(/\bon delete no action\b/g, "on delete restrict")
|
|
186
|
+
.replace(/\bon update no action\b/g, "on update restrict"),
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
reason: "Postgres implicit public schema qualifiers are redundant in migrations.",
|
|
190
|
+
apply: (sql, dialect) => (dialect === "postgresql" ? sql.replace(/\bpublic\./g, "") : sql),
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
reason: "Postgres index method defaults to btree; explicit USING btree is redundant.",
|
|
194
|
+
apply: (sql, dialect) =>
|
|
195
|
+
dialect === "postgresql" ? sql.replace(/\s+using\s+btree\b/g, "") : sql,
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
reason: "MySQL 'integer' and 'int' are synonyms; normalize for comparisons.",
|
|
199
|
+
apply: (sql, dialect) => (dialect === "mysql" ? sql.replace(/\binteger\b/g, "int") : sql),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
reason: "Postgres 'numeric' and 'decimal' are synonyms; normalize for comparisons.",
|
|
203
|
+
apply: (sql, dialect) =>
|
|
204
|
+
dialect === "postgresql" ? sql.replace(/\bnumeric\b/g, "decimal") : sql,
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
const STATEMENT_NORMALIZATION_RULES: StatementNormalizationRule[] = [
|
|
209
|
+
{
|
|
210
|
+
reason:
|
|
211
|
+
"Drizzle migrations do not emit CREATE SCHEMA; ignore schema creation for parity checks.",
|
|
212
|
+
apply: (sql, ctx) => {
|
|
213
|
+
if (ctx.dialect === "postgresql" && sql.startsWith("create schema ")) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return sql;
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
reason:
|
|
221
|
+
"External-id uniqueness can appear as column UNIQUE or a table-level UNIQUE(id); normalize to markers.",
|
|
222
|
+
apply: (sql, ctx) => {
|
|
223
|
+
if (!sql.startsWith("create table ")) {
|
|
224
|
+
return sql;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const tableMatch = sql.match(/^create table\s+([\w.]+)\s*\(/);
|
|
228
|
+
const tableName = tableMatch?.[1];
|
|
229
|
+
if (!tableName) {
|
|
230
|
+
return sql;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const idUniqueRegex = /\bid\b([^,]*?)\bunique\b/;
|
|
234
|
+
if (idUniqueRegex.test(sql)) {
|
|
235
|
+
ctx.uniqueIdTables.add(tableName);
|
|
236
|
+
sql = sql.replace(idUniqueRegex, "id$1");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const tableUniqueRegex = /,\s*constraint\s+\w+\s+unique\s*\(\s*id\s*\)/g;
|
|
240
|
+
if (tableUniqueRegex.test(sql)) {
|
|
241
|
+
ctx.uniqueIdTables.add(tableName);
|
|
242
|
+
sql = sql.replace(tableUniqueRegex, "");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return sql;
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
reason:
|
|
250
|
+
"SQLite emits inline FK constraints on CREATE TABLE; normalize them to markers for parity.",
|
|
251
|
+
apply: (sql, ctx) => {
|
|
252
|
+
if (!sql.startsWith("create table ") || !sql.includes(" foreign key ")) {
|
|
253
|
+
return sql;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const tableMatch = sql.match(/^create table\s+(\w+)\s*\(/);
|
|
257
|
+
const tableName = tableMatch?.[1];
|
|
258
|
+
if (!tableName) {
|
|
259
|
+
return sql;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const fkRegex =
|
|
263
|
+
/,\s*(?:constraint\s+(\w+)\s+)?foreign key\s*\(([^)]+)\)\s+references\s+(\w+)\s*\(([^)]+)\)(?:\s+on delete\s+(\w+))?(?:\s+on update\s+(\w+))?/g;
|
|
264
|
+
|
|
265
|
+
let match: RegExpExecArray | null;
|
|
266
|
+
while ((match = fkRegex.exec(sql))) {
|
|
267
|
+
const constraintName = match[1]?.trim();
|
|
268
|
+
const columns = match[2].replace(/\s+/g, " ").trim();
|
|
269
|
+
const referencedTable = match[3].trim();
|
|
270
|
+
const referencedColumns = match[4].replace(/\s+/g, " ").trim();
|
|
271
|
+
const onDelete = (match[5] ?? "restrict").trim();
|
|
272
|
+
const onUpdate = (match[6] ?? "restrict").trim();
|
|
273
|
+
ctx.foreignKeys.add(
|
|
274
|
+
`__fk__ ${tableName}(${columns})->${referencedTable}(${referencedColumns}) on delete ${onDelete} on update ${onUpdate}`,
|
|
275
|
+
);
|
|
276
|
+
if (constraintName) {
|
|
277
|
+
ctx.nameMarkers.push(
|
|
278
|
+
`__fkname__ ${tableName}.${constraintName}(${columns})->${referencedTable}(${referencedColumns}) on delete ${onDelete} on update ${onUpdate}`,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return sql.replace(fkRegex, "");
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
reason: "Drizzle can emit inline column REFERENCES; normalize these to FK markers for parity.",
|
|
288
|
+
apply: (sql, ctx) => {
|
|
289
|
+
if (!sql.startsWith("create table ") || !sql.includes(" references ")) {
|
|
290
|
+
return sql;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const tableMatch = sql.match(/^create table\s+(\w+)\s*\(/);
|
|
294
|
+
const tableName = tableMatch?.[1];
|
|
295
|
+
if (!tableName) {
|
|
296
|
+
return sql;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const referencesRegex =
|
|
300
|
+
/\b(\w+)\b([^,]*?)\s+references\s+(\w+)\s*\(([^)]+)\)(?:\s+on delete\s+(\w+))?(?:\s+on update\s+(\w+))?/g;
|
|
301
|
+
let match: RegExpExecArray | null;
|
|
302
|
+
|
|
303
|
+
while ((match = referencesRegex.exec(sql))) {
|
|
304
|
+
const columnName = match[1].trim();
|
|
305
|
+
const referencedTable = match[3].trim();
|
|
306
|
+
const referencedColumns = match[4].replace(/\s+/g, " ").trim();
|
|
307
|
+
const onDelete = (match[5] ?? "restrict").trim();
|
|
308
|
+
const onUpdate = (match[6] ?? "restrict").trim();
|
|
309
|
+
ctx.foreignKeys.add(
|
|
310
|
+
`__fk__ ${tableName}(${columnName})->${referencedTable}(${referencedColumns}) on delete ${onDelete} on update ${onUpdate}`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return sql.replace(referencesRegex, "$1$2");
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
reason:
|
|
319
|
+
"FK action clauses without any remaining FK/REFERENCES are orphaned; strip to avoid false diffs.",
|
|
320
|
+
apply: (sql) => {
|
|
321
|
+
if (
|
|
322
|
+
sql.startsWith("create table ") &&
|
|
323
|
+
!sql.includes(" references ") &&
|
|
324
|
+
!sql.includes(" foreign key ") &&
|
|
325
|
+
sql.includes(" on delete ")
|
|
326
|
+
) {
|
|
327
|
+
return sql.replace(/\s+on delete\s+\w+/g, "").replace(/\s+on update\s+\w+/g, "");
|
|
328
|
+
}
|
|
329
|
+
return sql;
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
reason: "ALTER TABLE ADD FOREIGN KEY is equivalent to inline FK markers; normalize and drop.",
|
|
334
|
+
apply: (sql, ctx) => {
|
|
335
|
+
if (!sql.startsWith("alter table ") || !sql.includes(" foreign key ")) {
|
|
336
|
+
return sql;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const tableMatch = sql.match(/^alter table\s+(\w+)\s+/);
|
|
340
|
+
const tableName = tableMatch?.[1];
|
|
341
|
+
const fkRegex =
|
|
342
|
+
/(?:constraint\s+(\w+)\s+)?foreign key\s*\(([^)]+)\)\s+references\s+(\w+)\s*\(([^)]+)\)(?:\s+on delete\s+(\w+))?(?:\s+on update\s+(\w+))?/g;
|
|
343
|
+
|
|
344
|
+
if (tableName) {
|
|
345
|
+
let match: RegExpExecArray | null;
|
|
346
|
+
while ((match = fkRegex.exec(sql))) {
|
|
347
|
+
const constraintName = match[1]?.trim();
|
|
348
|
+
const columns = match[2].replace(/\s+/g, " ").trim();
|
|
349
|
+
const referencedTable = match[3].trim();
|
|
350
|
+
const referencedColumns = match[4].replace(/\s+/g, " ").trim();
|
|
351
|
+
const onDelete = (match[5] ?? "restrict").trim();
|
|
352
|
+
const onUpdate = (match[6] ?? "restrict").trim();
|
|
353
|
+
ctx.foreignKeys.add(
|
|
354
|
+
`__fk__ ${tableName}(${columns})->${referencedTable}(${referencedColumns}) on delete ${onDelete} on update ${onUpdate}`,
|
|
355
|
+
);
|
|
356
|
+
if (constraintName) {
|
|
357
|
+
ctx.nameMarkers.push(
|
|
358
|
+
`__fkname__ ${tableName}.${constraintName}(${columns})->${referencedTable}(${referencedColumns}) on delete ${onDelete} on update ${onUpdate}`,
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return null;
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
reason:
|
|
369
|
+
"Some adapters emit a dedicated unique index for external IDs; normalize to external-id markers.",
|
|
370
|
+
apply: (sql, ctx) => {
|
|
371
|
+
const uniqueIndexMatch = sql.match(/^create unique index\s+\w+\s+on\s+(\w+)\s*\(\s*id\s*\)/);
|
|
372
|
+
if (!uniqueIndexMatch) {
|
|
373
|
+
return sql;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
ctx.uniqueIdTables.add(uniqueIndexMatch[1]);
|
|
377
|
+
return null;
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
reason:
|
|
382
|
+
"MySQL inline constraints are semantically equivalent to separate statements; normalize to indexes and column PK.",
|
|
383
|
+
apply: (sql, ctx) => {
|
|
384
|
+
if (ctx.dialect !== "mysql" || !sql.startsWith("create table ")) {
|
|
385
|
+
return sql;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const tableMatch = sql.match(/^create table\s+(\w+)\s*\(/);
|
|
389
|
+
const tableName = tableMatch?.[1];
|
|
390
|
+
let pkColumn: string | undefined;
|
|
391
|
+
let pkConstraintName: string | undefined;
|
|
392
|
+
|
|
393
|
+
if (tableName) {
|
|
394
|
+
const uniqueRegex = /constraint\s+(\w+)\s+unique\s*\(([^)]+)\)/g;
|
|
395
|
+
let match: RegExpExecArray | null;
|
|
396
|
+
while ((match = uniqueRegex.exec(sql))) {
|
|
397
|
+
ctx.extraStatements.push(`create unique index ${match[1]} on ${tableName} (${match[2]})`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const pkMatch = sql.match(/constraint\s+(\w+)\s+primary key\s*\(([^)]+)\)/);
|
|
401
|
+
if (pkMatch) {
|
|
402
|
+
pkConstraintName = pkMatch[1].trim();
|
|
403
|
+
pkColumn = pkMatch[2].trim();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
sql = sql.replace(/,\s*constraint\s+\w+\s+unique\s*\([^)]+\)/g, "");
|
|
408
|
+
sql = sql.replace(/,\s*constraint\s+\w+\s+primary key\s*\([^)]+\)/g, "");
|
|
409
|
+
sql = sql.replace(/\s+,/g, ",").replace(/,\s+\)/g, ")");
|
|
410
|
+
|
|
411
|
+
if (pkColumn) {
|
|
412
|
+
if (tableName && pkConstraintName) {
|
|
413
|
+
ctx.nameMarkers.push(`__pkname__ ${tableName}.${pkConstraintName}(${pkColumn})`);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const pkRegex = new RegExp(`\\b${pkColumn}\\b([^,]*?)\\bprimary key\\b`);
|
|
417
|
+
if (!pkRegex.test(sql)) {
|
|
418
|
+
const columnRegex = new RegExp(`\\b${pkColumn}\\b([^,]*?)\\bnot null\\b`);
|
|
419
|
+
sql = sql.replace(columnRegex, `${pkColumn}$1not null primary key`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return sql.replace(
|
|
424
|
+
/\bauto_increment not null primary key\b/g,
|
|
425
|
+
"not null primary key auto_increment",
|
|
426
|
+
);
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
reason: "Normalize commas/spaces for deterministic statement ordering.",
|
|
431
|
+
apply: (sql) =>
|
|
432
|
+
sql
|
|
433
|
+
.replace(/\s+/g, " ")
|
|
434
|
+
.replace(/\s+,/g, ",")
|
|
435
|
+
.replace(/,\s+/g, ",")
|
|
436
|
+
.replace(/,\s*\)/g, ")")
|
|
437
|
+
.trim(),
|
|
438
|
+
},
|
|
439
|
+
];
|
|
440
|
+
|
|
441
|
+
function normalizeSql(sql: string, dialect: Dialect): string {
|
|
442
|
+
let normalized = sql;
|
|
443
|
+
for (const rule of NORMALIZATION_RULES) {
|
|
444
|
+
normalized = rule.apply(normalized, dialect);
|
|
445
|
+
}
|
|
446
|
+
return normalized;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function applyStatementNormalization(
|
|
450
|
+
sql: string,
|
|
451
|
+
ctx: StatementNormalizationContext,
|
|
452
|
+
): string | null {
|
|
453
|
+
let normalized = sql;
|
|
454
|
+
for (const rule of STATEMENT_NORMALIZATION_RULES) {
|
|
455
|
+
const next = rule.apply(normalized, ctx);
|
|
456
|
+
if (next === null) {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
normalized = next;
|
|
460
|
+
}
|
|
461
|
+
return normalized;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function normalizeStatements(statements: string[], dialect: Dialect): string[] {
|
|
465
|
+
const normalized: string[] = [];
|
|
466
|
+
const context: StatementNormalizationContext = {
|
|
467
|
+
dialect,
|
|
468
|
+
uniqueIdTables: new Set<string>(),
|
|
469
|
+
foreignKeys: new Set<string>(),
|
|
470
|
+
extraStatements: [],
|
|
471
|
+
nameMarkers: [],
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
for (const statement of statements) {
|
|
475
|
+
const normalizedSql = normalizeSql(statement, dialect);
|
|
476
|
+
let sql = normalizedSql;
|
|
477
|
+
if (!sql) {
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const normalizedStatement = applyStatementNormalization(sql, context);
|
|
482
|
+
if (normalizedStatement) {
|
|
483
|
+
normalized.push(normalizedStatement);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
for (const tableName of context.uniqueIdTables) {
|
|
488
|
+
normalized.push(`__unique__ ${tableName}.id`);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
for (const fk of context.foreignKeys) {
|
|
492
|
+
normalized.push(fk);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
for (const marker of context.nameMarkers) {
|
|
496
|
+
const normalizedMarker = applyStatementNormalization(
|
|
497
|
+
normalizeSql(marker, context.dialect),
|
|
498
|
+
context,
|
|
499
|
+
);
|
|
500
|
+
if (normalizedMarker) {
|
|
501
|
+
normalized.push(normalizedMarker);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
for (const extra of context.extraStatements) {
|
|
506
|
+
const normalizedExtra = applyStatementNormalization(
|
|
507
|
+
normalizeSql(extra, context.dialect),
|
|
508
|
+
context,
|
|
509
|
+
);
|
|
510
|
+
if (normalizedExtra) {
|
|
511
|
+
normalized.push(normalizedExtra);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return normalized.sort();
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function isNonDdlStatement(statement: string): boolean {
|
|
519
|
+
const trimmed = statement.trim().toLowerCase();
|
|
520
|
+
return (
|
|
521
|
+
trimmed.startsWith("pragma defer_foreign_keys") || trimmed.startsWith("set foreign_key_checks")
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async function getDrizzleMigrationSql(dialect: Dialect): Promise<string[]> {
|
|
526
|
+
const result = await writeAndLoadSchema(
|
|
527
|
+
`drizzle-kit-parity-${dialect}`,
|
|
528
|
+
paritySchema,
|
|
529
|
+
dialect,
|
|
530
|
+
paritySchema.name,
|
|
531
|
+
false,
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
try {
|
|
535
|
+
const schemaModule = await import(`${result.schemaFilePath}?t=${Date.now()}`);
|
|
536
|
+
let migrationStatements: string[];
|
|
537
|
+
|
|
538
|
+
if (dialect === "postgresql") {
|
|
539
|
+
migrationStatements = await generateMigration(
|
|
540
|
+
generateDrizzleJson({}),
|
|
541
|
+
generateDrizzleJson(schemaModule),
|
|
542
|
+
);
|
|
543
|
+
} else if (dialect === "sqlite") {
|
|
544
|
+
migrationStatements = await generateSQLiteMigration(
|
|
545
|
+
await generateSQLiteDrizzleJson({}),
|
|
546
|
+
await generateSQLiteDrizzleJson(schemaModule),
|
|
547
|
+
);
|
|
548
|
+
} else {
|
|
549
|
+
migrationStatements = await generateMySQLMigration(
|
|
550
|
+
await generateMySQLDrizzleJson({}),
|
|
551
|
+
await generateMySQLDrizzleJson(schemaModule),
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return migrationStatements.filter((statement) => !isNonDdlStatement(statement));
|
|
556
|
+
} finally {
|
|
557
|
+
await result.cleanup();
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function getKyselyMigrationSql(dialect: Dialect): string[] {
|
|
562
|
+
const resolver = createNamingResolver(
|
|
563
|
+
paritySchema,
|
|
564
|
+
paritySchema.name,
|
|
565
|
+
defaultNamingStrategyForDatabase(dialect),
|
|
566
|
+
);
|
|
567
|
+
const prepared = createPreparedMigrations({
|
|
568
|
+
schema: paritySchema,
|
|
569
|
+
namespace: paritySchema.name,
|
|
570
|
+
database: dialect,
|
|
571
|
+
updateVersionInMigration: false,
|
|
572
|
+
resolver,
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
const statements = prepared
|
|
576
|
+
.compile(0, paritySchema.version, { updateVersionInMigration: false })
|
|
577
|
+
.statements.map((statement) => statement.sql);
|
|
578
|
+
|
|
579
|
+
return statements.filter((statement) => !isNonDdlStatement(statement));
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
describe("drizzle-kit migrations match generic SQL DDL", () => {
|
|
583
|
+
const cases: Dialect[] = ["postgresql", "sqlite", "mysql"];
|
|
584
|
+
|
|
585
|
+
for (const dialect of cases) {
|
|
586
|
+
it(`matches DDL for ${dialect}`, async () => {
|
|
587
|
+
const drizzleSql = await getDrizzleMigrationSql(dialect);
|
|
588
|
+
const kyselySql = getKyselyMigrationSql(dialect);
|
|
589
|
+
|
|
590
|
+
const normalizedDrizzle = normalizeStatements(drizzleSql, dialect);
|
|
591
|
+
const normalizedKysely = normalizeStatements(kyselySql, dialect);
|
|
592
|
+
|
|
593
|
+
for (const [index, statement] of normalizedDrizzle.entries()) {
|
|
594
|
+
expect(statement).toEqual(normalizedKysely[index]);
|
|
595
|
+
}
|
|
596
|
+
expect(normalizedDrizzle.length).toEqual(normalizedKysely.length);
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { mkdir, writeFile, rm, access } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { generateDrizzleSchema, type SupportedProvider } from "../../schema-output/drizzle";
|
|
4
4
|
import type { Schema } from "../../schema/create";
|
|
5
|
-
import { internalSchema } from "../../fragments/internal-fragment";
|
|
5
|
+
import { internalSchema } from "../../fragments/internal-fragment.schema";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Writes a Fragno schema to a temporary TypeScript file and dynamically imports it.
|
|
@@ -19,7 +19,8 @@ export async function writeAndLoadSchema(
|
|
|
19
19
|
testFileName: string,
|
|
20
20
|
schema: Schema,
|
|
21
21
|
dialect: SupportedProvider,
|
|
22
|
-
namespace?: string,
|
|
22
|
+
namespace?: string | null,
|
|
23
|
+
includeInternalSchema: boolean = true,
|
|
23
24
|
) {
|
|
24
25
|
// Create test-specific directory inside _generated
|
|
25
26
|
const baseDir = join(import.meta.dirname, "_generated");
|
|
@@ -41,15 +42,18 @@ export async function writeAndLoadSchema(
|
|
|
41
42
|
// Generate and write the Drizzle schema to file
|
|
42
43
|
// Always include settings schema first (as done in generation-engine.ts), then the test schema
|
|
43
44
|
// De-duplicate: if the test schema IS the settings schema, don't add it twice
|
|
44
|
-
const fragments: Array<{ namespace: string; schema: Schema }> = [
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const fragments: Array<{ namespace: string | null; schema: Schema }> = [];
|
|
46
|
+
|
|
47
|
+
if (includeInternalSchema) {
|
|
48
|
+
fragments.push({ namespace: null, schema: internalSchema });
|
|
49
|
+
}
|
|
47
50
|
|
|
48
51
|
if (schema !== internalSchema) {
|
|
49
|
-
|
|
52
|
+
const effectiveNamespace = namespace === undefined ? schema.name : namespace;
|
|
53
|
+
fragments.push({ namespace: effectiveNamespace, schema });
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
const drizzleSchemaTs =
|
|
56
|
+
const drizzleSchemaTs = generateDrizzleSchema(fragments, dialect);
|
|
53
57
|
await writeFile(schemaFilePath, drizzleSchemaTs, "utf-8");
|
|
54
58
|
|
|
55
59
|
// Ensure the file is accessible before importing (handle race conditions)
|