kysely-ddl 0.1.0 → 0.2.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/CHANGELOG.md +47 -0
- package/README.md +88 -44
- package/dist/generator/diff.d.ts +2 -0
- package/dist/generator/diff.js +5 -2
- package/dist/generator/generate.d.ts +9 -3
- package/dist/generator/generate.js +8 -2
- package/dist/generator/render.d.ts +11 -2
- package/dist/generator/render.js +33 -6
- package/dist/generator/snapshot.d.ts +6 -0
- package/dist/generator/snapshot.js +1 -0
- package/dist/index.d.ts +18 -8
- package/dist/index.js +15 -8
- package/dist/migrator/index.d.ts +15 -0
- package/dist/migrator/index.js +13 -0
- package/dist/{kysely → migrator}/provider.js +21 -9
- package/dist/{kysely → migrator}/runner.d.ts +18 -6
- package/dist/{kysely → migrator}/runner.js +46 -13
- package/dist/migrator/store.d.ts +34 -2
- package/dist/migrator/store.js +104 -19
- package/dist/table/columns.d.ts +1 -1
- package/dist/table/define.d.ts +11 -1
- package/dist/table/define.js +2 -1
- package/package.json +4 -4
- package/dist/kysely/index.d.ts +0 -12
- package/dist/kysely/index.js +0 -3
- /package/dist/{kysely → migrator}/provider.d.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versions follow [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.2.0] — 2026-09-12
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `concurrently: true` on an index in `defineTable`: the index is built and
|
|
12
|
+
dropped with `CONCURRENTLY`. Postgres refuses that inside a transaction and
|
|
13
|
+
inside a multi-statement query, so the generator writes such statements into a
|
|
14
|
+
migration of their own, `<stamp>_<name>_concurrently.sql`, marked
|
|
15
|
+
`--> no-transaction` and split by `--> statement-breakpoint`, with a
|
|
16
|
+
`DROP INDEX CONCURRENTLY IF EXISTS` in front of every create so that a rerun
|
|
17
|
+
after a failure is clean. `GenerateResult.concurrently` holds these
|
|
18
|
+
statements. Toggling the flag on an existing index is not a change; the
|
|
19
|
+
snapshot records it, and older snapshots read as `false`.
|
|
20
|
+
- `--> no-transaction`: a header marker in a `.sql` migration. The runner honors
|
|
21
|
+
it under `transaction: 'each'` and `'none'` and rejects it under `'all'`
|
|
22
|
+
before applying anything. `sqlFileMigrationProvider` turns it into
|
|
23
|
+
`config: { transaction: false }` for Kysely 0.30+
|
|
24
|
+
(`transactionMode: 'per-migration'`); on older Kysely the marked migration
|
|
25
|
+
fails with a clear error when the `Migrator` runs it inside a transaction.
|
|
26
|
+
- `MigrationError.line`: the line postgres pointed at inside the failing query.
|
|
27
|
+
- `readMigration`, `renderConcurrentStatements`, `NO_TRANSACTION_MARKER`,
|
|
28
|
+
`CONCURRENTLY_SUFFIX` and the `MigrationFile` type.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Breaking:** entry points. `kysely-ddl/kysely` is gone: `inferKyselyTable`,
|
|
33
|
+
`inferKyselyDatabase`, `jsonb`, `jsonbArray` and the `Jsonb` type now come
|
|
34
|
+
from `kysely-ddl`, which therefore imports `kysely`, the peer dependency, at
|
|
35
|
+
runtime. The runner (`createMigrator`, `migrateToLatest`, `MigrationError`,
|
|
36
|
+
`DEFAULT_JOURNAL_TABLE`, `MIGRATION_LOCK_ID` and their types) and
|
|
37
|
+
`sqlFileMigrationProvider` moved to the new `kysely-ddl/migrator`, so that a
|
|
38
|
+
project which applies its migrations some other way does not pull them in.
|
|
39
|
+
- **Breaking:** the `defineTable` option `tableName` is now `name`.
|
|
40
|
+
- **Breaking:** the runner's default `transaction` mode is `'each'` instead of
|
|
41
|
+
`'all'`, so that a generated `_concurrently` migration runs out of the box.
|
|
42
|
+
Pass `transaction: 'all'` to keep the whole run atomic.
|
|
43
|
+
- **Breaking:** `writeMigration` returns the names of the files written, an
|
|
44
|
+
array, instead of one name.
|
|
45
|
+
- **Breaking:** a `Change` of kind `dropIndex` carries `concurrently`.
|
|
46
|
+
- Ordinary migrations are written as plain SQL, without
|
|
47
|
+
`--> statement-breakpoint`, and the runner sends such a file to postgres as
|
|
48
|
+
one query: postgres runs it as one implicit transaction and reports the
|
|
49
|
+
failing line. Files with breakpoints, hand-written or `_concurrently`, are
|
|
50
|
+
still sent one chunk at a time.
|
|
51
|
+
|
|
5
52
|
## [0.1.0] — 2026-09-10
|
|
6
53
|
|
|
7
54
|
First release.
|
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/hehmonke/kysely-ddl/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/kysely-ddl)
|
|
5
|
+
[](LICENSE)
|
|
5
6
|
|
|
6
7
|
PostgreSQL schema as TypeScript code, SQL migrations generated from snapshot
|
|
7
8
|
diffs, Kysely table types, and a migration runner on top of your `Kysely`.
|
|
@@ -28,8 +29,8 @@ Bun, or any PostgreSQL dialect for Kysely. Runtime: Node >= 20 or Bun >= 1.2.
|
|
|
28
29
|
|
|
29
30
|
| import | contents |
|
|
30
31
|
|---|---|
|
|
31
|
-
| `kysely-ddl` | table definitions, migration generation, migration files on disk
|
|
32
|
-
| `kysely-ddl/
|
|
32
|
+
| `kysely-ddl` | table definitions, migration generation, migration files on disk, `inferKyselyTable` / `inferKyselyDatabase`, `jsonb` / `jsonbArray` |
|
|
33
|
+
| `kysely-ddl/migrator` | running migrations: `createMigrator` / `migrateToLatest` and `sqlFileMigrationProvider`. A separate entry point, so that a project which applies its migrations some other way does not pull the runner in |
|
|
33
34
|
|
|
34
35
|
## Quick start
|
|
35
36
|
|
|
@@ -39,7 +40,7 @@ Bun, or any PostgreSQL dialect for Kysely. Runtime: Node >= 20 or Bun >= 1.2.
|
|
|
39
40
|
import { defineTable, ref, sql } from 'kysely-ddl';
|
|
40
41
|
|
|
41
42
|
export const userTable = defineTable({
|
|
42
|
-
|
|
43
|
+
name: 'user',
|
|
43
44
|
// builders arrive as an argument; they are not exported one by one
|
|
44
45
|
columns: t => ({
|
|
45
46
|
// no explicit column name -> derived from the property: created_at, apple_id, ...
|
|
@@ -56,7 +57,7 @@ export const userTable = defineTable({
|
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
export const sessionTable = defineTable({
|
|
59
|
-
|
|
60
|
+
name: 'session',
|
|
60
61
|
columns: t => ({
|
|
61
62
|
id: t.uuid().notNull().default(sql`gen_random_uuid()`),
|
|
62
63
|
userId: t.uuid().notNull(),
|
|
@@ -75,17 +76,19 @@ import * as schema from './schema';
|
|
|
75
76
|
const dir = './migrations';
|
|
76
77
|
const result = generateMigration([schema.userTable, schema.sessionTable], readLatestSnapshot(dir));
|
|
77
78
|
|
|
78
|
-
if (result.
|
|
79
|
+
if (result.changes.length === 0) {
|
|
79
80
|
console.log('no changes');
|
|
80
81
|
} else {
|
|
81
|
-
|
|
82
|
+
// one file, or two when the diff has indexes built CONCURRENTLY:
|
|
83
|
+
// [ '20260910123045_migration', '20260910123046_migration_concurrently' ]
|
|
84
|
+
console.log(writeMigration(dir, process.argv[2] ?? 'migration', result));
|
|
82
85
|
}
|
|
83
86
|
```
|
|
84
87
|
|
|
85
88
|
`migrate.ts`, applying migrations:
|
|
86
89
|
|
|
87
90
|
```ts
|
|
88
|
-
import { migrateToLatest } from 'kysely-ddl/
|
|
91
|
+
import { migrateToLatest } from 'kysely-ddl/migrator';
|
|
89
92
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
90
93
|
import pg from 'pg';
|
|
91
94
|
|
|
@@ -102,7 +105,7 @@ try {
|
|
|
102
105
|
Types for queries:
|
|
103
106
|
|
|
104
107
|
```ts
|
|
105
|
-
import type { inferKyselyDatabase } from 'kysely-ddl
|
|
108
|
+
import type { inferKyselyDatabase } from 'kysely-ddl';
|
|
106
109
|
import * as schema from './schema';
|
|
107
110
|
|
|
108
111
|
type DB = inferKyselyDatabase<typeof schema>;
|
|
@@ -120,7 +123,7 @@ A hybrid: columns as chains, everything else as a declarative block.
|
|
|
120
123
|
|---|---|
|
|
121
124
|
| column types | `t.uuid` `t.varchar({ length })` `t.integer` `t.bigint` `t.boolean` `t.numeric({ precision, scale })` `t.timestamp({ withTimezone, precision })` `t.jsonb` `t.enum([...])` |
|
|
122
125
|
| modifiers | `.notNull()` `.default(v \| sql)` `.defaultNow()` `.array()` `.$type<T>()` `.generatedAlwaysAsIdentity()` |
|
|
123
|
-
| table | `primaryKey` (composite too), `uniques`, `indexes` (unique
|
|
126
|
+
| table | `primaryKey` (composite too), `uniques`, `indexes` (unique, partial via `where`, built online via `concurrently`), `foreignKeys` (`onDelete` / `onUpdate`), `checks`; names are optional everywhere |
|
|
124
127
|
| expressions | `` sql`...` `` with column and literal interpolation, `inArray(c.status, [...])` |
|
|
125
128
|
|
|
126
129
|
When a column name is not given, it is derived from the property name with the
|
|
@@ -183,17 +186,16 @@ Collisions are caught too: two objects on the same columns, an index named like
|
|
|
183
186
|
a unique constraint, a check with neither columns nor a name, two properties
|
|
184
187
|
mapping to the same database name.
|
|
185
188
|
|
|
186
|
-
###
|
|
189
|
+
### Limitations
|
|
187
190
|
|
|
188
191
|
Native enums, stored generated columns, schemas other than `public`, views,
|
|
189
|
-
`DEFERRABLE`, covering indexes, index methods other than btree.
|
|
190
|
-
|
|
191
|
-
in the diff -> a branch in the renderer.
|
|
192
|
+
`DEFERRABLE`, covering indexes, index methods other than btree. Pull requests are
|
|
193
|
+
welcome; [CONTRIBUTING.md](CONTRIBUTING.md) describes how such a feature is added.
|
|
192
194
|
|
|
193
195
|
## Types for Kysely
|
|
194
196
|
|
|
195
197
|
```ts
|
|
196
|
-
import type { inferKyselyDatabase, inferKyselyTable } from 'kysely-ddl
|
|
198
|
+
import type { inferKyselyDatabase, inferKyselyTable } from 'kysely-ddl';
|
|
197
199
|
import type { Insertable, Selectable } from 'kysely';
|
|
198
200
|
|
|
199
201
|
type UserTable = inferKyselyTable<typeof userTable>;
|
|
@@ -255,7 +257,7 @@ and the `Jsonb<T>` brand in the write types keeps raw objects and strings from
|
|
|
255
257
|
slipping past them:
|
|
256
258
|
|
|
257
259
|
```ts
|
|
258
|
-
import { jsonb, jsonbArray } from 'kysely-ddl
|
|
260
|
+
import { jsonb, jsonbArray } from 'kysely-ddl';
|
|
259
261
|
|
|
260
262
|
await db.insertInto('user').values({ settings: jsonb({ theme: 'dark', tags: ['a'] }) }).execute();
|
|
261
263
|
await db.updateTable('user').set({ settings: jsonb({ theme: 'light' }) }).where('id', '=', id).execute();
|
|
@@ -294,15 +296,56 @@ migrations/
|
|
|
294
296
|
snapshot.json
|
|
295
297
|
```
|
|
296
298
|
|
|
297
|
-
`writeMigration(dir, name, result)` writes the `.sql`
|
|
298
|
-
`snapshot.json`,
|
|
299
|
+
`writeMigration(dir, name, result)` writes the `.sql` files and updates
|
|
300
|
+
`snapshot.json`, returning the names in application order.
|
|
301
|
+
`readLatestSnapshot(dir)` reads the snapshot for the next diff, and
|
|
299
302
|
`listMigrations(dir)` returns names in the order the runner applies them (by
|
|
300
303
|
character codes, like Kysely). There is no separate journal on disk: the table in
|
|
301
304
|
the database knows what has been applied.
|
|
302
305
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
+
An ordinary migration is plain SQL. The runner sends the whole file to postgres
|
|
307
|
+
as one query, and postgres runs a multi-statement query as one implicit
|
|
308
|
+
transaction, so even under `transaction: 'none'` a failing statement rolls the
|
|
309
|
+
file back. `MigrationError.line` says which line postgres pointed at.
|
|
310
|
+
|
|
311
|
+
### Indexes built `CONCURRENTLY`
|
|
312
|
+
|
|
313
|
+
`CREATE INDEX CONCURRENTLY` does not lock the table against writes, but postgres
|
|
314
|
+
refuses it inside a transaction and inside a multi-statement query alike. An
|
|
315
|
+
index declared with `concurrently: true` is therefore written as a migration of
|
|
316
|
+
its own, one second after the ordinary one:
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
indexes: [{ columns: ['userId'], concurrently: true }],
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
migrations/
|
|
324
|
+
20260910120000_add_tickets.sql <- the table and its fk, one transaction
|
|
325
|
+
20260910120001_add_tickets_concurrently.sql <- the index, outside any transaction
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
```sql
|
|
329
|
+
--> no-transaction
|
|
330
|
+
DROP INDEX CONCURRENTLY IF EXISTS "ticket_user_id_idx";
|
|
331
|
+
--> statement-breakpoint
|
|
332
|
+
CREATE INDEX CONCURRENTLY "ticket_user_id_idx" ON "ticket" ("user_id");
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
The `--> no-transaction` marker in the header tells the runner to skip the
|
|
336
|
+
transaction, and `--> statement-breakpoint` makes it send the statements one at a
|
|
337
|
+
time, which `CONCURRENTLY` also demands. Both are comments for `psql`. Dropping
|
|
338
|
+
such an index is `DROP INDEX CONCURRENTLY` in the same kind of file. A failed
|
|
339
|
+
`CREATE INDEX CONCURRENTLY` leaves an invalid index behind while the migration
|
|
340
|
+
stays unrecorded, hence the `DROP INDEX CONCURRENTLY IF EXISTS` in front: the
|
|
341
|
+
rerun is clean. Toggling `concurrently` on an existing index changes nothing.
|
|
342
|
+
|
|
343
|
+
The runner applies such a migration under the default `transaction: 'each'` and
|
|
344
|
+
under `'none'`, and rejects it under `'all'` before touching the database; through the provider,
|
|
345
|
+
Kysely's `Migrator` needs `transactionMode: 'per-migration'` (Kysely 0.30+).
|
|
346
|
+
Both markers may also be written by hand. A `--> no-transaction` below the
|
|
347
|
+
header is an error: postgres would take it for a comment. `readMigration(dir, name)`
|
|
348
|
+
returns the execution chunks together with the `transaction` flag.
|
|
306
349
|
|
|
307
350
|
Names are monotonic: if the previous migration was created in the same second,
|
|
308
351
|
the timestamp is bumped forward, otherwise the suffix would decide the order.
|
|
@@ -340,13 +383,13 @@ The runner takes a ready `Kysely` with any PostgreSQL dialect and works through
|
|
|
340
383
|
single connection (`db.connection()`): the lock and the transaction live on it.
|
|
341
384
|
|
|
342
385
|
```ts
|
|
343
|
-
import { createMigrator, migrateToLatest } from 'kysely-ddl/
|
|
386
|
+
import { createMigrator, migrateToLatest } from 'kysely-ddl/migrator';
|
|
344
387
|
|
|
345
388
|
const migrator = createMigrator({
|
|
346
389
|
db, // a Kysely instance, not a Transaction
|
|
347
390
|
migrationsDir: './migrations',
|
|
348
391
|
journalTable: 'kysely_migration', // the default, same as Kysely
|
|
349
|
-
transaction: '
|
|
392
|
+
transaction: 'each', // the default; 'all' | 'each' | 'none'
|
|
350
393
|
allowUnordered: false,
|
|
351
394
|
});
|
|
352
395
|
|
|
@@ -358,13 +401,15 @@ await migrateToLatest({ db, migrationsDir: './migrations' }); // the same in one
|
|
|
358
401
|
|
|
359
402
|
| option | effect |
|
|
360
403
|
|---|---|
|
|
361
|
-
| `transaction: '
|
|
362
|
-
| `transaction: '
|
|
363
|
-
| `transaction: 'none'` | no
|
|
404
|
+
| `transaction: 'each'` | the default: one transaction per migration, the ones before the failure stay applied. A `--> no-transaction` migration runs without one, its journal row in autocommit right after it |
|
|
405
|
+
| `transaction: 'all'` | the whole run in one transaction, like Kysely: a failing migration rolls back the earlier ones from the same run. A `--> no-transaction` migration is an error before anything is applied: one shared transaction cannot leave it out |
|
|
406
|
+
| `transaction: 'none'` | no `BEGIN` at all. A plain file still runs as one implicit transaction; only a file split by `--> statement-breakpoint` fails statement by statement. `--> no-transaction` changes nothing here |
|
|
364
407
|
| `allowUnordered` | apply migrations that sort before already applied ones (branch merges). An error by default |
|
|
365
408
|
|
|
366
|
-
A failing
|
|
367
|
-
|
|
409
|
+
A failing query throws `MigrationError` with `migration`, `statement` (the text
|
|
410
|
+
sent: a whole file, or one chunk between breakpoints), `line` (where postgres
|
|
411
|
+
pointed inside it), `applied` (what this run applied before the failure) and the
|
|
412
|
+
driver's `cause`.
|
|
368
413
|
A migration present in the journal but missing on disk is an error: restore the
|
|
369
414
|
file from history or delete the row by hand.
|
|
370
415
|
|
|
@@ -378,13 +423,22 @@ lock is the same `pg_advisory_lock`. So `.sql` migrations can also be run by the
|
|
|
378
423
|
built-in `Migrator`, and the two runners can alternate on one database:
|
|
379
424
|
|
|
380
425
|
```ts
|
|
381
|
-
import { sqlFileMigrationProvider } from 'kysely-ddl/
|
|
426
|
+
import { sqlFileMigrationProvider } from 'kysely-ddl/migrator';
|
|
382
427
|
import { Migrator } from 'kysely/migration';
|
|
383
428
|
|
|
384
429
|
const migrator = new Migrator({ db, provider: sqlFileMigrationProvider('./migrations') });
|
|
385
430
|
const { error, results } = await migrator.migrateToLatest();
|
|
386
431
|
```
|
|
387
432
|
|
|
433
|
+
`--> no-transaction` reaches the `Migrator` as `config: { transaction: false }`
|
|
434
|
+
on the migration, the field Kysely 0.30+ honors under
|
|
435
|
+
`new Migrator({ ..., transactionMode: 'per-migration' })`: every migration in
|
|
436
|
+
its own transaction, the marked one without. Under the default `'per-run'`
|
|
437
|
+
Kysely itself reports the marked migration as an error and applies nothing.
|
|
438
|
+
Older Kysely ignores `config`; there the marked migration checks that it is not
|
|
439
|
+
inside a transaction and fails with an error naming the fix, and the only way to
|
|
440
|
+
run it is `disableTransactions: true` for the whole run.
|
|
441
|
+
|
|
388
442
|
There are no rollbacks: the generator only writes forward. Without `down`,
|
|
389
443
|
Kysely skips a migration on `migrateDown` (`NotExecuted`) and leaves it in the
|
|
390
444
|
journal, so by default the provider supplies a `down` that fails with a clear
|
|
@@ -415,23 +469,13 @@ What the checks showed on `pg` 8.23 and `Bun.SQL` 1.4 (parameters via Kysely):
|
|
|
415
469
|
Reading jsonb, JSON arrays, `jsonb[]` and `varchar[]` yields parsed JS values
|
|
416
470
|
with both drivers.
|
|
417
471
|
|
|
418
|
-
##
|
|
419
|
-
|
|
420
|
-
```bash
|
|
421
|
-
bun install
|
|
422
|
-
bun run db:up # postgres:18-alpine on 54329
|
|
423
|
-
DATABASE_URL=postgres://postgres:postgres@localhost:54329/kysely_ddl bun test
|
|
424
|
-
bun run typecheck && bun run lint && bun run build
|
|
425
|
-
DATABASE_URL=... bun run smoke:node # the built dist under Node with pg
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
Without `DATABASE_URL` the integration tests are skipped, the unit tests always
|
|
429
|
-
run. Type-level checks live in `test/types.test-d.ts` and are read by `tsc` only.
|
|
472
|
+
## Contributing
|
|
430
473
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
474
|
+
Bug reports and pull requests are welcome on
|
|
475
|
+
[GitHub](https://github.com/hehmonke/kysely-ddl/issues). [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
476
|
+
covers setting up the repository, running the tests and cutting a release.
|
|
477
|
+
Changes between versions are listed in [CHANGELOG.md](CHANGELOG.md).
|
|
434
478
|
|
|
435
479
|
## License
|
|
436
480
|
|
|
437
|
-
MIT
|
|
481
|
+
[MIT](LICENSE)
|
package/dist/generator/diff.d.ts
CHANGED
|
@@ -63,6 +63,8 @@ export type Change = {
|
|
|
63
63
|
} | {
|
|
64
64
|
readonly kind: 'dropIndex';
|
|
65
65
|
readonly index: string;
|
|
66
|
+
/** `DROP INDEX CONCURRENTLY`, in the `--> no-transaction` migration together with the concurrent creates. */
|
|
67
|
+
readonly concurrently: boolean;
|
|
66
68
|
};
|
|
67
69
|
export declare function diffSnapshots(prev: Snapshot, next: Snapshot): Change[];
|
|
68
70
|
export {};
|
package/dist/generator/diff.js
CHANGED
|
@@ -24,6 +24,7 @@ function sameConstraint(a, b) {
|
|
|
24
24
|
}
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
|
+
/** `concurrently` is left out on purpose: it says how to build the index, not what the index is. */
|
|
27
28
|
function sameIndex(a, b) {
|
|
28
29
|
return a.unique === b.unique && sameArray(a.columns, b.columns) && a.where === b.where;
|
|
29
30
|
}
|
|
@@ -137,13 +138,15 @@ export function diffSnapshots(prev, next) {
|
|
|
137
138
|
createIndexes.push({ kind: 'createIndex', table: after.name, index });
|
|
138
139
|
}
|
|
139
140
|
else if (!sameIndex(old, index)) {
|
|
140
|
-
|
|
141
|
+
// the drop follows the NEW flag: both statements must land in the same
|
|
142
|
+
// migration, or the create would run before the drop
|
|
143
|
+
dropIndexes.push({ kind: 'dropIndex', index: index.name, concurrently: index.concurrently });
|
|
141
144
|
createIndexes.push({ kind: 'createIndex', table: after.name, index });
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
for (const index of before.indexes) {
|
|
145
148
|
if (!afterIndexes.has(index.name)) {
|
|
146
|
-
dropIndexes.push({ kind: 'dropIndex', index: index.name });
|
|
149
|
+
dropIndexes.push({ kind: 'dropIndex', index: index.name, concurrently: index.concurrently });
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
}
|
|
@@ -7,13 +7,19 @@ import type { AnyTable } from '../table/define.ts';
|
|
|
7
7
|
import { type Change } from './diff.ts';
|
|
8
8
|
import { type Snapshot } from './snapshot.ts';
|
|
9
9
|
export interface GenerateResult {
|
|
10
|
-
/**
|
|
10
|
+
/** The migration text, without the `CONCURRENTLY` statements; an empty string means none. */
|
|
11
11
|
readonly sql: string;
|
|
12
|
-
/** The same SQL
|
|
12
|
+
/** The same SQL statement by statement. */
|
|
13
13
|
readonly statements: readonly string[];
|
|
14
|
+
/**
|
|
15
|
+
* The `CONCURRENTLY` index statements. Postgres refuses them inside a
|
|
16
|
+
* transaction and inside a multi-statement query alike, so `writeMigration`
|
|
17
|
+
* puts them into a migration of their own, marked `--> no-transaction`.
|
|
18
|
+
*/
|
|
19
|
+
readonly concurrently: readonly string[];
|
|
14
20
|
/** The snapshot to store next to the migration. */
|
|
15
21
|
readonly snapshot: Snapshot;
|
|
16
|
-
/** Parsed changes; handy for tests and for a "what changed" summary. */
|
|
22
|
+
/** Parsed changes; handy for tests and for a "what changed" summary. Empty means nothing to write. */
|
|
17
23
|
readonly changes: readonly Change[];
|
|
18
24
|
}
|
|
19
25
|
export declare function generateMigration(tables: readonly AnyTable[], previous?: Snapshot): GenerateResult;
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { diffSnapshots } from './diff.js';
|
|
2
|
-
import { renderChanges, renderStatements } from './render.js';
|
|
2
|
+
import { renderChanges, renderConcurrentStatements, renderStatements } from './render.js';
|
|
3
3
|
import { buildSnapshot, EMPTY_SNAPSHOT } from './snapshot.js';
|
|
4
4
|
export function generateMigration(tables, previous = EMPTY_SNAPSHOT) {
|
|
5
5
|
const snapshot = buildSnapshot(tables);
|
|
6
6
|
const changes = diffSnapshots(previous, snapshot);
|
|
7
|
-
return {
|
|
7
|
+
return {
|
|
8
|
+
sql: renderChanges(changes),
|
|
9
|
+
statements: renderStatements(changes),
|
|
10
|
+
concurrently: renderConcurrentStatements(changes),
|
|
11
|
+
snapshot,
|
|
12
|
+
changes,
|
|
13
|
+
};
|
|
8
14
|
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { Change } from './diff.ts';
|
|
2
2
|
export declare function renderChange(change: Change): string[];
|
|
3
|
-
/**
|
|
3
|
+
/** The statements of the ordinary migration, that is, everything but the `CONCURRENTLY` ones. */
|
|
4
4
|
export declare function renderStatements(changes: readonly Change[]): string[];
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* The statements of the `--> no-transaction` migration: `CONCURRENTLY` drops and
|
|
7
|
+
* creates. A replaced index yields its drop twice, as a change of its own and as
|
|
8
|
+
* the guard in front of the create, so duplicates are folded.
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderConcurrentStatements(changes: readonly Change[]): string[];
|
|
11
|
+
/**
|
|
12
|
+
* The ordinary migration as one text: multi-line statements are separated by a
|
|
13
|
+
* blank line, single-line ones follow each other. `writeMigration` writes it as is.
|
|
14
|
+
*/
|
|
6
15
|
export declare function renderChanges(changes: readonly Change[]): string;
|
package/dist/generator/render.js
CHANGED
|
@@ -67,8 +67,19 @@ function createTable(table) {
|
|
|
67
67
|
}
|
|
68
68
|
function createIndex(table, index) {
|
|
69
69
|
const unique = index.unique ? 'UNIQUE ' : '';
|
|
70
|
+
const concurrently = index.concurrently ? 'CONCURRENTLY ' : '';
|
|
70
71
|
const where = index.where === null ? '' : ` WHERE ${index.where}`;
|
|
71
|
-
return `CREATE ${unique}INDEX ${q(index.name)} ON ${q(table)} (${columns(index.columns)})${where};`;
|
|
72
|
+
return `CREATE ${unique}INDEX ${concurrently}${q(index.name)} ON ${q(table)} (${columns(index.columns)})${where};`;
|
|
73
|
+
}
|
|
74
|
+
function dropIndexConcurrently(name) {
|
|
75
|
+
return `DROP INDEX CONCURRENTLY IF EXISTS ${q(name)};`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Changes whose SQL postgres refuses inside a transaction, and inside a
|
|
79
|
+
* multi-statement query too. `writeMigration` gives them a migration of their own.
|
|
80
|
+
*/
|
|
81
|
+
function isConcurrent(change) {
|
|
82
|
+
return (change.kind === 'createIndex' && change.index.concurrently) || (change.kind === 'dropIndex' && change.concurrently);
|
|
72
83
|
}
|
|
73
84
|
function alterColumn(table, from, to) {
|
|
74
85
|
const head = `ALTER TABLE ${q(table)} ALTER COLUMN ${q(to.name)}`;
|
|
@@ -111,18 +122,34 @@ export function renderChange(change) {
|
|
|
111
122
|
`ADD ${constraintClause(change.constraint)};`,
|
|
112
123
|
];
|
|
113
124
|
case 'createIndex':
|
|
114
|
-
|
|
125
|
+
// a failed CREATE INDEX CONCURRENTLY leaves an invalid index behind, and the
|
|
126
|
+
// migration stays unrecorded; the drop in front makes the rerun clean
|
|
127
|
+
return change.index.concurrently
|
|
128
|
+
? [dropIndexConcurrently(change.index.name), createIndex(change.table, change.index)]
|
|
129
|
+
: [createIndex(change.table, change.index)];
|
|
115
130
|
case 'dropIndex':
|
|
116
|
-
return [`DROP INDEX ${q(change.index)};`];
|
|
131
|
+
return [change.concurrently ? dropIndexConcurrently(change.index) : `DROP INDEX ${q(change.index)};`];
|
|
117
132
|
default:
|
|
118
133
|
return unreachable(change);
|
|
119
134
|
}
|
|
120
135
|
}
|
|
121
|
-
/**
|
|
136
|
+
/** The statements of the ordinary migration, that is, everything but the `CONCURRENTLY` ones. */
|
|
122
137
|
export function renderStatements(changes) {
|
|
123
|
-
return changes.flatMap(renderChange);
|
|
138
|
+
return changes.filter(change => !isConcurrent(change)).flatMap(renderChange);
|
|
124
139
|
}
|
|
125
|
-
/**
|
|
140
|
+
/**
|
|
141
|
+
* The statements of the `--> no-transaction` migration: `CONCURRENTLY` drops and
|
|
142
|
+
* creates. A replaced index yields its drop twice, as a change of its own and as
|
|
143
|
+
* the guard in front of the create, so duplicates are folded.
|
|
144
|
+
*/
|
|
145
|
+
export function renderConcurrentStatements(changes) {
|
|
146
|
+
const statements = changes.filter(isConcurrent).flatMap(renderChange);
|
|
147
|
+
return statements.filter((statement, i) => statements.indexOf(statement) === i);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The ordinary migration as one text: multi-line statements are separated by a
|
|
151
|
+
* blank line, single-line ones follow each other. `writeMigration` writes it as is.
|
|
152
|
+
*/
|
|
126
153
|
export function renderChanges(changes) {
|
|
127
154
|
const statements = renderStatements(changes);
|
|
128
155
|
let out = '';
|
|
@@ -25,6 +25,12 @@ export interface TableSnapshot {
|
|
|
25
25
|
readonly unique: boolean;
|
|
26
26
|
readonly columns: readonly string[];
|
|
27
27
|
readonly where: string | null;
|
|
28
|
+
/**
|
|
29
|
+
* Built and dropped with `CONCURRENTLY`. How the index is built, not what it
|
|
30
|
+
* is: the diff ignores it when comparing indexes. Snapshots written before
|
|
31
|
+
* the field existed simply lack it, which reads as `false`.
|
|
32
|
+
*/
|
|
33
|
+
readonly concurrently: boolean;
|
|
28
34
|
}[];
|
|
29
35
|
readonly foreignKeys: readonly {
|
|
30
36
|
readonly name: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,27 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* kysely-ddl: PostgreSQL schema as code
|
|
3
|
-
*
|
|
2
|
+
* kysely-ddl: PostgreSQL schema as code, SQL migration generation and the
|
|
3
|
+
* Kysely side of it, row types and jsonb values. Imports `kysely`, the peer
|
|
4
|
+
* dependency, and no driver.
|
|
4
5
|
*
|
|
5
6
|
* Layers and dependency direction:
|
|
6
7
|
*
|
|
7
|
-
* table ◄── generator ◄── migrator ◄──
|
|
8
|
+
* table ◄── generator ◄── migrator/store ◄── migrator (runner, provider)
|
|
9
|
+
* table ◄── kysely (row types, jsonb values)
|
|
8
10
|
*
|
|
9
11
|
* Entry points:
|
|
10
12
|
*
|
|
11
|
-
* kysely-ddl
|
|
12
|
-
*
|
|
13
|
+
* kysely-ddl — this file: tables, generation, migration files on disk,
|
|
14
|
+
* `inferKyselyTable` / `inferKyselyDatabase`, `jsonb` / `jsonbArray`
|
|
15
|
+
* kysely-ddl/migrator — running migrations: `createMigrator` / `migrateToLatest`
|
|
16
|
+
* and `sqlFileMigrationProvider`; separate, so that a project
|
|
17
|
+
* which applies migrations some other way does not pull it in
|
|
13
18
|
*
|
|
14
19
|
* Exports are sorted by module path: generator (snapshot, diff, render,
|
|
15
|
-
* facade), migrator (files on disk), table
|
|
20
|
+
* facade), kysely (row types, jsonb values), migrator (files on disk), table
|
|
21
|
+
* (table definitions).
|
|
16
22
|
*/
|
|
17
23
|
export { diffSnapshots } from './generator/diff.ts';
|
|
18
24
|
export type { Change, Constraint } from './generator/diff.ts';
|
|
19
25
|
export { generateMigration } from './generator/generate.ts';
|
|
20
26
|
export type { GenerateResult } from './generator/generate.ts';
|
|
21
|
-
export { renderChange, renderChanges, renderStatements } from './generator/render.ts';
|
|
27
|
+
export { renderChange, renderChanges, renderConcurrentStatements, renderStatements } from './generator/render.ts';
|
|
22
28
|
export { buildSnapshot, EMPTY_SNAPSHOT, SNAPSHOT_VERSION } from './generator/snapshot.ts';
|
|
23
29
|
export type { ColumnSnapshot, Snapshot, TableSnapshot } from './generator/snapshot.ts';
|
|
24
|
-
export {
|
|
30
|
+
export type { inferKyselyDatabase, inferKyselyTable } from './kysely/infer.ts';
|
|
31
|
+
export { jsonb, jsonbArray } from './kysely/json.ts';
|
|
32
|
+
export type { Jsonb } from './kysely/json.ts';
|
|
33
|
+
export { CONCURRENTLY_SUFFIX, listMigrations, MIGRATION_EXTENSION, migrationTimestamp, NO_TRANSACTION_MARKER, readLatestSnapshot, readMigration, readStatements, SNAPSHOT_FILE, STATEMENT_SEPARATOR, writeMigration, } from './migrator/store.ts';
|
|
34
|
+
export type { MigrationFile } from './migrator/store.ts';
|
|
25
35
|
export { toCamelCase, toSnakeCase } from './table/casing.ts';
|
|
26
36
|
export type { CamelCase, SnakeCase } from './table/casing.ts';
|
|
27
37
|
export { ColumnBuilder, columnBuilders } from './table/columns.ts';
|
package/dist/index.js
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* kysely-ddl: PostgreSQL schema as code
|
|
3
|
-
*
|
|
2
|
+
* kysely-ddl: PostgreSQL schema as code, SQL migration generation and the
|
|
3
|
+
* Kysely side of it, row types and jsonb values. Imports `kysely`, the peer
|
|
4
|
+
* dependency, and no driver.
|
|
4
5
|
*
|
|
5
6
|
* Layers and dependency direction:
|
|
6
7
|
*
|
|
7
|
-
* table ◄── generator ◄── migrator ◄──
|
|
8
|
+
* table ◄── generator ◄── migrator/store ◄── migrator (runner, provider)
|
|
9
|
+
* table ◄── kysely (row types, jsonb values)
|
|
8
10
|
*
|
|
9
11
|
* Entry points:
|
|
10
12
|
*
|
|
11
|
-
* kysely-ddl
|
|
12
|
-
*
|
|
13
|
+
* kysely-ddl — this file: tables, generation, migration files on disk,
|
|
14
|
+
* `inferKyselyTable` / `inferKyselyDatabase`, `jsonb` / `jsonbArray`
|
|
15
|
+
* kysely-ddl/migrator — running migrations: `createMigrator` / `migrateToLatest`
|
|
16
|
+
* and `sqlFileMigrationProvider`; separate, so that a project
|
|
17
|
+
* which applies migrations some other way does not pull it in
|
|
13
18
|
*
|
|
14
19
|
* Exports are sorted by module path: generator (snapshot, diff, render,
|
|
15
|
-
* facade), migrator (files on disk), table
|
|
20
|
+
* facade), kysely (row types, jsonb values), migrator (files on disk), table
|
|
21
|
+
* (table definitions).
|
|
16
22
|
*/
|
|
17
23
|
export { diffSnapshots } from './generator/diff.js';
|
|
18
24
|
export { generateMigration } from './generator/generate.js';
|
|
19
|
-
export { renderChange, renderChanges, renderStatements } from './generator/render.js';
|
|
25
|
+
export { renderChange, renderChanges, renderConcurrentStatements, renderStatements } from './generator/render.js';
|
|
20
26
|
export { buildSnapshot, EMPTY_SNAPSHOT, SNAPSHOT_VERSION } from './generator/snapshot.js';
|
|
21
|
-
export {
|
|
27
|
+
export { jsonb, jsonbArray } from './kysely/json.js';
|
|
28
|
+
export { CONCURRENTLY_SUFFIX, listMigrations, MIGRATION_EXTENSION, migrationTimestamp, NO_TRANSACTION_MARKER, readLatestSnapshot, readMigration, readStatements, SNAPSHOT_FILE, STATEMENT_SEPARATOR, writeMigration, } from './migrator/store.js';
|
|
22
29
|
export { toCamelCase, toSnakeCase } from './table/casing.js';
|
|
23
30
|
export { ColumnBuilder, columnBuilders } from './table/columns.js';
|
|
24
31
|
export { AUTO_NAMES, defineTable, ref } from './table/define.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running `.sql` migrations. Entry point `kysely-ddl/migrator`, kept apart from
|
|
3
|
+
* the root one so that a project which applies its migrations some other way
|
|
4
|
+
* (psql, a CI job, a runner of its own) does not pull this in.
|
|
5
|
+
*
|
|
6
|
+
* Two ways to run: `createMigrator` / `migrateToLatest`, a runner on top of a
|
|
7
|
+
* ready `Kysely`, and `sqlFileMigrationProvider`, which feeds the same files to
|
|
8
|
+
* Kysely's own `Migrator`. Both read what `writeMigration` writes; the file
|
|
9
|
+
* format itself (`store.ts` next door) is exported from the root entry point,
|
|
10
|
+
* since generating a migration needs it and running one is optional.
|
|
11
|
+
*/
|
|
12
|
+
export { sqlFileMigrationProvider } from './provider.ts';
|
|
13
|
+
export type { SqlMigrationProviderOptions } from './provider.ts';
|
|
14
|
+
export { createMigrator, DEFAULT_JOURNAL_TABLE, migrateToLatest, MIGRATION_LOCK_ID, MigrationError, } from './runner.ts';
|
|
15
|
+
export type { MigrationRunResult, MigrationStatus, MigratorOptions, SqlMigrator, TransactionMode } from './runner.ts';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running `.sql` migrations. Entry point `kysely-ddl/migrator`, kept apart from
|
|
3
|
+
* the root one so that a project which applies its migrations some other way
|
|
4
|
+
* (psql, a CI job, a runner of its own) does not pull this in.
|
|
5
|
+
*
|
|
6
|
+
* Two ways to run: `createMigrator` / `migrateToLatest`, a runner on top of a
|
|
7
|
+
* ready `Kysely`, and `sqlFileMigrationProvider`, which feeds the same files to
|
|
8
|
+
* Kysely's own `Migrator`. Both read what `writeMigration` writes; the file
|
|
9
|
+
* format itself (`store.ts` next door) is exported from the root entry point,
|
|
10
|
+
* since generating a migration needs it and running one is optional.
|
|
11
|
+
*/
|
|
12
|
+
export { sqlFileMigrationProvider } from './provider.js';
|
|
13
|
+
export { createMigrator, DEFAULT_JOURNAL_TABLE, migrateToLatest, MIGRATION_LOCK_ID, MigrationError, } from './runner.js';
|
|
@@ -21,13 +21,16 @@
|
|
|
21
21
|
*
|
|
22
22
|
* On transactions: on postgres Kysely runs the WHOLE run in one transaction by
|
|
23
23
|
* default, DDL is transactional there, so a failing migration rolls back all the
|
|
24
|
-
* previous ones from the same run.
|
|
25
|
-
* `
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* previous ones from the same run. A migration with `--> no-transaction` in its
|
|
25
|
+
* header gets `config: { transaction: false }`, which Kysely 0.30+ honors under
|
|
26
|
+
* `new Migrator({ ..., transactionMode: 'per-migration' })`: every migration in
|
|
27
|
+
* its own transaction, the marked one without. Older Kysely ignores `config`, so
|
|
28
|
+
* the marked migration checks that it is not inside a transaction and fails with
|
|
29
|
+
* a clear error instead of a postgres one; there the only way out is
|
|
30
|
+
* `disableTransactions: true` for the whole run.
|
|
28
31
|
*/
|
|
29
32
|
import { sql } from 'kysely';
|
|
30
|
-
import { listMigrations,
|
|
33
|
+
import { listMigrations, NO_TRANSACTION_MARKER, readMigration } from './store.js';
|
|
31
34
|
/**
|
|
32
35
|
* A provider that hands `.sql` files from a folder to the `Migrator`.
|
|
33
36
|
* The migration name is the file name without the extension, the order is
|
|
@@ -39,14 +42,22 @@ export function sqlFileMigrationProvider(dir, options = {}) {
|
|
|
39
42
|
async getMigrations() {
|
|
40
43
|
const migrations = {};
|
|
41
44
|
for (const name of listMigrations(dir)) {
|
|
42
|
-
|
|
45
|
+
const file = readMigration(dir, name);
|
|
46
|
+
const migration = {
|
|
43
47
|
async up(db) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
if (!file.transaction && db.isTransaction) {
|
|
49
|
+
throw new Error(`${name} is marked "${NO_TRANSACTION_MARKER}" but the Migrator runs it inside a transaction. ` +
|
|
50
|
+
"Kysely 0.30+: new Migrator({ ..., transactionMode: 'per-migration' }); older Kysely: " +
|
|
51
|
+
"disableTransactions: true; or kysely-ddl's createMigrator({ ..., transaction: 'each' }).");
|
|
52
|
+
}
|
|
53
|
+
// chunk by chunk: a plain file is one chunk, a file with
|
|
54
|
+
// `--> statement-breakpoint` several; CONCURRENTLY statements must travel alone
|
|
55
|
+
for (const statement of file.statements) {
|
|
47
56
|
await sql.raw(statement).execute(db);
|
|
48
57
|
}
|
|
49
58
|
},
|
|
59
|
+
// only when marked: in Kysely's other transaction modes an explicit value is an error
|
|
60
|
+
...(file.transaction ? {} : { config: { transaction: false } }),
|
|
50
61
|
...(onDown === 'throw'
|
|
51
62
|
? {
|
|
52
63
|
down() {
|
|
@@ -56,6 +67,7 @@ export function sqlFileMigrationProvider(dir, options = {}) {
|
|
|
56
67
|
}
|
|
57
68
|
: {}),
|
|
58
69
|
};
|
|
70
|
+
migrations[name] = migration;
|
|
59
71
|
}
|
|
60
72
|
return migrations;
|
|
61
73
|
},
|
|
@@ -34,10 +34,13 @@ export interface MigratorOptions {
|
|
|
34
34
|
/** The journal table. `kysely_migration` by default, like Kysely. */
|
|
35
35
|
readonly journalTable?: string;
|
|
36
36
|
/**
|
|
37
|
-
* `
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
37
|
+
* `each` (default): a transaction per migration; a migration with
|
|
38
|
+
* `--> no-transaction` in its header, which is how the generator writes
|
|
39
|
+
* `CREATE INDEX CONCURRENTLY`, runs without one. `all`: the whole run in one
|
|
40
|
+
* transaction, like Kysely, so a failing migration rolls back the previous ones
|
|
41
|
+
* from the same run; the marker is an error here, one shared transaction cannot
|
|
42
|
+
* leave a migration out. `none`: no `BEGIN` at all; a plain file still runs as
|
|
43
|
+
* one implicit transaction, since postgres treats a multi-statement query that way.
|
|
41
44
|
*/
|
|
42
45
|
readonly transaction?: TransactionMode;
|
|
43
46
|
/**
|
|
@@ -60,14 +63,23 @@ export interface SqlMigrator {
|
|
|
60
63
|
/** Applies everything pending. A failing statement throws `MigrationError`. */
|
|
61
64
|
toLatest(): Promise<MigrationRunResult>;
|
|
62
65
|
}
|
|
63
|
-
/** A failing
|
|
66
|
+
/** A failing query: which migration, which text and what was applied before it. */
|
|
64
67
|
export declare class MigrationError extends Error {
|
|
65
68
|
readonly migration: string;
|
|
69
|
+
/** The text sent as one query: a whole file, or one chunk between `--> statement-breakpoint` markers. */
|
|
66
70
|
readonly statement: string;
|
|
67
71
|
/** Migrations applied by this run before the error. With `transaction: 'all'` they are rolled back. */
|
|
68
72
|
readonly applied: readonly string[];
|
|
69
73
|
readonly name = "MigrationError";
|
|
70
|
-
|
|
74
|
+
/**
|
|
75
|
+
* The line inside `statement` postgres pointed at, when it did. A generated
|
|
76
|
+
* migration is one query with several statements, so this is what locates
|
|
77
|
+
* the failing one.
|
|
78
|
+
*/
|
|
79
|
+
readonly line: number | undefined;
|
|
80
|
+
constructor(migration: string,
|
|
81
|
+
/** The text sent as one query: a whole file, or one chunk between `--> statement-breakpoint` markers. */
|
|
82
|
+
statement: string,
|
|
71
83
|
/** Migrations applied by this run before the error. With `transaction: 'all'` they are rolled back. */
|
|
72
84
|
applied: readonly string[], cause: unknown);
|
|
73
85
|
}
|
|
@@ -18,31 +18,49 @@
|
|
|
18
18
|
* happy with `Migrator` takes `sqlFileMigrationProvider`.
|
|
19
19
|
*/
|
|
20
20
|
import { sql } from 'kysely';
|
|
21
|
-
import { listMigrations,
|
|
21
|
+
import { listMigrations, NO_TRANSACTION_MARKER, readMigration } from './store.js';
|
|
22
22
|
/** Same as Kysely, so both runners see one state. */
|
|
23
23
|
export const DEFAULT_JOURNAL_TABLE = 'kysely_migration';
|
|
24
24
|
/** The `pg_advisory_lock` key, the same as Kysely's `PostgresAdapter` uses. */
|
|
25
25
|
export const MIGRATION_LOCK_ID = 3853314791062309107n;
|
|
26
26
|
/** How long to wait for another run. An hour, like Kysely. */
|
|
27
27
|
const LOCK_TIMEOUT_MS = 60 * 60 * 1000;
|
|
28
|
-
/** A failing
|
|
28
|
+
/** A failing query: which migration, which text and what was applied before it. */
|
|
29
29
|
export class MigrationError extends Error {
|
|
30
30
|
migration;
|
|
31
31
|
statement;
|
|
32
32
|
applied;
|
|
33
33
|
name = 'MigrationError';
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* The line inside `statement` postgres pointed at, when it did. A generated
|
|
36
|
+
* migration is one query with several statements, so this is what locates
|
|
37
|
+
* the failing one.
|
|
38
|
+
*/
|
|
39
|
+
line;
|
|
40
|
+
constructor(migration,
|
|
41
|
+
/** The text sent as one query: a whole file, or one chunk between `--> statement-breakpoint` markers. */
|
|
42
|
+
statement,
|
|
35
43
|
/** Migrations applied by this run before the error. With `transaction: 'all'` they are rolled back. */
|
|
36
44
|
applied, cause) {
|
|
37
|
-
|
|
45
|
+
const line = lineOf(statement, cause);
|
|
46
|
+
super(`${migration}: statement failed${line === undefined ? '' : ` at line ${line}`}\n${statement}\n${describe(cause)}`, { cause });
|
|
38
47
|
this.migration = migration;
|
|
39
48
|
this.statement = statement;
|
|
40
49
|
this.applied = applied;
|
|
50
|
+
this.line = line;
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
53
|
function describe(cause) {
|
|
44
54
|
return cause instanceof Error ? cause.message : String(cause);
|
|
45
55
|
}
|
|
56
|
+
/** Postgres reports the error position as a 1-based character offset into the query; both drivers pass it on. */
|
|
57
|
+
function lineOf(statement, cause) {
|
|
58
|
+
const position = Number(cause?.position);
|
|
59
|
+
if (!Number.isInteger(position) || position < 1) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
return statement.slice(0, position - 1).split('\n').length;
|
|
63
|
+
}
|
|
46
64
|
export function createMigrator(options) {
|
|
47
65
|
const { db, migrationsDir: dir } = options;
|
|
48
66
|
if (db.isTransaction) {
|
|
@@ -53,7 +71,7 @@ export function createMigrator(options) {
|
|
|
53
71
|
throw new Error(`journalTable: "${journal}" may only contain letters, digits and underscores`);
|
|
54
72
|
}
|
|
55
73
|
const table = sql.table(journal);
|
|
56
|
-
const mode = options.transaction ?? '
|
|
74
|
+
const mode = options.transaction ?? 'each';
|
|
57
75
|
const allowUnordered = options.allowUnordered ?? false;
|
|
58
76
|
return {
|
|
59
77
|
status: () => db.connection().execute(async (connection) => {
|
|
@@ -122,8 +140,17 @@ async function apply(connection, table, dir, pending, mode) {
|
|
|
122
140
|
if (pending.length === 0) {
|
|
123
141
|
return { applied };
|
|
124
142
|
}
|
|
125
|
-
|
|
126
|
-
|
|
143
|
+
// parse everything first: a malformed file, or a marker the mode cannot honor,
|
|
144
|
+
// must fail before the first statement reaches the database
|
|
145
|
+
const migrations = pending.map(name => ({ name, ...readMigration(dir, name) }));
|
|
146
|
+
const marked = migrations.find(migration => !migration.transaction);
|
|
147
|
+
// under 'none' the marker asks for what already happens; under 'all' it cannot be honored
|
|
148
|
+
if (marked !== undefined && mode === 'all') {
|
|
149
|
+
throw new Error(`${dir}: ${marked.name} is marked "${NO_TRANSACTION_MARKER}", but transaction: 'all' runs the whole run ` +
|
|
150
|
+
"in one transaction and cannot leave a migration out. Use transaction: 'each'.");
|
|
151
|
+
}
|
|
152
|
+
const runOne = async ({ name, statements }) => {
|
|
153
|
+
for (const statement of statements) {
|
|
127
154
|
try {
|
|
128
155
|
await run(connection, statement);
|
|
129
156
|
}
|
|
@@ -149,19 +176,25 @@ async function apply(connection, table, dir, pending, mode) {
|
|
|
149
176
|
switch (mode) {
|
|
150
177
|
case 'all':
|
|
151
178
|
await inTransaction(async () => {
|
|
152
|
-
for (const
|
|
153
|
-
await runOne(
|
|
179
|
+
for (const migration of migrations) {
|
|
180
|
+
await runOne(migration);
|
|
154
181
|
}
|
|
155
182
|
});
|
|
156
183
|
break;
|
|
157
184
|
case 'each':
|
|
158
|
-
for (const
|
|
159
|
-
|
|
185
|
+
for (const migration of migrations) {
|
|
186
|
+
// a marked migration runs in autocommit, its journal row too, like Kysely's 'per-migration' mode
|
|
187
|
+
if (migration.transaction) {
|
|
188
|
+
await inTransaction(() => runOne(migration));
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
await runOne(migration);
|
|
192
|
+
}
|
|
160
193
|
}
|
|
161
194
|
break;
|
|
162
195
|
case 'none':
|
|
163
|
-
for (const
|
|
164
|
-
await runOne(
|
|
196
|
+
for (const migration of migrations) {
|
|
197
|
+
await runOne(migration);
|
|
165
198
|
}
|
|
166
199
|
break;
|
|
167
200
|
}
|
package/dist/migrator/store.d.ts
CHANGED
|
@@ -7,11 +7,43 @@ export declare const SNAPSHOT_FILE = "snapshot.json";
|
|
|
7
7
|
* stays valid when fed to psql as a whole.
|
|
8
8
|
*/
|
|
9
9
|
export declare const STATEMENT_SEPARATOR = "--> statement-breakpoint";
|
|
10
|
+
/**
|
|
11
|
+
* A header marker: the migration runs outside a transaction, which postgres
|
|
12
|
+
* demands for `CREATE INDEX CONCURRENTLY` and the like. Recognized only in the
|
|
13
|
+
* header, that is, among the blank lines and comments before the first statement.
|
|
14
|
+
*
|
|
15
|
+
* The runner honors it under `transaction: 'each'`. For Kysely's `Migrator` the
|
|
16
|
+
* provider turns it into `config: { transaction: false }`, which Kysely 0.30+
|
|
17
|
+
* honors under `transactionMode: 'per-migration'`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const NO_TRANSACTION_MARKER = "--> no-transaction";
|
|
10
20
|
export declare function migrationTimestamp(now?: Date): string;
|
|
11
21
|
/** Migration names (without `.sql`), sorted the way Kysely sorts them: by character codes. */
|
|
12
22
|
export declare function listMigrations(dir: string): string[];
|
|
13
23
|
/** The schema state after the latest migration; the next diff is computed from it. */
|
|
14
24
|
export declare function readLatestSnapshot(dir: string): Snapshot;
|
|
25
|
+
/** A parsed migration file. */
|
|
26
|
+
export interface MigrationFile {
|
|
27
|
+
/** The statements, split on `STATEMENT_SEPARATOR`, with the header markers taken out. */
|
|
28
|
+
readonly statements: readonly string[];
|
|
29
|
+
/** `false` when the header carries `NO_TRANSACTION_MARKER`. */
|
|
30
|
+
readonly transaction: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** The statements of a migration and what its header markers said. */
|
|
33
|
+
export declare function readMigration(dir: string, name: string): MigrationFile;
|
|
34
|
+
/** The statements only; `readMigration` also tells whether the migration wants a transaction. */
|
|
15
35
|
export declare function readStatements(dir: string, name: string): string[];
|
|
16
|
-
/**
|
|
17
|
-
export declare
|
|
36
|
+
/** The suffix of the migration that holds the `CONCURRENTLY` statements. */
|
|
37
|
+
export declare const CONCURRENTLY_SUFFIX = "_concurrently";
|
|
38
|
+
/**
|
|
39
|
+
* Writes the migration files, updates the snapshot and returns the names in
|
|
40
|
+
* application order.
|
|
41
|
+
*
|
|
42
|
+
* The ordinary statements go into `<stamp>_<name>.sql` as plain SQL: postgres
|
|
43
|
+
* runs such a file as one query. The `CONCURRENTLY` statements, which postgres
|
|
44
|
+
* refuses inside a transaction and inside a multi-statement query alike, go into
|
|
45
|
+
* `<stamp+1>_<name>_concurrently.sql`, marked `--> no-transaction` and split by
|
|
46
|
+
* `--> statement-breakpoint` so that the runner sends them one at a time.
|
|
47
|
+
* Either file is skipped when it would be empty.
|
|
48
|
+
*/
|
|
49
|
+
export declare function writeMigration(dir: string, name: string, result: GenerateResult): string[];
|
package/dist/migrator/store.js
CHANGED
|
@@ -17,17 +17,34 @@
|
|
|
17
17
|
* which is all that is needed to compute the next diff. The price is merge
|
|
18
18
|
* conflicts: two branches that each add a migration diverge in one file, and
|
|
19
19
|
* the snapshot has to be regenerated after the merge.
|
|
20
|
+
*
|
|
21
|
+
* Inside a file, `-->` comments are markers for the runner; postgres sees plain
|
|
22
|
+
* comments. `--> statement-breakpoint` separates statements, and
|
|
23
|
+
* `--> no-transaction` in the header says the migration runs outside a transaction.
|
|
20
24
|
*/
|
|
21
25
|
import fs from 'node:fs';
|
|
22
26
|
import path from 'node:path';
|
|
23
27
|
import { EMPTY_SNAPSHOT } from '../generator/snapshot.js';
|
|
24
28
|
export const MIGRATION_EXTENSION = '.sql';
|
|
25
29
|
export const SNAPSHOT_FILE = 'snapshot.json';
|
|
30
|
+
/** Machine-readable comments start with this. */
|
|
31
|
+
const MARKER = '-->';
|
|
32
|
+
const NO_TRANSACTION = 'no-transaction';
|
|
26
33
|
/**
|
|
27
34
|
* Separator between statements inside a file. It is an SQL comment, so the file
|
|
28
35
|
* stays valid when fed to psql as a whole.
|
|
29
36
|
*/
|
|
30
37
|
export const STATEMENT_SEPARATOR = '--> statement-breakpoint';
|
|
38
|
+
/**
|
|
39
|
+
* A header marker: the migration runs outside a transaction, which postgres
|
|
40
|
+
* demands for `CREATE INDEX CONCURRENTLY` and the like. Recognized only in the
|
|
41
|
+
* header, that is, among the blank lines and comments before the first statement.
|
|
42
|
+
*
|
|
43
|
+
* The runner honors it under `transaction: 'each'`. For Kysely's `Migrator` the
|
|
44
|
+
* provider turns it into `config: { transaction: false }`, which Kysely 0.30+
|
|
45
|
+
* honors under `transactionMode: 'per-migration'`.
|
|
46
|
+
*/
|
|
47
|
+
export const NO_TRANSACTION_MARKER = `${MARKER} ${NO_TRANSACTION}`;
|
|
31
48
|
/** Parses the file name prefix back into a date. */
|
|
32
49
|
function parseTimestamp(value) {
|
|
33
50
|
return new Date(Date.UTC(Number(value.slice(0, 4)), Number(value.slice(4, 6)) - 1, Number(value.slice(6, 8)), Number(value.slice(8, 10)), Number(value.slice(10, 12)), Number(value.slice(12, 14))));
|
|
@@ -61,36 +78,104 @@ export function readLatestSnapshot(dir) {
|
|
|
61
78
|
}
|
|
62
79
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
63
80
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return
|
|
81
|
+
/** The word after `-->` on a marker line, undefined for any other line. */
|
|
82
|
+
function markerOf(line) {
|
|
83
|
+
return line.startsWith(MARKER) ? line.slice(MARKER.length).trim() : undefined;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The header is everything before the first statement: blank lines, plain `--`
|
|
87
|
+
* comments and markers. Markers are taken out, the rest goes to postgres as is.
|
|
88
|
+
* A marker below the header would reach postgres as a comment and change
|
|
89
|
+
* nothing, so it is an error rather than a silent no-op.
|
|
90
|
+
*/
|
|
91
|
+
function parseMigration(text, name) {
|
|
92
|
+
const kept = [];
|
|
93
|
+
let transaction = true;
|
|
94
|
+
let inHeader = true;
|
|
95
|
+
for (const raw of text.split('\n')) {
|
|
96
|
+
const line = raw.trim();
|
|
97
|
+
const marker = markerOf(line);
|
|
98
|
+
if (inHeader && marker !== undefined && line !== STATEMENT_SEPARATOR) {
|
|
99
|
+
if (marker !== NO_TRANSACTION) {
|
|
100
|
+
throw new Error(`${name}: unknown marker "${line}" in the header. The only header marker is "${NO_TRANSACTION_MARKER}".`);
|
|
101
|
+
}
|
|
102
|
+
transaction = false;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// the first statement, or a separator, ends the header
|
|
106
|
+
if (inHeader && line !== '' && (marker !== undefined || !line.startsWith('--'))) {
|
|
107
|
+
inHeader = false;
|
|
108
|
+
}
|
|
109
|
+
if (!inHeader && marker === NO_TRANSACTION) {
|
|
110
|
+
throw new Error(`${name}: "${NO_TRANSACTION_MARKER}" must be in the header, before the first statement.`);
|
|
111
|
+
}
|
|
112
|
+
kept.push(raw);
|
|
113
|
+
}
|
|
114
|
+
const statements = kept
|
|
115
|
+
.join('\n')
|
|
67
116
|
.split(STATEMENT_SEPARATOR)
|
|
68
117
|
.map(statement => statement.trim())
|
|
69
118
|
.filter(statement => statement !== '');
|
|
119
|
+
return { statements, transaction };
|
|
120
|
+
}
|
|
121
|
+
/** The statements of a migration and what its header markers said. */
|
|
122
|
+
export function readMigration(dir, name) {
|
|
123
|
+
return parseMigration(fs.readFileSync(path.join(dir, `${name}${MIGRATION_EXTENSION}`), 'utf8'), name);
|
|
124
|
+
}
|
|
125
|
+
/** The statements only; `readMigration` also tells whether the migration wants a transaction. */
|
|
126
|
+
export function readStatements(dir, name) {
|
|
127
|
+
return [...readMigration(dir, name).statements];
|
|
70
128
|
}
|
|
71
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* Names must grow monotonically: Kysely applies migrations in alphabetical
|
|
131
|
+
* order. Two migrations created within the same second would get the same
|
|
132
|
+
* prefix, and the suffix would decide the order, that is, by luck.
|
|
133
|
+
*/
|
|
134
|
+
function nextTimestamp(dir) {
|
|
135
|
+
const previous = listMigrations(dir).at(-1);
|
|
136
|
+
const stamp = migrationTimestamp();
|
|
137
|
+
if (previous !== undefined && stamp <= previous.slice(0, 14)) {
|
|
138
|
+
return migrationTimestamp(new Date(parseTimestamp(previous.slice(0, 14)).getTime() + 1000));
|
|
139
|
+
}
|
|
140
|
+
return stamp;
|
|
141
|
+
}
|
|
142
|
+
/** The suffix of the migration that holds the `CONCURRENTLY` statements. */
|
|
143
|
+
export const CONCURRENTLY_SUFFIX = '_concurrently';
|
|
144
|
+
/**
|
|
145
|
+
* Writes the migration files, updates the snapshot and returns the names in
|
|
146
|
+
* application order.
|
|
147
|
+
*
|
|
148
|
+
* The ordinary statements go into `<stamp>_<name>.sql` as plain SQL: postgres
|
|
149
|
+
* runs such a file as one query. The `CONCURRENTLY` statements, which postgres
|
|
150
|
+
* refuses inside a transaction and inside a multi-statement query alike, go into
|
|
151
|
+
* `<stamp+1>_<name>_concurrently.sql`, marked `--> no-transaction` and split by
|
|
152
|
+
* `--> statement-breakpoint` so that the runner sends them one at a time.
|
|
153
|
+
* Either file is skipped when it would be empty.
|
|
154
|
+
*/
|
|
72
155
|
export function writeMigration(dir, name, result) {
|
|
73
|
-
if (result.statements.length === 0) {
|
|
156
|
+
if (result.statements.length === 0 && result.concurrently.length === 0) {
|
|
74
157
|
throw new Error('nothing to write: no changes');
|
|
75
158
|
}
|
|
76
159
|
if (!/^[a-z0-9_]+$/.test(name)) {
|
|
77
160
|
throw new Error(`migration name "${name}": only [a-z0-9_] is allowed`);
|
|
78
161
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
162
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
163
|
+
const written = [];
|
|
164
|
+
const write = (suffix, text) => {
|
|
165
|
+
const migration = `${nextTimestamp(dir)}_${name}${suffix}`;
|
|
166
|
+
const file = path.join(dir, `${migration}${MIGRATION_EXTENSION}`);
|
|
167
|
+
if (fs.existsSync(file)) {
|
|
168
|
+
throw new Error(`migration ${migration} already exists`);
|
|
169
|
+
}
|
|
170
|
+
fs.writeFileSync(file, text);
|
|
171
|
+
written.push(migration);
|
|
172
|
+
};
|
|
173
|
+
if (result.statements.length > 0) {
|
|
174
|
+
write('', result.sql);
|
|
86
175
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (fs.existsSync(file)) {
|
|
90
|
-
throw new Error(`migration ${migration} already exists`);
|
|
176
|
+
if (result.concurrently.length > 0) {
|
|
177
|
+
write(CONCURRENTLY_SUFFIX, `${NO_TRANSACTION_MARKER}\n${result.concurrently.join(`\n${STATEMENT_SEPARATOR}\n`)}\n`);
|
|
91
178
|
}
|
|
92
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
93
|
-
fs.writeFileSync(file, `${result.statements.join(`\n${STATEMENT_SEPARATOR}\n`)}\n`);
|
|
94
179
|
fs.writeFileSync(path.join(dir, SNAPSHOT_FILE), `${JSON.stringify(result.snapshot, null, 2)}\n`);
|
|
95
|
-
return
|
|
180
|
+
return written;
|
|
96
181
|
}
|
package/dist/table/columns.d.ts
CHANGED
|
@@ -168,7 +168,7 @@ declare function timestamp<N extends string>(name: N, config?: TimestampConfig):
|
|
|
168
168
|
declare function timestamp(config?: TimestampConfig): Fresh<undefined, Date>;
|
|
169
169
|
/**
|
|
170
170
|
* `unknown` in TS, narrow it with `$type<T>()`. Values for writes go only
|
|
171
|
-
* through `jsonb()` from `kysely-ddl
|
|
171
|
+
* through `jsonb()` from `kysely-ddl`: drivers accept a raw jsonb
|
|
172
172
|
* parameter differently, and the helper evens that out.
|
|
173
173
|
*/
|
|
174
174
|
declare function jsonb<N extends string>(name: N): Fresh<N, unknown, undefined, true>;
|
package/dist/table/define.d.ts
CHANGED
|
@@ -69,6 +69,8 @@ export interface TableSpec {
|
|
|
69
69
|
readonly unique: boolean;
|
|
70
70
|
readonly columns: readonly string[];
|
|
71
71
|
readonly where: Sql | undefined;
|
|
72
|
+
/** Built and dropped with `CONCURRENTLY`, in a migration of its own. */
|
|
73
|
+
readonly concurrently: boolean;
|
|
72
74
|
}[];
|
|
73
75
|
readonly foreignKeys: readonly {
|
|
74
76
|
readonly name: string;
|
|
@@ -121,7 +123,7 @@ export interface ForeignKeyDef<TCols> {
|
|
|
121
123
|
export declare function ref<T extends AnyTable>(table: T, columns: readonly (keyof T['_']['columns'] & string)[]): Reference;
|
|
122
124
|
export interface TableOptions<TName extends string, TCols extends Record<string, AnyColumn>> {
|
|
123
125
|
/** The table name in the database. Inferred as a literal; the Kysely interface keys come from it. */
|
|
124
|
-
readonly
|
|
126
|
+
readonly name: TName;
|
|
125
127
|
/**
|
|
126
128
|
* Columns are declared with a callback that receives the builder set:
|
|
127
129
|
*
|
|
@@ -153,6 +155,14 @@ export interface TableOptions<TName extends string, TCols extends Record<string,
|
|
|
153
155
|
readonly unique?: boolean;
|
|
154
156
|
readonly columns: readonly (keyof TCols & string)[];
|
|
155
157
|
readonly where?: (c: Refs<TCols>) => Sql;
|
|
158
|
+
/**
|
|
159
|
+
* Build and drop the index with `CONCURRENTLY`, without locking the table
|
|
160
|
+
* against writes. Postgres refuses that inside a transaction, so the
|
|
161
|
+
* generator puts such statements into a separate migration marked
|
|
162
|
+
* `--> no-transaction`, which the runner applies under `transaction: 'each'`.
|
|
163
|
+
* Toggling the flag on an existing index changes nothing in the database.
|
|
164
|
+
*/
|
|
165
|
+
readonly concurrently?: boolean;
|
|
156
166
|
}[];
|
|
157
167
|
readonly foreignKeys?: readonly ForeignKeyDef<TCols>[];
|
|
158
168
|
readonly checks?: readonly {
|
package/dist/table/define.js
CHANGED
|
@@ -46,7 +46,7 @@ export function ref(table, columns) {
|
|
|
46
46
|
}
|
|
47
47
|
// ── defineTable ──────────────────────────────────────────────────────────────
|
|
48
48
|
export function defineTable(options) {
|
|
49
|
-
const name = options.
|
|
49
|
+
const name = options.name;
|
|
50
50
|
assertIdentifier(name, 'table');
|
|
51
51
|
const columns = [];
|
|
52
52
|
const dbName = {};
|
|
@@ -93,6 +93,7 @@ export function defineTable(options) {
|
|
|
93
93
|
unique: index.unique ?? false,
|
|
94
94
|
columns: indexColumns,
|
|
95
95
|
where: index.where !== undefined ? index.where(typedRefs) : undefined,
|
|
96
|
+
concurrently: index.concurrently ?? false,
|
|
96
97
|
};
|
|
97
98
|
});
|
|
98
99
|
// ── primary key ────────────────────────────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kysely-ddl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "PostgreSQL schema as TypeScript code: SQL migrations from snapshot diffs, Kysely table types and a migration runner",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"postgres",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"types": "./dist/index.d.ts",
|
|
42
42
|
"default": "./dist/index.js"
|
|
43
43
|
},
|
|
44
|
-
"./
|
|
45
|
-
"types": "./dist/
|
|
46
|
-
"default": "./dist/
|
|
44
|
+
"./migrator": {
|
|
45
|
+
"types": "./dist/migrator/index.d.ts",
|
|
46
|
+
"default": "./dist/migrator/index.js"
|
|
47
47
|
},
|
|
48
48
|
"./package.json": "./package.json"
|
|
49
49
|
},
|
package/dist/kysely/index.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Everything tied to Kysely: row types, jsonb values, the migration runner on
|
|
3
|
-
* top of a ready `Kysely`, and the `.sql` file provider for the built-in `Migrator`.
|
|
4
|
-
* Entry point `kysely-ddl/kysely`.
|
|
5
|
-
*/
|
|
6
|
-
export type { inferKyselyDatabase, inferKyselyTable } from './infer.ts';
|
|
7
|
-
export { jsonb, jsonbArray } from './json.ts';
|
|
8
|
-
export type { Jsonb } from './json.ts';
|
|
9
|
-
export { sqlFileMigrationProvider } from './provider.ts';
|
|
10
|
-
export type { SqlMigrationProviderOptions } from './provider.ts';
|
|
11
|
-
export { createMigrator, DEFAULT_JOURNAL_TABLE, migrateToLatest, MIGRATION_LOCK_ID, MigrationError, } from './runner.ts';
|
|
12
|
-
export type { MigrationRunResult, MigrationStatus, MigratorOptions, SqlMigrator, TransactionMode } from './runner.ts';
|
package/dist/kysely/index.js
DELETED
|
File without changes
|