okmodel 0.2.1 → 0.4.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.
Files changed (148) hide show
  1. package/README.md +882 -48
  2. package/dist/contracts/catalog/document.d.ts +6 -2
  3. package/dist/contracts/catalog/enum.d.ts +31 -4
  4. package/dist/contracts/catalog/extension.d.ts +61 -0
  5. package/dist/contracts/catalog/privilege.d.ts +64 -0
  6. package/dist/contracts/catalog/routine.d.ts +56 -0
  7. package/dist/contracts/catalog/types.d.ts +164 -10
  8. package/dist/contracts/catalog/view.d.ts +73 -0
  9. package/dist/dialects/pg/column.d.ts +3 -1
  10. package/dist/dialects/pg/ddl.d.ts +65 -1
  11. package/dist/dialects/pg/enum.d.ts +10 -8
  12. package/dist/dialects/pg/ext/citext.d.ts +14 -0
  13. package/dist/dialects/pg/ext/citext.js +11 -0
  14. package/dist/dialects/pg/ext/index.d.ts +52 -0
  15. package/dist/dialects/pg/ext/index.js +6 -0
  16. package/dist/dialects/pg/ext/pg-trgm.d.ts +53 -0
  17. package/dist/dialects/pg/ext/pg-trgm.js +42 -0
  18. package/dist/dialects/pg/fn/index.d.ts +107 -0
  19. package/dist/dialects/pg/fn/index.js +175 -0
  20. package/dist/dialects/pg/fn/touch.d.ts +14 -0
  21. package/dist/dialects/pg/index.js +104 -19
  22. package/dist/dialects/pg/introspect.d.ts +10 -1
  23. package/dist/dialects/pg/operators.d.ts +8 -1
  24. package/dist/dialects/pg/role/check.d.ts +45 -0
  25. package/dist/dialects/pg/role/index.d.ts +50 -0
  26. package/dist/dialects/pg/role/sql.d.ts +51 -0
  27. package/dist/dialects/pg/table.d.ts +24 -1
  28. package/dist/dialects/pg/tenancy.d.ts +9 -0
  29. package/dist/dialects/pg/view/client.d.ts +30 -0
  30. package/dist/dialects/pg/view/depend.d.ts +35 -0
  31. package/dist/dialects/pg/view/index.d.ts +114 -0
  32. package/dist/dialects/pg/view/index.js +289 -0
  33. package/dist/dialects/pg/view/scratch.d.ts +22 -0
  34. package/dist/dialects/pg/where.d.ts +2 -2
  35. package/dist/runtime/catalog-cache.d.ts +17 -0
  36. package/dist/runtime/compat-words.d.ts +23 -0
  37. package/dist/runtime/drift-detail.d.ts +20 -4
  38. package/dist/runtime/drift.d.ts +4 -2
  39. package/dist/runtime/pg/bun.js +3 -3
  40. package/dist/runtime/pg/pg.js +3 -3
  41. package/dist/runtime/pg/pglite.js +3 -3
  42. package/dist/runtime/pg/postgresjs.js +3 -3
  43. package/dist/runtime/tenancy/index.js +6 -3
  44. package/dist/runtime/traits/index.d.ts +7 -2
  45. package/dist/runtime/traits/index.js +151 -13
  46. package/dist/runtime/types.d.ts +2 -0
  47. package/dist/runtime/validate/index.js +4 -4
  48. package/dist/shared/{xkz8g6by.js → 1g0drakv.js} +2 -2
  49. package/dist/shared/2321avmn.js +418 -0
  50. package/dist/shared/{7mht49yd.js → 3p4b1fg0.js} +57 -18
  51. package/dist/shared/41kv2xdm.js +318 -0
  52. package/dist/shared/{97g2jnrw.js → 4ef7mmqf.js} +3 -3
  53. package/dist/shared/{sacqha1y.js → 4m9fn52z.js} +2 -2
  54. package/dist/shared/5mtf6cj3.js +94 -0
  55. package/dist/shared/{1dp5knt7.js → 5vdm4sgp.js} +2 -2
  56. package/dist/shared/{a1gdp7j9.js → 6693j9rs.js} +1 -1
  57. package/dist/shared/{38crxbgn.js → 6p5k20sm.js} +3 -3
  58. package/dist/shared/7nk68fj8.js +751 -0
  59. package/dist/shared/84ptn35q.js +71 -0
  60. package/dist/shared/{d2whjz4k.js → 8jb4ymh2.js} +581 -28
  61. package/dist/shared/8ktwd3rh.js +97 -0
  62. package/dist/shared/94ez8kzh.js +19 -0
  63. package/dist/shared/{3a5ne750.js → 9m80mkac.js} +2 -2
  64. package/dist/shared/9x25fxhc.js +150 -0
  65. package/dist/shared/9yn6a2vd.js +11 -0
  66. package/dist/shared/ady1ms74.js +72 -0
  67. package/dist/shared/{4jzzghr6.js → awrsqrn7.js} +6 -6
  68. package/dist/shared/{4b9da9d7.js → axhk06q1.js} +19 -1
  69. package/dist/shared/{d04j53v8.js → c5g81ngb.js} +82 -14
  70. package/dist/shared/{n47rd821.js → c8aghh05.js} +1 -1
  71. package/dist/shared/{dd4f8mnx.js → c9z5g0fm.js} +4 -2
  72. package/dist/shared/{ps43134h.js → dyj86cmj.js} +4 -4
  73. package/dist/shared/{0qkxbm1f.js → fa9vt245.js} +1 -1
  74. package/dist/shared/{qa5dsc7z.js → fb00y8fp.js} +3 -3
  75. package/dist/shared/{qxa0yb9j.js → g7ez901n.js} +30 -1
  76. package/dist/shared/{py3nh7jy.js → gdh9mrtp.js} +2 -2
  77. package/dist/shared/{yw5c610b.js → gjthds9j.js} +2 -2
  78. package/dist/shared/hcmdtwgh.js +102 -0
  79. package/dist/shared/hqjsxnz7.js +13 -0
  80. package/dist/shared/htnaj77v.js +17 -0
  81. package/dist/shared/{v18jva5b.js → jgxzjzcz.js} +1 -1
  82. package/dist/shared/{x9v1qgeb.js → jsd07prz.js} +3 -3
  83. package/dist/shared/{g9f0zqce.js → kafaa5zv.js} +1 -1
  84. package/dist/shared/kj680nxv.js +19 -0
  85. package/dist/shared/{mxmcdsh5.js → kkzwp3d2.js} +9 -9
  86. package/dist/shared/{g1b5zzj9.js → n0dgs0pb.js} +1 -1
  87. package/dist/shared/n5y9zyj1.js +2431 -0
  88. package/dist/shared/{zt8y0wzb.js → n7mcc1gk.js} +1 -1
  89. package/dist/shared/{ge03bfca.js → nxb42q7h.js} +2 -2
  90. package/dist/shared/q6zsyh31.js +2192 -0
  91. package/dist/shared/{sfh4pk75.js → q795afwa.js} +26 -21
  92. package/dist/shared/{10tjenmd.js → qn0srwwq.js} +1 -1
  93. package/dist/shared/s4mb6a4b.js +545 -0
  94. package/dist/shared/{ytg3xf9p.js → sf8m6tfy.js} +3 -3
  95. package/dist/shared/{zd93g3rq.js → sr5ms7b3.js} +1 -1
  96. package/dist/shared/{xatfg0gn.js → tmbdywx0.js} +4 -19
  97. package/dist/shared/tny2t3sm.js +100 -0
  98. package/dist/shared/{e7a268fy.js → v7tpnhnz.js} +1 -1
  99. package/dist/shared/{ecdzej21.js → wqem4jed.js} +7 -7
  100. package/dist/shared/xghkt7a9.js +1144 -0
  101. package/dist/shared/y005b761.js +112 -0
  102. package/dist/shared/ycatkxmq.js +84 -0
  103. package/dist/shared/ygem90rn.js +900 -0
  104. package/dist/shared/{eaxwrgby.js → yjw02enp.js} +6 -3
  105. package/dist/shared/{mrq77sgw.js → zg24x0hm.js} +1 -1
  106. package/dist/shared/{cstgkzdx.js → zvbjc949.js} +1 -1
  107. package/dist/tooling/cli.js +66 -13
  108. package/dist/tooling/migrate/apply.d.ts +75 -5
  109. package/dist/tooling/migrate/backfill.d.ts +136 -0
  110. package/dist/tooling/migrate/check.d.ts +65 -0
  111. package/dist/tooling/migrate/classify.d.ts +108 -0
  112. package/dist/tooling/migrate/commands.d.ts +2 -1
  113. package/dist/tooling/migrate/config.d.ts +30 -0
  114. package/dist/tooling/migrate/doctor.d.ts +28 -0
  115. package/dist/tooling/migrate/drift.d.ts +35 -0
  116. package/dist/tooling/migrate/estimate.d.ts +50 -0
  117. package/dist/tooling/migrate/ext.d.ts +17 -0
  118. package/dist/tooling/migrate/extensions.d.ts +53 -0
  119. package/dist/tooling/migrate/files.d.ts +12 -0
  120. package/dist/tooling/migrate/index.d.ts +3 -2
  121. package/dist/tooling/migrate/index.js +67 -1
  122. package/dist/tooling/migrate/lint-rules.d.ts +51 -0
  123. package/dist/tooling/migrate/lint.d.ts +71 -0
  124. package/dist/tooling/migrate/managed.d.ts +17 -0
  125. package/dist/tooling/migrate/plan.d.ts +78 -10
  126. package/dist/tooling/migrate/project.d.ts +64 -5
  127. package/dist/tooling/migrate/provision.d.ts +18 -0
  128. package/dist/tooling/migrate/reference.d.ts +40 -0
  129. package/dist/tooling/migrate/runner.d.ts +108 -0
  130. package/dist/tooling/migrate/safe.d.ts +77 -0
  131. package/dist/tooling/migrate/seed.d.ts +20 -0
  132. package/dist/tooling/migrate/snapshot.d.ts +78 -0
  133. package/dist/tooling/migrate/status.d.ts +25 -6
  134. package/dist/tooling/migrate/type-name.d.ts +19 -0
  135. package/dist/tooling/testing/factories.d.ts +117 -0
  136. package/dist/tooling/testing/facts.d.ts +72 -0
  137. package/dist/tooling/testing/index.d.ts +89 -0
  138. package/dist/tooling/testing/index.js +8 -0
  139. package/dist/tooling/testing/isolation.d.ts +30 -0
  140. package/dist/tooling/testing/random.d.ts +40 -0
  141. package/dist/tooling/testing/record.d.ts +38 -0
  142. package/package.json +26 -2
  143. package/dist/shared/5z4q2zda.js +0 -2073
  144. package/dist/shared/eqmtn1bj.js +0 -399
  145. package/dist/shared/rtnaqre0.js +0 -37
  146. package/dist/shared/zwmqxb0g.js +0 -79
  147. package/dist/shared/{m9tv7xn7.js → radd3z4g.js} +3 -3
  148. /package/dist/shared/{9k6vetvf.js → rrstp3kb.js} +0 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `provision(target)` for one configured target (D198).
