okmodel 0.5.0 → 0.6.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 (169) hide show
  1. package/README.md +36 -18
  2. package/dist/adapters/pg/no-inflight-timeout.d.ts +17 -0
  3. package/dist/adapters/pg/postgres-extra.d.ts +22 -0
  4. package/dist/contracts/catalog/document.d.ts +19 -5
  5. package/dist/contracts/catalog/object.d.ts +2 -1
  6. package/dist/contracts/catalog/policy.d.ts +48 -0
  7. package/dist/contracts/catalog/types.d.ts +25 -1
  8. package/dist/contracts/catalog/view.d.ts +2 -0
  9. package/dist/contracts/internal.js +1 -1
  10. package/dist/contracts/location.d.ts +10 -0
  11. package/dist/dialects/pg/column.d.ts +13 -2
  12. package/dist/dialects/pg/ddl.d.ts +37 -1
  13. package/dist/dialects/pg/emit.d.ts +19 -1
  14. package/dist/dialects/pg/errors.d.ts +32 -1
  15. package/dist/dialects/pg/ext/citext.js +1 -1
  16. package/dist/dialects/pg/ext/index.js +1 -1
  17. package/dist/dialects/pg/ext/pg-trgm.js +1 -1
  18. package/dist/dialects/pg/filter-parse.d.ts +10 -3
  19. package/dist/dialects/pg/fn/index.js +10 -2
  20. package/dist/dialects/pg/index.d.ts +2 -1
  21. package/dist/dialects/pg/index.js +29 -17
  22. package/dist/dialects/pg/introspect.d.ts +7 -0
  23. package/dist/dialects/pg/model.d.ts +7 -2
  24. package/dist/dialects/pg/ops/or.d.ts +1 -1
  25. package/dist/dialects/pg/owner.d.ts +42 -0
  26. package/dist/dialects/pg/path-endpoint.d.ts +20 -0
  27. package/dist/dialects/pg/role/check.d.ts +3 -0
  28. package/dist/dialects/pg/role/index.d.ts +71 -6
  29. package/dist/dialects/pg/role/sql.d.ts +11 -0
  30. package/dist/dialects/pg/schema.d.ts +2 -0
  31. package/dist/dialects/pg/table.d.ts +25 -3
  32. package/dist/dialects/pg/temporal.d.ts +5 -76
  33. package/dist/dialects/pg/tenancy.d.ts +45 -3
  34. package/dist/dialects/pg/view/client.d.ts +19 -1
  35. package/dist/dialects/pg/view/index.d.ts +4 -0
  36. package/dist/dialects/pg/view/index.js +21 -178
  37. package/dist/dialects/pg/view/refresh.d.ts +24 -0
  38. package/dist/runtime/batch.d.ts +1 -1
  39. package/dist/runtime/client.d.ts +6 -1
  40. package/dist/runtime/conflict.d.ts +2 -1
  41. package/dist/runtime/drift.d.ts +9 -0
  42. package/dist/runtime/inflight.d.ts +14 -0
  43. package/dist/runtime/pg/bun.js +6 -5
  44. package/dist/runtime/pg/pg.js +5 -5
  45. package/dist/runtime/pg/pglite.js +6 -5
  46. package/dist/runtime/pg/postgresjs.js +5 -5
  47. package/dist/runtime/plan.d.ts +22 -1
  48. package/dist/runtime/predicate.d.ts +19 -0
  49. package/dist/runtime/safety/index.js +1 -1
  50. package/dist/runtime/tenancy/composite.d.ts +26 -0
  51. package/dist/runtime/tenancy/context.d.ts +16 -0
  52. package/dist/runtime/tenancy/index.d.ts +9 -5
  53. package/dist/runtime/tenancy/index.js +1508 -37
  54. package/dist/runtime/tenancy/path.d.ts +87 -0
  55. package/dist/runtime/tenancy/rls.d.ts +67 -0
  56. package/dist/runtime/tenancy/via.d.ts +22 -0
  57. package/dist/runtime/traits/index.js +17 -11
  58. package/dist/runtime/types.d.ts +13 -1
  59. package/dist/runtime/validate/index.js +4 -4
  60. package/dist/runtime/write.d.ts +1 -1
  61. package/dist/shared/09p049xa.js +702 -0
  62. package/dist/shared/{ykv7wh5b.js → 1f0xa3cb.js} +5 -5
  63. package/dist/shared/1nw1f3vs.js +23 -0
  64. package/dist/shared/{6p5k20sm.js → 1z04fa85.js} +21 -11
  65. package/dist/shared/{p7pfx624.js → 2cg3wnxc.js} +314 -65
  66. package/dist/shared/{dyj86cmj.js → 2jd1sn0e.js} +2 -2
  67. package/dist/shared/{qgvt8nx1.js → 2z7wv5tj.js} +21 -5
  68. package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
  69. package/dist/shared/{sf8m6tfy.js → 32cnm7kn.js} +8 -3
  70. package/dist/shared/{t4tbsy9h.js → 358zsv4r.js} +1 -1
  71. package/dist/shared/{jdgqqx31.js → 3fqwmr09.js} +38 -14
  72. package/dist/shared/{922qbgnw.js → 3qnhc7w5.js} +16 -9
  73. package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
  74. package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
  75. package/dist/shared/{k7cc9nzk.js → 5d8t84hg.js} +15 -3
  76. package/dist/shared/{fa9vt245.js → 5j36z8dn.js} +17 -6
  77. package/dist/shared/{1g0drakv.js → 5yv4k6a6.js} +1 -1
  78. package/dist/shared/8dqhth8c.js +12 -0
  79. package/dist/shared/8etn7d8w.js +62 -0
  80. package/dist/shared/{g516zdsb.js → 8x72s86k.js} +476 -30
  81. package/dist/shared/{nkc9sfqb.js → 929h19xp.js} +145 -7
  82. package/dist/shared/{gjthds9j.js → 9628a9xt.js} +1 -1
  83. package/dist/shared/{8xsbagad.js → 9s0ypp87.js} +88 -7
  84. package/dist/shared/{b066g80h.js → ag4c7516.js} +2 -2
  85. package/dist/shared/b3awxn7h.js +45 -0
  86. package/dist/shared/b4v57v3x.js +9 -0
  87. package/dist/shared/bdk4sty6.js +58 -0
  88. package/dist/shared/{s6gqfn5j.js → c58secgj.js} +125 -8
  89. package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
  90. package/dist/shared/d5azex6s.js +6 -0
  91. package/dist/shared/{5fqx9yh4.js → dk8v79p3.js} +3 -1
  92. package/dist/shared/{4m9fn52z.js → ed0b8k4s.js} +1 -1
  93. package/dist/shared/{z2rnngj4.js → ek2wt2dz.js} +8 -5
  94. package/dist/shared/{wxn4b7pp.js → f0f1n281.js} +59 -12
  95. package/dist/shared/{gdh9mrtp.js → g90m9b0g.js} +3 -2
  96. package/dist/shared/{tbszx8mz.js → gwn6v667.js} +2 -2
  97. package/dist/shared/{c8aghh05.js → h15jx9bc.js} +8 -2
  98. package/dist/shared/{3p4b1fg0.js → hazqqecq.js} +17 -93
  99. package/dist/shared/{4ef7mmqf.js → j3rkk835.js} +34 -11
  100. package/dist/shared/{bt9sfq0z.js → j60w0dvr.js} +56 -21
  101. package/dist/shared/{ycatkxmq.js → jgqj11w4.js} +1 -1
  102. package/dist/shared/k68eedmy.js +85 -0
  103. package/dist/shared/{szm0cksa.js → knmes2kh.js} +34 -6
  104. package/dist/shared/{ckjbyrt9.js → mb2ycs1p.js} +7 -9
  105. package/dist/shared/{tq9afp8d.js → n076ydsc.js} +1 -1
  106. package/dist/shared/{xhh1srw3.js → nx9hh281.js} +50 -32
  107. package/dist/shared/{kdkxh3kb.js → p248q79y.js} +58 -59
  108. package/dist/shared/{990a291j.js → p6bjzq9q.js} +14 -9
  109. package/dist/shared/{h4nqvjkp.js → pjr6xrm5.js} +2 -2
  110. package/dist/shared/pz6rj8ay.js +212 -0
  111. package/dist/shared/{5vdm4sgp.js → q3j1tq7a.js} +36 -23
  112. package/dist/shared/{8bwbwy8h.js → qq3899ht.js} +128 -18
  113. package/dist/shared/{dxjp14rk.js → r11n8grd.js} +2 -2
  114. package/dist/shared/r1fdd0f6.js +19 -0
  115. package/dist/shared/{8ktwd3rh.js → r3j8s5y0.js} +1 -1
  116. package/dist/shared/rhs3xe4k.js +7 -0
  117. package/dist/shared/{jsd07prz.js → rpycarcr.js} +6 -6
  118. package/dist/shared/{xsq7v6wp.js → sbdme3vy.js} +14 -2
  119. package/dist/shared/{xdpj5kkm.js → t3g0k88x.js} +775 -264
  120. package/dist/shared/{ayz64vzj.js → ttmg09vd.js} +708 -19
  121. package/dist/shared/{r6pj4fq2.js → venqrjna.js} +8 -4
  122. package/dist/shared/{fancm5pt.js → vjpnzwjz.js} +138 -15
  123. package/dist/shared/{9tgmr1gh.js → vmfkhszw.js} +44 -9
  124. package/dist/shared/{wqem4jed.js → wkme6tep.js} +11 -9
  125. package/dist/shared/{qy9rdq0g.js → wt5rzv1h.js} +15 -11
  126. package/dist/shared/wxr8cwns.js +6 -0
  127. package/dist/shared/{bzsjtd7c.js → wynzeggs.js} +19 -14
  128. package/dist/shared/x3jc53kg.js +220 -0
  129. package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
  130. package/dist/shared/{fyqkk1m0.js → xq0mvhts.js} +10 -9
  131. package/dist/shared/xqve9598.js +53 -0
  132. package/dist/shared/{47xzb9fc.js → y2t3jktd.js} +2 -2
  133. package/dist/shared/{9x25fxhc.js → y34a8714.js} +1 -1
  134. package/dist/shared/{k7dwkhvq.js → y6jdds7g.js} +4 -1
  135. package/dist/shared/{h8ne16fq.js → ys0xzyaj.js} +1 -1
  136. package/dist/shared/{c9z5g0fm.js → ywr8hmey.js} +5 -2
  137. package/dist/shared/{g7ez901n.js → ywspezzx.js} +1 -1
  138. package/dist/shared/{9m80mkac.js → ze4hxsmy.js} +1 -1
  139. package/dist/shared/{fb00y8fp.js → zm0mafk4.js} +12 -6
  140. package/dist/shared/{y005b761.js → zy8r7qvz.js} +6 -2
  141. package/dist/tooling/cli.js +97 -24
  142. package/dist/tooling/migrate/apply.d.ts +14 -0
  143. package/dist/tooling/migrate/check.d.ts +3 -4
  144. package/dist/tooling/migrate/classify.d.ts +4 -0
  145. package/dist/tooling/migrate/commands.d.ts +1 -2
  146. package/dist/tooling/migrate/config.d.ts +3 -2
  147. package/dist/tooling/migrate/drift.d.ts +1 -1
  148. package/dist/tooling/migrate/estimate.d.ts +14 -1
  149. package/dist/tooling/migrate/files.d.ts +11 -1
  150. package/dist/tooling/migrate/index.js +17 -16
  151. package/dist/tooling/migrate/lint.d.ts +4 -4
  152. package/dist/tooling/migrate/lost.d.ts +28 -0
  153. package/dist/tooling/migrate/paths.d.ts +14 -0
  154. package/dist/tooling/migrate/plan.d.ts +10 -3
  155. package/dist/tooling/migrate/policy.d.ts +11 -0
  156. package/dist/tooling/migrate/runner.d.ts +25 -0
  157. package/dist/tooling/migrate/sandbox.d.ts +57 -0
  158. package/dist/tooling/migrate/sql-lex.d.ts +39 -0
  159. package/dist/tooling/migrate/text.d.ts +42 -0
  160. package/dist/tooling/node-version.d.ts +21 -0
  161. package/dist/tooling/testing/factories.d.ts +10 -0
  162. package/dist/tooling/testing/facts.d.ts +33 -5
  163. package/dist/tooling/testing/index.d.ts +1 -1
  164. package/dist/tooling/testing/index.js +3 -3
  165. package/dist/tooling/testing/isolation.d.ts +15 -8
  166. package/package.json +10 -6
  167. package/dist/shared/0v1g424p.js +0 -26
  168. package/dist/shared/r73pt594.js +0 -426
  169. package/dist/shared/rasvvzgz.js +0 -21