3
+ *
4
+ * Same rules as `okm migrate apply` on an empty target, including
5
+ * {@link assertTargetPolicy}. A target that is not empty is OKM1851.
6
+ * Tenant registry creation is M5.
7
+ */
8
+ /**
9
+ * Provisions one configured target from the head snapshot.
10
+ *
11
+ * The target must be empty. Reference rows are inserted if missing. History
12
+ * records `provisioned@<migration id>` and no migration step.
13
+ *
14
+ * @param target - Configured target name. `default` is the `database` target
15
+ * @param cwd - Project directory. Defaults to the process working directory
16
+ * @returns The same text `okm migrate apply` prints for that run
17
+ */
18
+ export declare function provision(target: string, cwd?: string): Promise<string>;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Reference rows declared on a table (D198).
3
+ *
4
+ * The rows live on the schema, not as a catalog diff. Apply inserts a row
5
+ * when its key is missing and does not update or delete. The statements are
6
+ * not migration steps, so OKM1542 does not see them.
7
+ */
8
+ import type { Catalog } from "../../contracts/catalog/types.js";
9
+ import { type AnyTable } from "../../dialects/pg/table.js";
10
+ /** One value a reference row may store. */
11
+ export type ReferenceValue = string | number | boolean | null;
12
+ /** One table's reference declaration, in SQL names. */
13
+ export type ReferenceTable = {
14
+ readonly table: string;
15
+ readonly key: readonly string[];
16
+ readonly rows: readonly (readonly (readonly [string, ReferenceValue])[])[];
17
+ };
18
+ /**
19
+ * Reads `reference` from the schema and checks it against the head catalog.
20
+ *
21
+ * A key must be a primary key, a unique constraint, or a unique index, in
22
+ * that column order. The catalog is not given a copy of the rows.
23
+ *
24
+ * @param tables - Tables the schema built
25
+ * @param casing - Schema casing. Absent means names are used as written
26
+ * @param head - Head snapshot the rows will be inserted into
27
+ * @returns One entry per table that declares rows
28
+ */
29
+ export declare function readReference(tables: readonly AnyTable[], casing: "snake" | undefined, head: Catalog): readonly ReferenceTable[];
30
+ /**
31
+ * Insert-if-missing statements for one schema.
32
+ *
33
+ * Each row is its own `INSERT … ON CONFLICT DO NOTHING`. There is no
34
+ * `UPDATE` and no `DELETE`.
35
+ *
36
+ * @param tables - Declarations from {@link readReference}
37
+ * @param schema - Schema the statements write
38
+ * @returns SQL in declaration order
39
+ */
40
+ export declare function referenceInserts(tables: readonly ReferenceTable[], schema: string): readonly string[];
@@ -0,0 +1,108 @@
1
+ /**
2
+ * One-target apply runner (D195).
3
+ *
4
+ * `okm migrate apply` calls {@link runTarget} with the units of one target.
5
+ * Bounded concurrency, several targets, canaries, and class flags are M5.
6
+ * A backfill step is one transaction per batch, with a checkpoint in
7
+ * `okm_backfill` after each commit.
8
+ */
9
+ import type { DriverConnection } from "../../contracts/driver.js";
10
+ import type { PlanStep } from "./plan.js";
11
+ /** One step with its index in the migration file. */
12
+ export type RunnableStep = {
13
+ readonly index: number;
14
+ readonly step: PlanStep;
15
+ };
16
+ /** Steps that commit together, or one step that cannot. */
17
+ export type RunnableUnit = {
18
+ readonly migrationId: string;
19
+ readonly catalogHash: string;
20
+ /** True when this unit is the last step of its migration. */
21
+ readonly finishes: boolean;
22
+ readonly transactional: boolean;
23
+ readonly steps: readonly RunnableStep[];
24
+ };
25
+ /** What one committed backfill batch recorded. */
26
+ export type BackfillBatch = {
27
+ readonly migrationId: string;
28
+ readonly stepIndex: number;
29
+ readonly batch: number;
30
+ readonly rows: number;
31
+ readonly lastKey: string | null;
32
+ };
33
+ /**
34
+ * Protection and timeouts the runner reads on every batch.
35
+ *
36
+ * `protected` is read again at each batch, so a caller can change it between
37
+ * batches.
38
+ */
39
+ export type RunPolicy = {
40
+ readonly target: string;
41
+ readonly protected: boolean;
42
+ readonly allowProtected?: boolean;
43
+ };
44
+ /** One target's units, retries, and checkpoints. */
45
+ export type TargetRun = {
46
+ readonly policy: RunPolicy;
47
+ readonly units: readonly RunnableUnit[];
48
+ readonly retries: number;
49
+ readonly lockTimeoutMs: number;
50
+ readonly statementTimeoutMs: number;
51
+ readonly backfillPauseMs?: number;
52
+ readonly backfillStatementTimeoutMs?: number;
53
+ readonly migrationRole?: string;
54
+ readonly schema?: string;
55
+ readonly onProgress?: (line: string) => void;
56
+ readonly afterBatch?: (batch: BackfillBatch) => void | Promise<void>;
57
+ };
58
+ /** What {@link runTarget} finished on this target. */
59
+ export type TargetRunReport = {
60
+ readonly target: string;
61
+ readonly applied: readonly string[];
62
+ readonly setRole?: string;
63
+ };
64
+ /**
65
+ * Runs one target's units in order.
66
+ *
67
+ * A finished step is skipped by the caller before it is placed in `units`.
68
+ * Lock timeout retries that step. A backfill retries the current batch only
69
+ * and resumes from `okm_backfill`.
70
+ *
71
+ * @param connection - Reserved connection. The caller holds the advisory lock
72
+ * @param run - Units, retries, and the protection flag
73
+ * @param session - Role assumed for the rest of the connection
74
+ * @returns The target and the migration ids that ran at least one unit
75
+ */
76
+ export declare function runTarget(connection: DriverConnection, run: TargetRun, session: {
77
+ setRole?: string;
78
+ }): Promise<TargetRunReport>;
79
+ /**
80
+ * Assumes `role` for the rest of this connection.
81
+ *
82
+ * A second call with the same role does nothing.
83
+ *
84
+ * @param connection - Reserved connection
85
+ * @param role - Migration role
86
+ * @param session - Remembers the role already assumed
87
+ */
88
+ export declare function assumeRole(connection: DriverConnection, role: string, session: {
89
+ setRole?: string;
90
+ }): Promise<void>;
91
+ /**
92
+ * Lets a new migration role create objects in `schema`.
93
+ *
94
+ * @param connection - Reserved connection, before `SET ROLE`
95
+ * @param schema - Schema the migration writes
96
+ * @param role - Role that was just created
97
+ */
98
+ export declare function grantSchema(connection: DriverConnection, schema: string, role: string): Promise<void>;
99
+ /**
100
+ * Reports whether Postgres refuses to run `sql` inside a transaction.
101
+ *
102
+ * `VACUUM`, `ALTER TYPE … ADD VALUE`, and concurrent index create or drop
103
+ * each need their own unit.
104
+ *
105
+ * @param sql - One statement
106
+ * @returns `true` when the statement cannot share a transaction
107
+ */
108
+ export declare function statementOutsideTransaction(sql: string): boolean;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Safe DDL for a table that already exists (D193).
3
+ *
4
+ * A new table keeps the plain statements from `ddl.ts`. These shapes are only
5
+ * for a table in the before-catalog: concurrent indexes, `NOT VALID` plus
6
+ * `VALIDATE`, `SET NOT NULL` through a validated check, a volatile default
7
+ * split from column creation, and a unique or primary key built from a
8
+ * concurrent unique index.
9
+ *
10
+ * The volatile-default fill is a backfill: one idempotent `UPDATE` per batch,
11
+ * limited to rows where the new column is still null (D195).
12
+ */
13
+ import type { Catalog, CatalogObject, ColumnObject } from "../../contracts/catalog/types.js";
14
+ import { type BackfillSpec } from "./backfill.js";
15
+ import type { StepKind } from "./classify.js";
16
+ /** One statement the planner emits instead of the plain form. */
17
+ export type SafeStep = {
18
+ readonly sql: string;
19
+ readonly kind: StepKind;
20
+ readonly action: "ddl" | "backfill";
21
+ readonly lock: string;
22
+ readonly transactional: boolean;
23
+ /** Set on the volatile-default fill. The statement is one batch. */
24
+ readonly backfill?: BackfillSpec;
25
+ };
26
+ /**
27
+ * Safe steps for one object on a table that already exists.
28
+ *
29
+ * Returns `undefined` when the plain statement is already safe: a stable or
30
+ * constant column default, or an object this rewrite does not cover.
31
+ *
32
+ * @param object - Object the plan is creating
33
+ * @param schema - Concrete schema name
34
+ * @param catalogs - Before and after catalogs, for volatility and name clashes
35
+ * @param batchSize - Rows per batch written on a volatile-default fill
36
+ * @returns The replacement steps, or `undefined` to keep the plain statement
37
+ */
38
+ export declare function stepsForExistingTable(object: CatalogObject, schema: string, catalogs: readonly Catalog[], batchSize: number): readonly SafeStep[] | undefined;
39
+ /**
40
+ * `SET NOT NULL` through a validated check (Postgres 12+, always on the floor of 15).
41
+ *
42
+ * Postgres skips the table scan when a validated `CHECK (col IS NOT NULL)`
43
+ * already exists. The temporary check is dropped after `SET NOT NULL`.
44
+ *
45
+ * @param column - Column that must become `NOT NULL`
46
+ * @param schema - Concrete schema name
47
+ * @param setKind - `set-not-null` when the column already existed; `add-column` when this finishes a new column and the plan stays expand
48
+ * @param catalogs - Catalogs whose constraint names the temporary check must not reuse
49
+ * @returns Add `NOT VALID`, validate, set not null, drop the check
50
+ */
51
+ export declare function notNullSteps(column: ColumnObject, schema: string, setKind: "set-not-null" | "add-column", catalogs: readonly Catalog[]): readonly SafeStep[];
52
+ /**
53
+ * Inserts `CONCURRENTLY` into a `CREATE INDEX` statement.
54
+ *
55
+ * @param sql - Plain `create index` or `create unique index` from `ddl.ts`
56
+ * @returns The same statement, concurrent
57
+ */
58
+ export declare function concurrentIndexSql(sql: string): string;
59
+ /**
60
+ * Inserts `CONCURRENTLY` into a `DROP INDEX` statement.
61
+ *
62
+ * @param sql - Plain `drop index` from `ddl.ts`
63
+ * @returns The same statement, concurrent
64
+ */
65
+ export declare function concurrentDropIndexSql(sql: string): string;
66
+ /**
67
+ * Reports whether a default expression is volatile.
68
+ *
69
+ * A constant has no call. `now()` and the other stable builtins stay plain.
70
+ * `gen_random_uuid()`, `uuidv7()`, and any other call the catalog marks
71
+ * volatile are split. A call the catalog does not know is left plain.
72
+ *
73
+ * @param expression - Column `defaultExpression`, when the column has one
74
+ * @param catalogs - Catalogs that may hold the called function
75
+ * @returns `true` when adding the column with this default would rewrite existing rows
76
+ */
77
+ export declare function volatileDefault(expression: string | undefined, catalogs: readonly Catalog[]): boolean;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `okm seed <file>` (spec §20, D199).
3
+ *
4
+ * The file's default export receives the testing harness for the selected
5
+ * target. Protection is `assertTargetPolicy`, the same gate as every other
6
+ * command. The target is already migrated: this command does not apply DDL.
7
+ */
8
+ import { type InvokeFlags } from "./policy.js";
9
+ /**
10
+ * Runs a seed file against the selected target.
11
+ *
12
+ * Policy and target selection run before the pool opens, so a protected
13
+ * target is refused without a connection.
14
+ *
15
+ * @param cwd - Project directory
16
+ * @param file - Seed module, relative to `cwd`
17
+ * @param flags - `--target`, `--allow-protected`, and `--allow-pooler`
18
+ * @returns Text for stdout
19
+ */
20
+ export declare function seedProject(cwd: string, file: string, flags: InvokeFlags): Promise<string>;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Head snapshot install and the comparison with a replayed history (D198).
3
+ *
4
+ * Provision plans the head catalog from an empty catalog and runs that SQL.
5
+ * It does not replay migration files. OKM1521 is the difference between that
6
+ * result and a full replay, after {@link canonicalTypeName} (D191).
7
+ */
8
+ import type { Catalog } from "../../contracts/catalog/types.js";
9
+ import type { DriverConnection } from "../../contracts/driver.js";
10
+ import type { ReferenceTable } from "./reference.js";
11
+ /** History id prefix for a target installed from the head snapshot. */
12
+ export declare const PROVISION_PREFIX = "provisioned@";
13
+ /**
14
+ * History id written when a target is provisioned at `migrationId`.
15
+ *
16
+ * @param migrationId - Head migration id
17
+ * @returns `provisioned@` plus that id
18
+ */
19
+ export declare function provisionMark(migrationId: string): string;
20
+ /**
21
+ * Migration id stored inside a provision mark.
22
+ *
23
+ * @param migrationId - A history migration id
24
+ * @returns The id after the prefix, or `undefined` when it is not a mark
25
+ */
26
+ export declare function provisionedMigrationId(migrationId: string): string | undefined;
27
+ /**
28
+ * Marks every step up through a provisioned migration as done.
29
+ *
30
+ * A later file stays pending. A mark whose id is not in `migrations` is left
31
+ * for the caller to treat as history this project does not contain.
32
+ *
33
+ * @param migrations - Files in apply order
34
+ * @param done - Keys `${id}:${step}` already in history
35
+ * @param historyIds - `migration_id` values in history
36
+ * @returns A set the applier can skip
37
+ */
38
+ export declare function expandProvisioned(migrations: readonly {
39
+ readonly id: string;
40
+ readonly steps: readonly unknown[];
41
+ }[], done: ReadonlySet<string>, historyIds: readonly string[]): Set<string>;
42
+ /**
43
+ * Reports whether a history id belongs to this project's files.
44
+ *
45
+ * A `provisioned@` mark belongs when the id after the prefix is a file.
46
+ *
47
+ * @param migrationId - History migration id
48
+ * @param known - File ids
49
+ * @returns `true` when status should not call the row ahead
50
+ */
51
+ export declare function provisionHistoryIsKnown(migrationId: string, known: ReadonlySet<string>): boolean;
52
+ /**
53
+ * Installs `head` into an empty schema and inserts reference rows.
54
+ *
55
+ * The plan is from an empty catalog, so no expand, backfill, or contract
56
+ * file is replayed. An existing role is left in place. The caller records
57
+ * history.
58
+ *
59
+ * @param connection - Reserved connection, after the advisory lock
60
+ * @param head - Head snapshot
61
+ * @param schema - Schema that receives the objects
62
+ * @param reference - Rows to insert if missing
63
+ * @param migrationRole - Role assumed after this run creates it
64
+ */
65
+ export declare function installSnapshot(connection: DriverConnection, head: Catalog, schema: string, reference: readonly ReferenceTable[], session: {
66
+ setRole?: string;
67
+ }, migrationRole?: string): Promise<void>;
68
+ /**
69
+ * Names the first object that differs between two introspected catalogs.
70
+ *
71
+ * Type names are compared with {@link canonicalTypeName}. Managed history
72
+ * tables are omitted. `undefined` means the catalogs match.
73
+ *
74
+ * @param left - One introspection
75
+ * @param right - The other introspection
76
+ * @returns The object, and the statement that would change it, when they differ
77
+ */
78
+ export declare function snapshotDifference(left: Catalog, right: Catalog): string | undefined;
@@ -2,8 +2,10 @@
2
2
  * `okm migrate status`.
3
3
  *
4
4
  * One row per target: version, catalog hash, state, and a separate protected
5
- * column. The state is current, behind by expand, behind by contract, ahead,
6
- * or failed at a step.
5
+ * column. The state is current, behind by expand, behind by contract, ahead
6
+ * by expand, ahead by contract, or failed at a step with a resume hint. An
7
+ * unfinished backfill is a later section: migration, step, rows so far, last
8
+ * key, and state.
7
9
  */
8
10
  import type { MigrationClass } from "./plan.js";
9
11
  import type { StoredMigration } from "./files.js";
@@ -15,6 +17,18 @@ export type TargetStatus = {
15
17
  readonly catalogHash: string;
16
18
  readonly state: string;
17
19
  readonly protected: boolean;
20
+ /** Unfinished `okm_backfill` rows for this target. A finished step is absent. */
21
+ readonly backfills: readonly BackfillProgress[];
22
+ };
23
+ /** One backfill that has committed at least one batch and is not done. */
24
+ export type BackfillProgress = {
25
+ readonly migrationId: string;
26
+ readonly stepIndex: number;
27
+ /** Rows the committed batches changed. */
28
+ readonly rows: string;
29
+ /** Last committed boundary, or `-` when the first batch is still open. */
30
+ readonly lastKey: string;
31
+ readonly state: string;
18
32
  };