@@ -49,6 +49,14 @@ export type ApplyRequest = {
49
49
  * `SET ROLE` before any statement. That statement is not a plan step.
50
50
  */
51
51
  readonly migrationRole?: string;
52
+ /**
53
+ * Application role.
54
+ *
55
+ * After the plan, this role receives `SELECT` on `okm_meta` and nothing
56
+ * else. `connect()` can then read the stored hash. Check scratch paths
57
+ * leave this unset.
58
+ */
59
+ readonly appRole?: string;
52
60
  /**
53
61
  * Schema the migration role may create in.
54
62
  *
@@ -95,6 +103,12 @@ export type ApplyReport = {
95
103
  /** Set when the runner issued `SET ROLE` at session start. */
96
104
  readonly setRole?: string;
97
105
  };
106
+ /**
107
+ * Creates `okm_meta`, `okm_history`, and `okm_backfill` when they are absent.
108
+ *
109
+ * @param execute - Runs one statement on the apply or check connection
110
+ */
111
+ export declare function ensureMigrationTables(execute: (sql: string) => Promise<unknown>): Promise<void>;
98
112
  /**
99
113
  * Groups pending steps into the transactions apply will run.
100
114
  *
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * `okm migrate check` proves a migration history is sound (D196).
3
3
  *
4
- * The command replays every file into a scratch schema through
5
- * {@link applyTarget}, then plans the introspected schema back to that
6
- * file's catalog. It also checks the previous catalog, the head, and the
7
- * linter. Nothing here changes apply.
4
+ * The command replays every file into a scratch schema inside one transaction
5
+ * and rolls that transaction back. It also checks the previous catalog, the
6
+ * head, and the linter. Nothing here changes apply.
8
7
  */