19
33
  /** A checkpoint row the state function reads. */
20
34
  export type StatusHistory = {
@@ -25,9 +39,11 @@ export type StatusHistory = {
25
39
  /**
26
40
  * Decides the status state from files and history.
27
41
  *
28
- * A partial migration is `failed at step`. History the files do not contain
29
- * is `ahead`. Pending files are behind by expand, or by contract when any
30
- * pending step is not expand.
42
+ * A partial migration is `failed at step` plus the resume hint. History the
43
+ * files do not contain is ahead by expand, or ahead by contract when a stored
44
+ * class is not expand. Pending files are behind by expand, or by contract
45
+ * when any pending step is not expand. The class is the one stored on the
46
+ * file or in `okm_history`.
31
47
  *
32
48
  * @param migrations - Files in apply order
33
49
  * @param history - Checkpoint rows
@@ -68,7 +84,10 @@ export declare function statusProject(cwd: string, flags: InvokeFlags): Promise<
68
84
  /**
69
85
  * Prints status rows.
70
86
  *
87
+ * The target columns stay `target`, `version`, `catalog`, `state`, and
88
+ * `protected`. Unfinished backfills follow as their own section.
89
+ *
71
90
  * @param rows - One per target
72
- * @returns Header plus one line per target
91
+ * @returns Header plus one line per target, then unfinished backfills
73
92
  */
74
93
  export declare function formatStatus(rows: readonly TargetStatus[]): string;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * One spelling for a SQL type name in a diff (D191).
3
+ *
4
+ * The catalog keeps the spelling the schema wrote. Introspection keeps
5
+ * `format_type`. Both sides of a comparison pass through here, so
6
+ * `timestamptz` and `timestamp with time zone` are one type. A different
7
+ * length, precision, or type stays different. An unknown name is returned
8
+ * as written.
9
+ */
10
+ /**
11
+ * Maps a schema spelling or a `format_type` spelling to the long form.
12
+ *
13
+ * Modifiers and array ranks stay. `varchar(20)[]` becomes
14
+ * `character varying(20)[]`.
15
+ *
16
+ * @param typeName - Type name from a catalog definition
17
+ * @returns The Postgres long spelling
18
+ */
19
+ export declare function canonicalTypeName(typeName: string): string;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Factories: `create`, `createMany`, `with({ child: n })`, and `x.ref`.
3
+ *
4
+ * A required column the definition leaves out is filled from its type (D199).
5
+ * `x.ref` reuses the latest row of that table in the same tenant, or creates one.
6
+ */
7
+ import type { Connected } from "../../runtime/types.js";
8
+ import { type TableFacts, type TestingSchema } from "./facts.js";
9
+ import type { Rng } from "./random.js";
10
+ declare const REF: unique symbol;
11
+ /** A `x.ref("table")` marker. Resolved when the row is inserted. */
12
+ type FactoryRef = {
13
+ readonly [REF]: string;
14
+ };
15
+ /** Values a factory definition may return. */
16
+ export type FactoryContext = {
17
+ /** `userN@example.com`. N increases for each call. */
18
+ email(): string;
19
+ /** A first and last name from a fixed list. */
20
+ name(): string;
21
+ /** `count` words from a fixed list. */
22
+ words(count: number): string;
23
+ /** An integer in `[0, bound)`. `bound` defaults to 10_000. */
24
+ int(bound?: number): number;
25
+ /** A number in `[0, 1)`. */
26
+ float(): number;
27
+ /** A boolean. */
28
+ boolean(): boolean;
29
+ /** An ISO date `YYYY-MM-DD` in January 2020. */
30
+ date(): string;
31
+ /** A UUID from the seed. */
32
+ uuid(): string;
33
+ /** One entry of `list`. */
34
+ pick<T>(list: readonly T[]): T;
35
+ /**
36
+ * The primary key of `table`.
37
+ *
38
+ * The first call creates the row. A later call in the same tenant reuses it.
39
+ * A `create` of that table replaces the reused row.
40
+ */
41
+ ref(table: string): FactoryRef;
42
+ };
43
+ /** One table's factory. */
44
+ export type TableFactory = {
45
+ /**
46
+ * Inserts one row.
47
+ *
48
+ * @param overrides - Column values. A tenant key here is the scope, not a column.
49
+ * @returns The inserted row
50
+ */
51
+ create(overrides?: Readonly<Record<string, unknown>>): Promise<Record<string, unknown>>;
52
+ /**
53
+ * Inserts `count` rows. Each row draws new generated values.
54
+ *
55
+ * @param count - How many rows
56
+ * @param overrides - Column values applied to every row
57
+ * @returns The inserted rows
58
+ */
59
+ createMany(count: number, overrides?: Readonly<Record<string, unknown>>): Promise<readonly Record<string, unknown>[]>;
60
+ /**
61
+ * Creates `count` rows of `child` after the parent, linked by the foreign key.
62
+ *
63
+ * @param children - Child table name to how many rows
64
+ * @returns A factory that still inserts this table
65
+ */
66
+ with(children: Readonly<Record<string, number>>): TableFactory;
67
+ };
68
+ /**
69
+ * Remembers `host` for the harness object `okm seed` prints from.
70
+ *
71
+ * @param harness - The object returned by `testing()`
72
+ * @param host - Its factory state
73
+ */
74
+ export declare function attachHost(harness: object, host: FactoryHost): void;
75
+ /**
76
+ * Rows inserted through factories, summed by table name.
77
+ *
78
+ * @param harness - The object returned by `testing()`
79
+ * @returns Counts in table-name order
80
+ */
81
+ export declare function createdOf(harness: object): readonly {
82
+ readonly table: string;
83
+ readonly count: number;
84
+ }[];
85
+ /** Shared state for one harness. */
86
+ export type FactoryHost = {
87
+ readonly schema: TestingSchema;
88
+ readonly db: Connected<TestingSchema>;
89
+ readonly facts: ReadonlyMap<string, TableFacts>;
90
+ readonly rng: Rng;
91
+ /** Latest row of a table in one tenant. Isolation uses its own map. */
92
+ cache: Map<string, Record<string, unknown>>;
93
+ /** Tables currently being inserted. A repeat is a factory cycle. */
94
+ readonly creating: Set<string>;
95
+ readonly created: Map<string, number>;
96
+ readonly definitions: Map<string, (context: FactoryContext) => Readonly<Record<string, unknown>>>;
97
+ };
98
+ /**
99
+ * Builds the factory object for `definitions`.
100
+ *
101
+ * @param host - Schema, client, and seed
102
+ * @param definitions - One function per table
103
+ * @returns A factory per definition
104
+ */
105
+ export declare function bindFactories(host: FactoryHost, definitions: Readonly<Record<string, (context: FactoryContext) => Readonly<Record<string, unknown>>>>): Record<string, TableFactory>;
106
+ /**
107
+ * Inserts one row, filling required columns. Used by factories and by isolation.
108
+ *
109
+ * @param host - Schema, client, and seed
110
+ * @param table - Table name
111
+ * @param overrides - Column values and the tenant key
112
+ * @param children - Child counts from `with`
113
+ * @param inherited - Tenant from the parent row, when this row is a child
114
+ * @returns The inserted row
115
+ */
116
+ export declare function insertRow(host: FactoryHost, table: string, overrides: Readonly<Record<string, unknown>> | undefined, children: Readonly<Record<string, number>> | undefined, inherited: string | undefined): Promise<Record<string, unknown>>;
117
+ export {};
@@ -0,0 +1,72 @@
1
+ /**
2
+ * What a factory needs from a compiled schema: required columns, foreign keys,
3
+ * and which tables are tenant or global.
4
+ *
5
+ * Column tenancy is the only strategy in this version. A table is tenant when
6
+ * its predicate binds a scope, and global when `global("reason")` opted it out.
7
+ * `schemaPerTenant` and `databasePerTenant` are not in 0.4.
8
+ */
9
+ import type { Catalog } from "../../contracts/catalog/types.js";
10
+ import type { QuerySchema, RelationModel } from "../../dialects/pg/model.js";
11
+ /** A schema `testing()` can open. `schema()` returns this shape. */
12
+ export type TestingSchema = QuerySchema & {
13
+ readonly catalog: Catalog;
14
+ };
15
+ /** One foreign key, SQL names, child columns aligned with the target columns. */
16
+ export type ForeignKey = {
17
+ readonly columns: readonly string[];
18
+ readonly target: string;
19
+ readonly targetColumns: readonly string[];
20
+ };
21
+ /** One column, with the catalog facts the factory uses to fill a default. */
22
+ export type ColumnFacts = {
23
+ readonly field: string;
24
+ readonly sql: string;
25
+ readonly dataType: string;
26
+ readonly nullable: boolean;
27
+ readonly databaseDefault: boolean;
28
+ readonly identity: boolean;
29
+ readonly generated: boolean;
30
+ readonly guarded: boolean;
31
+ readonly writable: boolean;
32
+ readonly fill: boolean;
33
+ readonly accepts: readonly string[] | undefined;
34
+ readonly encode: (value: unknown) => string;
35
+ };
36
+ /** One table. */
37
+ export type TableFacts = {
38
+ readonly name: string;
39
+ readonly sql: string;
40
+ readonly primary: readonly string[];
41
+ readonly columns: readonly ColumnFacts[];
42
+ readonly relations: readonly RelationModel[];
43
+ readonly foreignKeys: readonly ForeignKey[];
44
+ /** Column tenancy applies a predicate. */
45
+ readonly tenant: boolean;
46
+ /** `global("reason")`. Absent on a tenant table and on a schema with no tenancy. */
47
+ readonly globalReason: string | undefined;
48
+ };
49
+ /**
50
+ * Scope value used to ask a tenancy object whether a table is tenant.
51
+ *
52
+ * A test tenancy can recognise this probe and still omit the predicate on a
53
+ * real query. That is how the isolation check is shown to fail.
54
+ */
55
+ export declare const tenantProbe = "00000000-0000-4000-8000-000000000001";
56
+ /**
57
+ * Reads every table in declaration order.
58
+ *
59
+ * @param schema - Compiled schema
60
+ * @returns One facts object per table
61
+ */
62
+ export declare function readFacts(schema: TestingSchema): readonly TableFacts[];
63
+ /**
64
+ * A column the factory must set when the definition and the overrides omit it.
65
+ *
66
+ * Nullable, a database default, identity, a generated expression, a client
67
+ * fill, and a guarded column are left to the database or the scope (D199).
68
+ *
69
+ * @param column - Column facts
70
+ * @returns Whether the factory fills it
71
+ */
72
+ export declare function isRequired(column: ColumnFacts): boolean;