9
8
  import type { Catalog } from "../../contracts/catalog/types.js";
10
9
  import { type MigrationClass } from "./classify.js";
@@ -76,6 +76,10 @@ export declare const STEP_CLASS: {
76
76
  readonly "rename-sequence": "contract";
77
77
  readonly "backfill-expand": "expand";
78
78
  readonly "backfill-contract": "contract";
79
+ readonly "enable-rls": "expand";
80
+ readonly "create-policy": "expand";
81
+ readonly "drop-policy": "contract";
82
+ readonly "disable-rls": "contract";
79
83
  readonly "raw-sql": "unclassified";
80
84
  };
81
85
  /** A planner operation. */
@@ -4,7 +4,6 @@
4
4
  * The bin wrapper imports {@link run}. Importing this module does not parse
5
5
  * `process.argv`.
6
6
  */
7
- import { OkmError } from "../../contracts/error.js";
8
7
  /** Where a command reads the project and writes its text. */
9
8
  export type CommandIo = {
10
9
  readonly cwd?: string;
@@ -18,7 +17,7 @@ export type CommandIo = {
18
17
  * @param error - Failure from a command
19
18
  * @returns The stderr text, including the trailing newline
20
19
  */
21
- export declare function formatFailure(error: OkmError): string;
20
+ export declare function formatFailure(error: unknown, verbose?: boolean): string;
22
21
  /**
23
22
  * Runs one `okm` invocation.
24
23
  *
@@ -59,10 +59,11 @@ export type MigrateConfig = {
59
59
  readonly statementTimeoutMs?: number;
60
60
  };
61
61
  /**
62
- * Migration role and application role.
62
+ * Migration role, application role, and the optional reporting role.
63
63
  *
64
64
  * A name is external unless it is also listed in `managed`. The plan creates
65
- * a managed role and never drops one.
65
+ * a managed role and never drops one. `reporting` receives select only.
66
+ * `columns` grants select or update on named columns.
66
67
  */
67
68
  readonly roles?: RolesInput;
68
69
  };
@@ -18,7 +18,7 @@ import type { RolesInput } from "../../dialects/pg/role/index.js";
18
18
  * @param author - Schema catalog, including roles when the config names them
19
19
  * @param roles - `defineConfig({ roles })`, when set
20
20
  */
21
- export declare function assertAuthorDrift(url: string, author: Catalog, roles?: RolesInput): Promise<void>;
21
+ export declare function assertAuthorDrift(url: string, author: Catalog, roles?: RolesInput, allowPooler?: boolean): Promise<void>;
22
22
  /**
23
23
  * Turns a postgres.js handle into the query shape introspection uses.
24
24
  *
@@ -40,7 +40,20 @@ export declare function readRowEstimates(url: string, schema: string, tables: re
40
40
  * @param estimates - `reltuples` for tables that exist on the target
41
41
  * @returns The text after `-- lock: `
42
42
  */
43
- export declare function annotateLock(step: PlanStep, estimates: ReadonlyMap<string, RowEstimate>): string;
43
+ export declare function annotateLock(step: PlanStep, estimates: ReadonlyMap<string, RowEstimate>, aliases?: ReadonlyMap<string, string>): string;
44
+ /**
45
+ * Maps a rename destination to the table that still has `reltuples`.
46
+ *
47
+ * The rename statement names the old table. Later steps name the new one,
48
+ * which is not in `pg_class` yet.
49
+ *
50
+ * @param steps - Plan steps
51
+ * @returns New name to old name
52
+ */
53
+ export declare function renameAliases(steps: readonly {
54
+ readonly kind?: string;
55
+ readonly sql: string;
56
+ }[]): ReadonlyMap<string, string>;
44
57
  /**
45
58
  * Compact phrase for one estimate.
46
59
  *
@@ -2,7 +2,7 @@
2
2
  * Migration files on disk: SQL from {@link formatPlan} plus the catalog hash.
3
3
  */
4
4
  import { parseCatalog } from "../../contracts/catalog/document.js";
5
- import { type PlanStep } from "./plan.js";
5
+ import { type MigrationPlan, type PlanStep } from "./plan.js";
6
6
  /** One migration the applier can run and resume. */
7
7
  export type StoredMigration = {
8
8
  readonly id: string;
@@ -25,6 +25,16 @@ export type StoredMigration = {
25
25
  * @returns Migrations in apply order
26
26
  */
27
27
  export declare function loadMigrations(directory: string): readonly StoredMigration[];
28
+ /**
29
+ * Reads one migration SQL file.
30
+ *
31
+ * Line endings and a UTF-8 BOM are normalized. A non-empty file with no steps
32
+ * and no `-- no steps` line is refused.
33
+ *
34
+ * @param path - `*.sql` path
35
+ * @returns The plan
36
+ */
37
+ export declare function readMigrationPlan(path: string): MigrationPlan;
28
38
  /**
29
39
  * Loads the last migration's catalog, which is the head snapshot.
30
40
  *
@@ -1,25 +1,27 @@
1
- import"../../shared/wqem4jed.js";
2
- import"../../shared/8bwbwy8h.js";
1
+ import"../../shared/wkme6tep.js";
2
+ import"../../shared/qq3899ht.js";
3
3
  import"../../shared/n442h54c.js";
4
4
  import {
5
5
  applyTarget2,
6
6
  backfillTiming2
7
- } from "../../shared/xhh1srw3.js";
7
+ } from "../../shared/nx9hh281.js";
8
8
  import {
9
- loadMigrations,
10
- loadHeadSnapshot,
11
9
  readReference
12
- } from "../../shared/kdkxh3kb.js";
13
- import"../../shared/szm0cksa.js";
14
- import"../../shared/fancm5pt.js";
10
+ } from "../../shared/p248q79y.js";
11
+ import"../../shared/knmes2kh.js";
12
+ import"../../shared/vjpnzwjz.js";
15
13
  import {
16
- assertTargetPolicy,
17
- selectTarget,
14
+ projectPath,
15
+ loadMigrations,
16
+ loadHeadSnapshot,
18
17
  loadBuiltSchema,
19
18
  loadConfig
20
- } from "../../shared/xdpj5kkm.js";
21
- import"../../shared/ayz64vzj.js";
22
- import"../../shared/8xsbagad.js";
19
+ } from "../../shared/t3g0k88x.js";
20
+ import {
21
+ assertTargetPolicy,
22
+ selectTarget
23
+ } from "../../shared/ttmg09vd.js";
24
+ import"../../shared/9s0ypp87.js";
23
25
  import {
24
26
  OkmError2
25
27
  } from "../../shared/52szzakm.js";
@@ -29,12 +31,11 @@ function defineConfig(config) {
29
31
  return config;
30
32
  }
31
33
  // src/tooling/migrate/provision.ts
32
- import { join } from "node:path";
33
34
  async function provision(target, cwd = process.cwd()) {
34
35
  const config = await loadConfig(cwd);
35
36
  const selected = selectTarget(config, target);
36
37
  assertTargetPolicy(selected, "provision");
37
- const directory = join(cwd, config.migrations ?? "migrations");
38
+ const directory = projectPath(cwd, config.migrations ?? "migrations");
38
39
  const migrations = loadMigrations(directory);
39
40
  const head = loadHeadSnapshot(directory);
40
41
  if (head === undefined) {
@@ -57,7 +58,7 @@ async function provision(target, cwd = process.cwd()) {
57
58
  reference: readReference(built.tables, built.casing, head.catalog),
58
59
  only: true
59
60
  },
60
- ...config.roles !== undefined ? { migrationRole: config.roles.migration } : {},
61
+ ...config.roles !== undefined ? { migrationRole: config.roles.migration, appRole: config.roles.app } : {},
61
62
  lintDirectory: directory
62
63
  });
63
64
  if (report.applied.length === 0)
@@ -2,14 +2,14 @@
2
2
  * Runs the migration linter.
3
3
  *
4
4
  * The linter reads a plan and the two catalogs. It does not connect.
5
- * An override is a `-- okm-allow` line on the statement. It silences only
6
- * the code it names, and only with a reason.
5
+ * An override is a `-- okm-allow` line on the step. It silences that code
6
+ * on every statement of the step, and only with a reason.
7
7
  */
8
8
  import type { Catalog } from "../../contracts/catalog/types.js";
9
9
  import { OkmError } from "../../contracts/error.js";
10
10
  import { type LintSeverity } from "./lint-rules.js";
11
- import { type MigrationPlan } from "./plan.js";
12
- /** One finding. `place` is `step N` or `table.column`, optionally prefixed with the file. */
11
+ import type { MigrationPlan } from "./plan.js";
12
+ /** One finding. `place` is `step N`, `step N statement M`, or `table.column`. */
13
13
  export type Finding = {
14
14
  readonly severity: LintSeverity;
15
15
  readonly code: string;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * A session that dies in the middle of a command.
3
+ *
4
+ * A commit that was sent and never answered stays OKM1401. Every other
5
+ * dropped connection is unavailable. Cleanup that throws is ignored so the
6
+ * original failure is the one the command reports.
7
+ */
8
+ import { OkmError } from "../../contracts/error.js";
9
+ /**
10
+ * Maps a lost session to unavailable or OKM1401.
11
+ *
12
+ * An {@link OkmError} that already has one of those kinds is returned. An
13
+ * `invalid` error whose cause is a lost session keeps its message and takes
14
+ * that kind. Anything else returns `undefined`.
15
+ *
16
+ * @param error - Caught value
17
+ * @returns The session failure, or `undefined` when this is some other error
18
+ */
19
+ export declare function sessionFailure(error: unknown): OkmError | undefined;
20
+ /**
21
+ * Runs cleanup and ignores its failure.
22
+ *
23
+ * A dead session must not replace the error the command is already reporting,
24
+ * and it must not leave the process waiting on that session.
25
+ *
26
+ * @param run - Close, release, or unlock
27
+ */
28
+ export declare function finishQuiet(run: () => Promise<unknown>): Promise<void>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Config paths resolved against the project directory.
3
+ */
4
+ /**
5
+ * Resolves a config path against `cwd`.
6
+ *
7
+ * An absolute segment stays absolute. A relative segment, including `..`,
8
+ * is resolved from `cwd`.
9
+ *
10
+ * @param cwd - Project directory
11
+ * @param parts - `schema`, `tables`, `migrations`, `out`, a seed file, or a child
12
+ * @returns An absolute path
13
+ */
14
+ export declare function projectPath(cwd: string, ...parts: readonly string[]): string;
@@ -70,10 +70,10 @@ export type PlanStep = {
70
70
  */
71
71
  readonly behavior?: "change";
72
72
  /**
73
- * `-- okm-allow` lines directly above the statement.
73
+ * `-- okm-allow` lines on the step.
74
74
  *
75
- * A reason silences that code only. An empty reason, or a code the
76
- * statement did not trigger, is OKM1510 and silences nothing.
75
+ * A reason silences that code on every statement of the step. An empty
76
+ * reason, or a code no statement triggered, is OKM1510 and silences nothing.
77
77
  */
78
78
  readonly allows?: readonly StepAllow[];
79
79
  };
@@ -128,6 +128,13 @@ export declare function formatPlan(plan: MigrationPlan, lockText?: (step: PlanSt
128
128
  /**
129
129
  * Reads a plan written by {@link formatPlan}.
130
130
  *
131
+ * A blank line ends a step. A blank line inside a quote, a dollar quote, a
132
+ * block comment, or a `BEGIN ATOMIC` routine body stays in that step, and so
133
+ * does a `--` line while that quote or body is still open.
134
+ *
135
+ * `\r\n` and a lone `\r` are read as `\n`. A leading UTF-8 BOM is stripped.
136
+ * UTF-16 is refused.
137
+ *
131
138
  * @param text - SQL file text
132
139
  * @returns The plan. The header class is kept when the file has no steps
133
140
  */
@@ -56,6 +56,17 @@ export declare function assertTargetAlias(targets: readonly TargetRecord[]): voi
56
56
  * @param allowPooler - The invocation opted in
57
57
  */
58
58
  export declare function assertDirectConnection(url: string, allowPooler: boolean): void;
59
+ /**
60
+ * Refuses a session whose backend pid changes between two statements.
61
+ *
62
+ * The two reads are separate round trips and are not wrapped in a
63
+ * transaction. A transaction-mode pooler can hand back a different backend
64
+ * for the second one. `--allow-pooler` skips the probe.
65
+ *
66
+ * @param readPid - Reads `pg_backend_pid()` on the session under test
67
+ * @param allowPooler - The invocation opted in
68
+ */
69
+ export declare function assertStableBackend(readPid: () => Promise<string>, allowPooler: boolean): Promise<void>;
59
70
  /**
60
71
  * Lists configured targets.
61
72
  *
@@ -95,7 +95,32 @@ export declare function assumeRole(connection: DriverConnection, role: string, s
95
95
  * @param schema - Schema the migration writes
96
96
  * @param role - Role that was just created
97
97
  */
98
+ /**
99
+ * Grants the application role `SELECT` on `okm_meta`.
100
+ *
101
+ * No other privilege. When the session has `SET ROLE`, the grant runs as
102
+ * the session user and the role is restored. A second apply grants the
103
+ * same privilege again.
104
+ *
105
+ * @param connection - Reserved connection
106
+ * @param role - Application role. Omitted when the config names none
107
+ * @param session - Role the runner already assumed
108
+ */
109
+ export declare function grantAppMeta(connection: DriverConnection, role: string | undefined, session: {
110
+ setRole?: string;
111
+ }): Promise<void>;
98
112
  export declare function grantSchema(connection: DriverConnection, schema: string, role: string): Promise<void>;
113
+ /**
114
+ * Fails when `GRANT USAGE ON SCHEMA` did not add the role.
115
+ *
116
+ * Postgres warns and changes nothing when the acting role does not own the
117
+ * schema. A later check then reports the grant forever. The migration role
118
+ * has to own the schema first.
119
+ *
120
+ * @param connection - Reserved connection, in the grant's transaction
121
+ * @param sql - The statement that just ran
122
+ */
123
+ export declare function assertSchemaUsageLanded(connection: DriverConnection, sql: string): Promise<void>;
99
124
  /**
100
125
  * Reports whether Postgres refuses to run `sql` inside a transaction.
101
126
  *
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Replays `okm migrate check` inside one transaction and rolls it back.
3
+ *
4
+ * The scratch schema is created in that transaction. Nothing here updates a
5
+ * system catalog or a real ACL. A step that cannot run in a transaction is
6
+ * rewritten to the transactional form, or skipped and named.
7
+ */
8
+ import type { Catalog } from "../../contracts/catalog/types.js";
9
+ import type { MigrateConfig } from "./config.js";
10
+ import { type MigrationPlan, type PlanStep } from "./plan.js";
11
+ /** One migration the check replays. */
12
+ export type CheckMigration = {
13
+ readonly id: string;
14
+ readonly catalog: Catalog;
15
+ readonly steps: readonly PlanStep[];
16
+ };
17
+ /** The first migration whose replay does not match its catalog. */
18
+ export type ReplayStop = {
19
+ readonly index: number;
20
+ readonly back: MigrationPlan;
21
+ };
22
+ /** Replay result. `skips` are statements check did not execute. */
23
+ export type ReplayOutcome = {
24
+ readonly skips: readonly string[];
25
+ readonly stop?: ReplayStop;
26
+ };
27
+ /**
28
+ * Replays `history` in one transaction and rolls it back.
29
+ *
30
+ * @param url - Target URL
31
+ * @param scratch - Schema created inside the transaction
32
+ * @param config - Roles and timeouts
33
+ * @param allowPooler - Passed through to the pooler guard
34
+ * @param history - Migrations in apply order
35
+ * @returns Skipped statements, and the first catalog mismatch
36
+ */
37
+ export declare function replayCheckHistory(url: string, scratch: string, config: MigrateConfig, allowPooler: boolean, history: readonly CheckMigration[]): Promise<ReplayOutcome>;
38
+ /**
39
+ * Compares a head snapshot with a full replay. Both run in rolled-back transactions.
40
+ *
41
+ * @param url - Target URL
42
+ * @param provided - Scratch schema for the snapshot
43
+ * @param replayed - Scratch schema for the history
44
+ * @param config - Roles and timeouts
45
+ * @param allowPooler - Passed through to the pooler guard
46
+ * @param history - Migrations in apply order
47
+ * @returns The difference, or `undefined` when the catalogs match
48
+ */
49
+ export declare function proveCheckSnapshot(url: string, provided: string, replayed: string, config: MigrateConfig, allowPooler: boolean, history: readonly CheckMigration[]): Promise<string | undefined>;
50
+ /**
51
+ * Rewrites `"public".` to the scratch schema.
52
+ *
53
+ * @param steps - Plan steps
54
+ * @param schema - Scratch schema
55
+ * @returns The same steps, aimed at `schema`
56
+ */
57
+ export declare function retargetSteps(steps: readonly PlanStep[], schema: string): readonly PlanStep[];
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Splits one migration step into statements.
3
+ *
4
+ * Comments are removed. A semicolon splits only at the top level.
5
+ * `standard_conforming_strings` is on: a plain `'...'` ends on a single
6
+ * quote, and `''` is one quote inside it. `E'...'` still treats a backslash
7
+ * as an escape. A dollar quote runs until the same tag; a different tag
8
+ * inside it is text, so `$outer$ ... $inner$ ... $inner$ ... $outer$` stays
9
+ * one span. A `BEGIN ATOMIC` body of `CREATE FUNCTION` or `CREATE PROCEDURE`
10
+ * stays in that statement: a semicolon inside it does not split, and
11
+ * `CASE ... END` does not close the body. `BEGIN ATOMIC` elsewhere still
12
+ * splits. An unterminated string, quoted identifier, block comment, dollar
13
+ * quote, or atomic body is one opaque tail. This function does not throw.
14
+ */
15
+ /** Statements of `sql`, comments removed, with no trailing semicolon. */
16
+ export declare function sqlStatements(sql: string): readonly string[];
17
+ /**
18
+ * Whether `sql` still has an open quote or block comment.
19
+ *
20
+ * A line comment does not count: it ends at the newline or at the end.
21
+ * An unclosed `BEGIN ATOMIC` routine body counts too.
22
+ *
23
+ * @param sql - Step text accumulated so far
24
+ * @returns `true` when a later blank line is still inside that quote or body
25
+ */
26
+ export declare function sqlQuoteOpen(sql: string): boolean;
27
+ /**
28
+ * Transaction control in a migration step.
29
+ *
30
+ * Dollar-quoted bodies are scanned only for a `DO` block. `CREATE FUNCTION`
31
+ * and `CREATE PROCEDURE` may commit inside their own body. A `BEGIN ATOMIC`
32
+ * body stays one statement, so its `END` is not transaction control. A bare
33
+ * `BEGIN` or `END` inside a `DO` body is plpgsql and is ignored. `ROLLBACK TO`
34
+ * does not end the outer transaction.
35
+ *
36
+ * @param sql - Step text
37
+ * @returns The command, or `undefined` when the step stays in one transaction
38
+ */
39
+ export declare function transactionControl(sql: string): string | undefined;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Migration file text before it is parsed.
3
+ *
4
+ * Line endings and a UTF-8 BOM are normalized here, once, so every reader
5
+ * sees the same plan. UTF-16 is refused. The kind stays `invalid`.
6
+ */
7
+ import { OkmError } from "../../contracts/error.js";
8
+ /** Why a migration file was refused before it was applied. */
9
+ export type MigrationTextReason = "no-steps" | "utf16";
10
+ /**
11
+ * A migration file this version will not apply.
12
+ *
13
+ * `reason` is `no-steps` or `utf16`. The kind stays `invalid`.
14
+ */
15
+ export declare class MigrationTextError extends OkmError {
16
+ /** Which check refused the file. */
17
+ readonly reason: MigrationTextReason;
18
+ /**
19
+ * @param reason - Which check failed
20
+ * @param message - What the author should change
21
+ */
22
+ constructor(reason: MigrationTextReason, message: string);
23
+ }
24
+ /**
25
+ * Strips a UTF-8 BOM and turns `\r\n` and a lone `\r` into `\n`.
26
+ *
27
+ * @param text - File text
28
+ * @param label - File name used when the text is UTF-16
29
+ * @returns Text `parsePlan` can split on `\n`
30
+ */
31
+ export declare function prepareMigrationText(text: string, label?: string): string;
32
+ /**
33
+ * Refuses a non-empty file that produced no steps and has no `-- no steps` line.
34
+ *
35
+ * An intentional empty plan from `formatPlan` carries that line.
36
+ * Anything else would apply as "nothing to apply" and skip the linter.
37
+ *
38
+ * @param text - Text already passed through {@link prepareMigrationText}
39
+ * @param stepCount - Steps the parser returned
40
+ * @param label - File name in the error
41
+ */
42
+ export declare function assertMigrationSteps(text: string, stepCount: number, label: string): void;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Node version `okm` needs in order to load `okmodel.config.ts`.
3
+ *
4
+ * Node 22.18 unflags type stripping. Bun loads TypeScript itself, so the
5
+ * check does not apply when `bun --bun` is the runtime.
6
+ */
7
+ /**
8
+ * Whether `version` can strip types in a `.ts` config.
9
+ *
10
+ * @param version - `process.version`, such as `v22.18.0`
11
+ * @returns `true` for Node 22.18 and every newer major
12
+ */
13
+ export declare function nodeSupportsConfigTypes(version: string): boolean;
14
+ /**
15
+ * Startup text when this process cannot load a TypeScript config.
16
+ *
17
+ * @param version - `process.version`
18
+ * @param bunRuntime - `true` when Bun is running the process
19
+ * @returns The stderr line, or `undefined` when the process can continue
20
+ */
21
+ export declare function nodeStartupMessage(version: string, bunRuntime: boolean): string | undefined;
@@ -114,4 +114,14 @@ export declare function bindFactories(host: FactoryHost, definitions: Readonly<R
114
114
  * @returns The inserted row
115
115
  */
116
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
+ /**
118
+ * Drops a cached factory row.
119
+ *
120
+ * `isolation()` deletes the row it inserted. A later table must not reuse that id.
121
+ *
122
+ * @param host - The open harness
123
+ * @param table - Table name
124
+ * @param tenant - Tenant scope, when the table is tenanted
125
+ */
126
+ export declare function forgetCached(host: FactoryHost, table: string, tenant: string | undefined): void;
117
127
  export {};
@@ -2,9 +2,9 @@
2
2
  * What a factory needs from a compiled schema: required columns, foreign keys,
3
3
  * and which tables are tenant or global.
4
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.
5
+ * A table is tenant when its predicate binds a scope, and global when
6
+ * `global("reason")` opted it out. Composite and path use that probe.
7
+ * `schemaPerTenant` and `databasePerTenant` are not in this version.
8
8
  */
9
9
  import type { Catalog } from "../../contracts/catalog/types.js";
10
10
  import type { QuerySchema, RelationModel } from "../../dialects/pg/model.js";
@@ -46,6 +46,25 @@ export type TableFacts = {
46
46
  /** `global("reason")`. Absent on a tenant table and on a schema with no tenancy. */
47
47
  readonly globalReason: string | undefined;
48
48
  };
49
+ /** One view or materialized view. Isolation reads it; it does not insert into it. */
50
+ export type ViewFacts = {
51
+ /** Client name, camelCase. */
52
+ readonly name: string;
53
+ /** SQL name. */
54
+ readonly sql: string;
55
+ readonly kind: "view" | "materializedView";
56
+ /** Author SQL. */
57
+ readonly query: string;
58
+ readonly columns: readonly {
59
+ readonly field: string;
60
+ readonly dataType: string;
61
+ readonly guarded: boolean;
62
+ }[];
63
+ /** The predicate binds a scope. A global view is not tenant. */
64
+ readonly tenant: boolean;
65
+ /** `global("reason")` on the view. Absent when the view is not global. */
66
+ readonly globalReason: string | undefined;
67
+ };
49
68
  /**
50
69
  * Scope value used to ask a tenancy object whether a table is tenant.
51
70
  *
@@ -56,13 +75,22 @@ export declare const tenantProbe = "00000000-0000-4000-8000-000000000001";
56
75
  /**
57
76
  * Reads every table in declaration order.
58
77
  *
59
- * Views and materialized views are in the query model too. They have no rows
60
- * of their own to insert, so they are left out.
78
+ * Views are read by {@link readViews}. They are not tables, so they are left out here.
61
79
  *
62
80
  * @param schema - Compiled schema
63
81
  * @returns One facts object per table
64
82
  */
65
83
  export declare function readFacts(schema: TestingSchema): readonly TableFacts[];
84
+ /**
85
+ * Reads every view and materialized view in model order.
86
+ *
87
+ * A global view is not tenant. The reason comes from the view declaration,
88
+ * which is not stored on the catalog.
89
+ *
90
+ * @param schema - Compiled schema
91
+ * @returns One facts object per view
92
+ */
93
+ export declare function readViews(schema: TestingSchema): readonly ViewFacts[];
66
94
  /**
67
95
  * A column the factory must set when the definition and the overrides omit it.
68
96
  *
@@ -72,7 +72,7 @@ export type Testing<S extends TestingSchema = TestingSchema> = {
72
72
  /**
73
73
  * Runs the cross-tenant check.
74
74
  *
75
- * @returns Checked tenant tables and skipped global tables
75
+ * @returns Checked tenant tables and views, and skipped global objects
76
76
  */
77
77
  isolation(): Promise<IsolationReport>;
78
78
  /** Closes the client and the pool. */
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  testing2
3
- } from "../../shared/g516zdsb.js";
4
- import"../../shared/8xsbagad.js";
5
- import"../../shared/8bwbwy8h.js";
3
+ } from "../../shared/8x72s86k.js";
4
+ import"../../shared/9s0ypp87.js";
5
+ import"../../shared/qq3899ht.js";
6
6
  export {
7
7
  testing2 as testing
8
8
  };