okmodel 0.3.0 → 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 (104) hide show
  1. package/README.md +182 -22
  2. package/dist/contracts/catalog/document.d.ts +6 -2
  3. package/dist/dialects/pg/ext/citext.js +1 -1
  4. package/dist/dialects/pg/ext/index.js +1 -1
  5. package/dist/dialects/pg/ext/pg-trgm.js +1 -1
  6. package/dist/dialects/pg/index.js +3 -3
  7. package/dist/dialects/pg/role/sql.d.ts +11 -4
  8. package/dist/dialects/pg/table.d.ts +17 -1
  9. package/dist/dialects/pg/view/index.js +1 -1
  10. package/dist/dialects/pg/view/scratch.d.ts +3 -2
  11. package/dist/runtime/catalog-cache.d.ts +17 -0
  12. package/dist/runtime/compat-words.d.ts +23 -0
  13. package/dist/runtime/drift-detail.d.ts +20 -4
  14. package/dist/runtime/drift.d.ts +4 -2
  15. package/dist/runtime/pg/bun.js +2 -2
  16. package/dist/runtime/pg/pg.js +2 -2
  17. package/dist/runtime/pg/pglite.js +2 -2
  18. package/dist/runtime/pg/postgresjs.js +2 -2
  19. package/dist/runtime/traits/index.js +5 -4
  20. package/dist/runtime/types.d.ts +2 -0
  21. package/dist/runtime/validate/index.js +2 -2
  22. package/dist/shared/2321avmn.js +418 -0
  23. package/dist/shared/{x6wysh83.js → 3p4b1fg0.js} +3 -5
  24. package/dist/shared/{wekze34d.js → 41kv2xdm.js} +74 -2
  25. package/dist/shared/{fs0ex00p.js → 4ef7mmqf.js} +2 -2
  26. package/dist/shared/5mtf6cj3.js +94 -0
  27. package/dist/shared/{999ade2q.js → 5vdm4sgp.js} +1 -1
  28. package/dist/shared/{qybh48nd.js → 6693j9rs.js} +1 -1
  29. package/dist/shared/{bqha5cg6.js → 6p5k20sm.js} +2 -2
  30. package/dist/shared/7nk68fj8.js +751 -0
  31. package/dist/shared/{ne02ng5c.js → 84ptn35q.js} +7 -4
  32. package/dist/shared/{t616ynfn.js → 8jb4ymh2.js} +59 -12
  33. package/dist/shared/{th14ygv2.js → 8ktwd3rh.js} +29 -1
  34. package/dist/shared/9yn6a2vd.js +11 -0
  35. package/dist/shared/ady1ms74.js +72 -0
  36. package/dist/shared/{scwxrpx2.js → c5g81ngb.js} +82 -15
  37. package/dist/shared/{6bazvqwm.js → fb00y8fp.js} +2 -2
  38. package/dist/shared/{jkc6ws1p.js → g7ez901n.js} +83 -2
  39. package/dist/shared/{kgnc8grt.js → gdh9mrtp.js} +1 -1
  40. package/dist/shared/hqjsxnz7.js +13 -0
  41. package/dist/shared/htnaj77v.js +17 -0
  42. package/dist/shared/{mt5wswjz.js → jgxzjzcz.js} +1 -1
  43. package/dist/shared/{6w5j844a.js → jsd07prz.js} +2 -2
  44. package/dist/shared/{m78s7898.js → kafaa5zv.js} +1 -1
  45. package/dist/shared/kj680nxv.js +19 -0
  46. package/dist/shared/{k34e0xf2.js → kkzwp3d2.js} +8 -8
  47. package/dist/shared/{2ssqrv66.js → n5y9zyj1.js} +1029 -174
  48. package/dist/shared/{ty3v3hfk.js → nxb42q7h.js} +2 -2
  49. package/dist/shared/{5nmdenb6.js → q6zsyh31.js} +1003 -40
  50. package/dist/shared/{fptpmvxp.js → q795afwa.js} +26 -21
  51. package/dist/shared/{3ekj9x92.js → qn0srwwq.js} +1 -1
  52. package/dist/shared/s4mb6a4b.js +545 -0
  53. package/dist/shared/{01yr20s5.js → sf8m6tfy.js} +2 -2
  54. package/dist/shared/{bryqk9k9.js → sr5ms7b3.js} +1 -1
  55. package/dist/shared/{meck53jv.js → tny2t3sm.js} +6 -5
  56. package/dist/shared/{p24x2334.js → v7tpnhnz.js} +1 -1
  57. package/dist/shared/{xw1d69vd.js → wqem4jed.js} +6 -6
  58. package/dist/shared/{49ypnfwt.js → xghkt7a9.js} +50 -28
  59. package/dist/shared/{7fvtrkwr.js → ycatkxmq.js} +1 -1
  60. package/dist/shared/ygem90rn.js +900 -0
  61. package/dist/shared/{1tfdfvhh.js → yjw02enp.js} +6 -4
  62. package/dist/shared/{ydaz1sv3.js → zg24x0hm.js} +1 -1
  63. package/dist/tooling/cli.js +46 -17
  64. package/dist/tooling/migrate/apply.d.ts +59 -5
  65. package/dist/tooling/migrate/backfill.d.ts +136 -0
  66. package/dist/tooling/migrate/check.d.ts +65 -0
  67. package/dist/tooling/migrate/classify.d.ts +108 -0
  68. package/dist/tooling/migrate/commands.d.ts +2 -2
  69. package/dist/tooling/migrate/config.d.ts +22 -0
  70. package/dist/tooling/migrate/doctor.d.ts +3 -0
  71. package/dist/tooling/migrate/drift.d.ts +18 -2
  72. package/dist/tooling/migrate/estimate.d.ts +50 -0
  73. package/dist/tooling/migrate/extensions.d.ts +2 -0
  74. package/dist/tooling/migrate/files.d.ts +12 -0
  75. package/dist/tooling/migrate/index.d.ts +3 -2
  76. package/dist/tooling/migrate/index.js +67 -1
  77. package/dist/tooling/migrate/lint-rules.d.ts +51 -0
  78. package/dist/tooling/migrate/lint.d.ts +71 -0
  79. package/dist/tooling/migrate/managed.d.ts +1 -1
  80. package/dist/tooling/migrate/plan.d.ts +64 -9
  81. package/dist/tooling/migrate/project.d.ts +42 -7
  82. package/dist/tooling/migrate/provision.d.ts +18 -0
  83. package/dist/tooling/migrate/reference.d.ts +40 -0
  84. package/dist/tooling/migrate/runner.d.ts +108 -0
  85. package/dist/tooling/migrate/safe.d.ts +77 -0
  86. package/dist/tooling/migrate/seed.d.ts +20 -0
  87. package/dist/tooling/migrate/snapshot.d.ts +78 -0
  88. package/dist/tooling/migrate/status.d.ts +25 -6
  89. package/dist/tooling/testing/factories.d.ts +117 -0
  90. package/dist/tooling/testing/facts.d.ts +72 -0
  91. package/dist/tooling/testing/index.d.ts +89 -0
  92. package/dist/tooling/testing/index.js +8 -0
  93. package/dist/tooling/testing/isolation.d.ts +30 -0
  94. package/dist/tooling/testing/random.d.ts +40 -0
  95. package/dist/tooling/testing/record.d.ts +38 -0
  96. package/package.json +6 -2
  97. package/dist/shared/dbjej1tw.js +0 -456
  98. package/dist/shared/g4f5kcx9.js +0 -37
  99. package/dist/shared/qbxzvwwc.js +0 -88
  100. package/dist/shared/zwmqxb0g.js +0 -79
  101. package/dist/shared/{xva8v7qk.js → awrsqrn7.js} +3 -3
  102. package/dist/shared/{75xkjv0t.js → fa9vt245.js} +0 -0
  103. package/dist/shared/{m9tv7xn7.js → radd3z4g.js} +3 -3
  104. /package/dist/shared/{0jc3ffzf.js → zvbjc949.js} +0 -0
@@ -1,19 +1,21 @@
1
1
  import {
2
2
  open2
3
- } from "./xw1d69vd.js";
3
+ } from "./wqem4jed.js";
4
4
  import"./n442h54c.js";
5
- import"./t616ynfn.js";
5
+ import"./8jb4ymh2.js";
6
6
  import {
7
7
  assertRoleHealth
8
8
  } from "./hcmdtwgh.js";
9
9
  import {
10
10
  errorDoc,
11
+ assertTargetPolicy,
11
12
  selectTarget,
12
13
  openProject
13
- } from "./5nmdenb6.js";
14
+ } from "./q6zsyh31.js";
14
15
  import {
15
16
  omitManagedObjects
16
- } from "./2ssqrv66.js";
17
+ } from "./n5y9zyj1.js";
18
+ import"./41kv2xdm.js";
17
19
  import {
18
20
  OkmError2
19
21
  } from "./52szzakm.js";
@@ -36,6 +38,7 @@ ${doc.fix}
36
38
  const objects = omitManagedObjects(opened.built.catalog).objects;
37
39
  if (opened.config.roles !== undefined) {
38
40
  const target = selectTarget(opened.config, flags?.target);
41
+ assertTargetPolicy(target, "check", flags?.allowProtected ?? false);
39
42
  const pool = open2({ url: target.url, max: 1 });
40
43
  try {
41
44
  await assertRoleHealth(pool, opened.config.roles, objects);
@@ -9,16 +9,14 @@ import {
9
9
  } from "./y005b761.js";
10
10
  import {
11
11
  extensionObject
12
- } from "./th14ygv2.js";
12
+ } from "./8ktwd3rh.js";
13
13
  import {
14
14
  dependencyOrder,
15
- catalog
16
- } from "./jkc6ws1p.js";
17
- import {
15
+ catalog,
18
16
  enumType,
19
17
  domainType,
20
18
  isDomain
21
- } from "./qbxzvwwc.js";
19
+ } from "./g7ez901n.js";
22
20
  import {
23
21
  canonicalJson,
24
22
  staticNamespace2,
@@ -46,7 +44,8 @@ import {
46
44
  } from "./4e4c6vq2.js";
47
45
  import {
48
46
  sha2562,
49
- assertIdentifier2
47
+ assertIdentifier2,
48
+ fitIdentifier2
50
49
  } from "./nfzc5z0w.js";
51
50
  import {
52
51
  catalogError2
@@ -201,7 +200,7 @@ function renameTable2(source, change) {
201
200
  if (!found) {
202
201
  catalogError2("OKM1020", `Table ${change.from} is not in the catalog.`);
203
202
  }
204
- const next = source.objects.map((object) => rewriteTableName(object, change, fromKey, toIdentity));
203
+ const next = source.objects.map((object) => rewriteTableName(object, change, fromKey, toIdentity, source));
205
204
  return catalog(next);
206
205
  }
207
206
  var serializedText = new WeakMap;
@@ -473,8 +472,8 @@ function rewriteExpr(text, change, active) {
473
472
  return text;
474
473
  return replaceIdentifier(text, change.from, change.to);
475
474
  }
476
- function rewriteTableName(object, change, fromKey, toIdentity) {
477
- const dependencies = retarget(object.dependencies, fromKey, toIdentity);
475
+ function rewriteTableName(object, change, fromKey, toIdentity, source) {
476
+ const dependencies = retarget(object.dependencies, fromKey, toIdentity, change);
478
477
  const named = object.kind === "table" && object.identity.name === change.from && sameNamespace2(object.identity.namespace, change.namespace);
479
478
  if (object.kind === "table") {
480
479
  return table3({
@@ -579,9 +578,19 @@ function rewriteTableName(object, change, fromKey, toIdentity) {
579
578
  });
580
579
  return rewriteView(object, dependencies, rewriteExpr(object.definition.query, { from: change.from, to: change.to }, local));
581
580
  }
582
- if (object.kind === "role" || object.kind === "grant" || object.kind === "defaultPrivilege") {
581
+ if (object.kind === "role" || object.kind === "defaultPrivilege") {
583
582
  return retargeted(object, dependencies);
584
583
  }
584
+ if (object.kind === "grant") {
585
+ return grantObject({
586
+ role: object.identity.role,
587
+ object: movedGrantRef(object.identity.object, change, source),
588
+ privilege: object.identity.privilege,
589
+ owner: object.owner,
590
+ provenance: object.provenance,
591
+ dependencies
592
+ });
593
+ }
585
594
  if (object.kind === "trigger") {
586
595
  const parent = movedParent(object.identity.parent, change);
587
596
  const local = parent.name !== object.identity.parent.name;
@@ -644,8 +653,46 @@ function rewriteSequence(object, dependencies) {
644
653
  function renameList(names, from, to) {
645
654
  return names.map((name) => name === from ? to : name);
646
655
  }
647
- function retarget(edges, fromKey, to) {
648
- return edges.map((edge) => identityKey2(edge.target) === fromKey ? to : edge.target);
656
+ function retarget(edges, fromKey, to, change) {
657
+ return edges.map((edge) => {
658
+ if (identityKey2(edge.target) === fromKey)
659
+ return to;
660
+ if (change === undefined)
661
+ return edge.target;
662
+ return movedChild(edge.target, change);
663
+ });
664
+ }
665
+ function movedChild(target, change) {
666
+ if (!("parent" in target))
667
+ return target;
668
+ if (target.parent.name !== change.from || !sameNamespace2(target.parent.namespace, change.namespace)) {
669
+ return target;
670
+ }
671
+ return { ...target, parent: { namespace: target.parent.namespace, name: change.to } };
672
+ }
673
+ function movedGrantRef(object, change, source) {
674
+ if (object.kind === "table" && object.name === change.from && sameNamespace2(object.namespace, change.namespace)) {
675
+ return { ...object, name: change.to };
676
+ }
677
+ if (object.kind !== "sequence")
678
+ return object;
679
+ const next = renamedIdentitySequence(source, change, object.name);
680
+ if (next === undefined)
681
+ return object;
682
+ return { ...object, name: next };
683
+ }
684
+ function renamedIdentitySequence(source, change, sequenceName) {
685
+ for (const object of source.objects) {
686
+ if (object.kind !== "column" || object.definition.identity === undefined)
687
+ continue;
688
+ if (object.identity.parent.name !== change.from || !sameNamespace2(object.identity.parent.namespace, change.namespace)) {
689
+ continue;
690
+ }
691
+ if (fitIdentifier2(`${change.from}_${object.identity.name}_seq`) !== sequenceName)
692
+ continue;
693
+ return fitIdentifier2(`${change.to}_${object.identity.name}_seq`);
694
+ }
695
+ return;
649
696
  }
650
697
  function dependencyJson(edges) {
651
698
  const indexed = edges.map((edge) => ({
@@ -50,11 +50,39 @@ function sameExtensionDefinition(before, after) {
50
50
  return false;
51
51
  return sameVersion(before.definition.version, after.definition.version);
52
52
  }
53
+ function alignExtensionVersions(source, other) {
54
+ const loose = looseNames(source, other);
55
+ if (loose.size === 0)
56
+ return source;
57
+ let changed = false;
58
+ const objects = source.objects.map((object) => {
59
+ if (object.kind !== "extension" || object.definition.version === undefined)
60
+ return object;
61
+ if (!loose.has(object.identity.name))
62
+ return object;
63
+ changed = true;
64
+ const { version: _version, ...definition } = object.definition;
65
+ return { ...object, definition };
66
+ });
67
+ return changed ? { version: source.version, objects } : source;
68
+ }
53
69
  function sameVersion(before, after) {
54
70
  if (!isExactVersion(after) || !isExactVersion(before))
55
71
  return true;
56
72
  return before === after;
57
73
  }
74
+ function looseNames(source, other) {
75
+ const names = new Set;
76
+ for (const catalog of [source, other]) {
77
+ for (const object of catalog.objects) {
78
+ if (object.kind !== "extension")
79
+ continue;
80
+ if (!isExactVersion(object.definition.version))
81
+ names.add(object.identity.name);
82
+ }
83
+ }
84
+ return names;
85
+ }
58
86
  function assertVersion(version) {
59
87
  if (version === undefined)
60
88
  return;
@@ -66,4 +94,4 @@ function parts(version) {
66
94
  return version.split(".").map((part) => Number(part));
67
95
  }
68
96
 
69
- export { extensionObject, isExactVersion, compareExtensionVersions, sameExtensionDefinition };
97
+ export { extensionObject, isExactVersion, compareExtensionVersions, sameExtensionDefinition, alignExtensionVersions };
@@ -0,0 +1,11 @@
1
+ // src/runtime/compat-words.ts
2
+ var CURRENT = "current";
3
+ var BEHIND_EXPAND = "behind by expand";
4
+ var BEHIND_CONTRACT = "behind by contract";
5
+ var AHEAD_EXPAND = "ahead by expand";
6
+ var AHEAD_CONTRACT = "ahead by contract";
7
+ function failedAt(step) {
8
+ return `failed at step ${String(step)} (resume with okm migrate apply)`;
9
+ }
10
+
11
+ export { CURRENT, BEHIND_EXPAND, BEHIND_CONTRACT, AHEAD_EXPAND, AHEAD_CONTRACT, failedAt };
@@ -0,0 +1,72 @@
1
+ import {
2
+ open2
3
+ } from "./wqem4jed.js";
4
+ import"./8jb4ymh2.js";
5
+ import {
6
+ createdOf,
7
+ testing2
8
+ } from "./ygem90rn.js";
9
+ import"./n442h54c.js";
10
+ import {
11
+ assertTargetPolicy,
12
+ assertDirectConnection,
13
+ selectTarget,
14
+ loadConfig,
15
+ loadSchema
16
+ } from "./q6zsyh31.js";
17
+ import"./n5y9zyj1.js";
18
+ import"./41kv2xdm.js";
19
+ import {
20
+ OkmError2
21
+ } from "./52szzakm.js";
22
+
23
+ // src/tooling/migrate/seed.ts
24
+ import { existsSync } from "node:fs";
25
+ import { join } from "node:path";
26
+ import { pathToFileURL } from "node:url";
27
+ async function seedProject(cwd, file, flags) {
28
+ const config = await loadConfig(cwd);
29
+ const target = selectTarget(config, flags.target);
30
+ assertTargetPolicy(target, "seed", flags.allowProtected);
31
+ const path = join(cwd, file);
32
+ if (!existsSync(path)) {
33
+ throw new OkmError2("invalid", `Seed file ${file} was not found.`, {
34
+ fix: { summary: "Pass okm seed <file> a module in this project." }
35
+ });
36
+ }
37
+ assertDirectConnection(target.url, flags.allowPooler || config.allowPooler === true);
38
+ const schema = await loadSchema(cwd, config.schema);
39
+ const pool = open2({ url: target.url, max: 1 });
40
+ const harness = await testing2(schema, { driver: pool, migrate: false });
41
+ try {
42
+ const imported = await import(`${pathToFileURL(path).href}?okm=${Date.now()}`);
43
+ const seed = isRecord(imported) ? imported.default : undefined;
44
+ if (typeof seed !== "function") {
45
+ throw new OkmError2("invalid", `${file} must default-export a function.`, {
46
+ fix: { summary: "export default async function seed(t) { ... }." }
47
+ });
48
+ }
49
+ await seed(harness);
50
+ return formatCreated(target.name, createdOf(harness));
51
+ } finally {
52
+ await harness.close();
53
+ }
54
+ }
55
+ function formatCreated(target, counts) {
56
+ const lines = [`target ${target}`];
57
+ if (counts.length === 0)
58
+ lines.push("created nothing");
59
+ else {
60
+ for (const item of counts)
61
+ lines.push(`${item.table} ${String(item.count)}`);
62
+ }
63
+ return `${lines.join(`
64
+ `)}
65
+ `;
66
+ }
67
+ function isRecord(value) {
68
+ return typeof value === "object" && value !== null;
69
+ }
70
+ export {
71
+ seedProject
72
+ };
@@ -1,17 +1,28 @@
1
1
  import {
2
2
  open2
3
- } from "./xw1d69vd.js";
4
- import"./t616ynfn.js";
3
+ } from "./wqem4jed.js";
4
+ import"./8jb4ymh2.js";
5
5
  import"./n442h54c.js";
6
6
  import {
7
- loadMigrations
8
- } from "./g4f5kcx9.js";
7
+ CURRENT,
8
+ BEHIND_EXPAND,
9
+ BEHIND_CONTRACT,
10
+ AHEAD_EXPAND,
11
+ AHEAD_CONTRACT,
12
+ failedAt
13
+ } from "./9yn6a2vd.js";
14
+ import {
15
+ loadMigrations,
16
+ expandProvisioned,
17
+ provisionHistoryIsKnown
18
+ } from "./7nk68fj8.js";
9
19
  import {
10
20
  assertTargetPolicy,
11
21
  listTargets,
12
22
  loadConfig
13
- } from "./5nmdenb6.js";
14
- import"./2ssqrv66.js";
23
+ } from "./q6zsyh31.js";
24
+ import"./n5y9zyj1.js";
25
+ import"./41kv2xdm.js";
15
26
  import {
16
27
  OkmError2
17
28
  } from "./52szzakm.js";
@@ -19,8 +30,8 @@ import {
19
30
  // src/tooling/migrate/status.ts
20
31
  import { join } from "node:path";
21
32
  function describeStatus(migrations, history, recorded) {
22
- const done = new Set(history.map((row) => `${row.migrationId}:${String(row.stepIndex)}`));
23
33
  const known = new Set(migrations.map((migration) => migration.id));
34
+ const done = expandProvisioned(migrations, new Set(history.map((row) => `${row.migrationId}:${String(row.stepIndex)}`)), history.map((row) => row.migrationId));
24
35
  for (const migration of migrations) {
25
36
  let have = 0;
26
37
  let missing = -1;
@@ -34,33 +45,33 @@ function describeStatus(migrations, history, recorded) {
34
45
  return {
35
46
  version: migration.id,
36
47
  catalogHash: recorded?.hash ?? "-",
37
- state: `failed at step ${String(missing)}`
48
+ state: failedAt(missing)
38
49
  };
39
50
  }
40
51
  }
41
- const extra = history.some((row) => !known.has(row.migrationId));
42
52
  const pending = migrations.filter((migration) => migration.steps.some((_, index) => !done.has(`${migration.id}:${String(index)}`)));
43
53
  const finished = lastComplete(migrations, done);
44
54
  const version = recorded?.version ?? finished ?? "-";
45
55
  const catalogHash = recorded?.hash ?? "-";
56
+ const extra = history.some((row) => !provisionHistoryIsKnown(row.migrationId, known));
46
57
  if (extra)
47
- return { version, catalogHash, state: "ahead" };
58
+ return { version, catalogHash, state: aheadState(history, known) };
48
59
  if (pending.length > 0) {
49
60
  const contract = pending.some((migration) => migration.steps.some((step) => step.class !== "expand"));
50
61
  return {
51
62
  version,
52
63
  catalogHash,
53
- state: contract ? "behind by contract" : "behind by expand"
64
+ state: contract ? BEHIND_CONTRACT : BEHIND_EXPAND
54
65
  };
55
66
  }
56
67
  const last = migrations.at(-1);
57
68
  if (last !== undefined && recorded !== undefined && recorded.hash !== last.catalogHash) {
58
- return { version, catalogHash, state: "ahead" };
69
+ return { version, catalogHash, state: aheadState(history, known) };
59
70
  }
60
71
  return {
61
72
  version: recorded?.version ?? last?.id ?? "-",
62
73
  catalogHash: recorded?.hash ?? last?.catalogHash ?? "-",
63
- state: "current"
74
+ state: CURRENT
64
75
  };
65
76
  }
66
77
  async function readTargetStatus(input) {
@@ -82,7 +93,8 @@ async function readTargetStatus(input) {
82
93
  version: described.version,
83
94
  catalogHash: described.catalogHash,
84
95
  state: described.state,
85
- protected: input.protected
96
+ protected: input.protected,
97
+ backfills: await readBackfills(pool)
86
98
  };
87
99
  } finally {
88
100
  await pool.close();
@@ -105,8 +117,17 @@ async function statusProject(cwd, flags) {
105
117
  }
106
118
  function formatStatus(rows) {
107
119
  const lines = ["target\tversion\tcatalog\tstate\tprotected"];
120
+ const backfills = [];
108
121
  for (const row of rows) {
109
122
  lines.push([row.target, row.version, row.catalogHash, row.state, row.protected ? "true" : "false"].join("\t"));
123
+ backfills.push(...row.backfills);
124
+ }
125
+ if (backfills.length > 0) {
126
+ lines.push("backfill");
127
+ lines.push("migration\tstep\trows\tkey\tstate");
128
+ for (const item of backfills) {
129
+ lines.push([item.migrationId, String(item.stepIndex), item.rows, item.lastKey, item.state].join("\t"));
130
+ }
110
131
  }
111
132
  return `${lines.join(`
112
133
  `)}
@@ -160,7 +181,33 @@ async function readHistory(pool) {
160
181
  rows.push({
161
182
  migrationId,
162
183
  stepIndex: Number(step),
163
- class: stepClass === "contract" || stepClass === "unclassified" ? stepClass : "expand"
184
+ class: storedClass(stepClass)
185
+ });
186
+ }
187
+ return rows;
188
+ }
189
+ async function readBackfills(pool) {
190
+ const present = await pool.execute("select to_regclass('okm_backfill') is not null");
191
+ if (!wireTrue(present.rows[0]?.[0] ?? null))
192
+ return [];
193
+ const result = await pool.execute(`select migration_id, step_index::text, rows_touched::text, last_key, state
194
+ from okm_backfill
195
+ where state <> 'done'
196
+ order by migration_id, step_index`);
197
+ const rows = [];
198
+ for (const row of result.rows) {
199
+ const migrationId = row[0];
200
+ const step = row[1];
201
+ const touched = row[2];
202
+ const state = row[4];
203
+ if (migrationId == null || step == null || touched == null || state == null)
204
+ continue;
205
+ rows.push({
206
+ migrationId,
207
+ stepIndex: Number(step),
208
+ rows: touched,
209
+ lastKey: row[3] ?? "-",
210
+ state
164
211
  });
165
212
  }
166
213
  return rows;
@@ -168,6 +215,26 @@ async function readHistory(pool) {
168
215
  function wireTrue(value) {
169
216
  return value === "t" || value === "true";
170
217
  }
218
+ function aheadState(history, known) {
219
+ let sawExtra = false;
220
+ let expandOnly = true;
221
+ for (const row of history) {
222
+ if (known.has(row.migrationId))
223
+ continue;
224
+ sawExtra = true;
225
+ if (row.class !== "expand")
226
+ expandOnly = false;
227
+ }
228
+ if (!sawExtra) {
229
+ expandOnly = history.length > 0 && history.every((row) => row.class === "expand");
230
+ }
231
+ return expandOnly ? AHEAD_EXPAND : AHEAD_CONTRACT;
232
+ }
233
+ function storedClass(value) {
234
+ if (value === "expand" || value === "contract" || value === "unclassified")
235
+ return value;
236
+ return "unclassified";
237
+ }
171
238
  export {
172
239
  statusProject
173
240
  };
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  send,
3
3
  connectClient
4
- } from "./bqha5cg6.js";
4
+ } from "./6p5k20sm.js";
5
5
  import {
6
6
  isConnectionLost
7
7
  } from "./tbszx8mz.js";
8
8
  import {
9
9
  WireSession
10
- } from "./0jc3ffzf.js";
10
+ } from "./zvbjc949.js";
11
11
  import {
12
12
  rejectClosed,
13
13
  runCall
@@ -1,11 +1,18 @@
1
1
  import {
2
+ assertNamespace2,
2
3
  identityKey2,
3
4
  identityLabel2,
4
- compareText
5
+ normaliseEdges,
6
+ compareText,
7
+ assertProvenance
5
8
  } from "./t4tbsy9h.js";
6
9
  import {
7
10
  CATALOG_VERSION2
8
11
  } from "./4e4c6vq2.js";
12
+ import {
13
+ assertIdentifier2,
14
+ assertStoredText
15
+ } from "./nfzc5z0w.js";
9
16
  import {
10
17
  catalogError2
11
18
  } from "./52szzakm.js";
@@ -135,4 +142,78 @@ function assertForeignKeys(objects, keys) {
135
142
  }
136
143
  }
137
144
 
138
- export { dependencyOrder, catalog };
145
+ // src/contracts/catalog/enum.ts
146
+ function enumType(input) {
147
+ assertNamespace2(input.namespace);
148
+ assertIdentifier2(input.name, "type");
149
+ assertProvenance(input.provenance);
150
+ if (input.labels.length === 0) {
151
+ catalogError2("OKM1020", `Enum ${input.name} must list at least one label.`);
152
+ }
153
+ const labels = [];
154
+ const seen = new Set;
155
+ for (const label of input.labels) {
156
+ assertStoredText(label, "enum label");
157
+ if (label.length === 0) {
158
+ catalogError2("OKM1020", `Enum ${input.name} has an empty label.`);
159
+ }
160
+ if (seen.has(label)) {
161
+ catalogError2("OKM1020", `Enum ${input.name} repeats label ${label}.`);
162
+ }
163
+ seen.add(label);
164
+ labels.push(label);
165
+ }
166
+ const identity = {
167
+ kind: "type",
168
+ namespace: input.namespace,
169
+ name: input.name
170
+ };
171
+ return {
172
+ kind: "type",
173
+ identity,
174
+ owner: input.owner ?? "managed",
175
+ definition: { labels },
176
+ dependencies: normaliseEdges(input.dependencies ?? []),
177
+ provenance: input.provenance
178
+ };
179
+ }
180
+ function domainType(input) {
181
+ assertNamespace2(input.namespace);
182
+ assertIdentifier2(input.name, "type");
183
+ assertProvenance(input.provenance);
184
+ if (input.base.length === 0) {
185
+ catalogError2("OKM1020", `Domain ${input.name} needs a base type.`);
186
+ }
187
+ assertStoredText(input.check, "domain check");
188
+ if (input.check.length === 0) {
189
+ catalogError2("OKM1020", `Domain ${input.name} needs a check.`);
190
+ }
191
+ const identity = {
192
+ kind: "type",
193
+ namespace: input.namespace,
194
+ name: input.name
195
+ };
196
+ const definition = { base: input.base, check: input.check };
197
+ return {
198
+ kind: "type",
199
+ identity,
200
+ owner: input.owner ?? "managed",
201
+ definition,
202
+ dependencies: normaliseEdges(input.dependencies ?? []),
203
+ provenance: input.provenance
204
+ };
205
+ }
206
+ function isDomain(definition) {
207
+ return "base" in definition;
208
+ }
209
+ function sameEnumLabels(left, right) {
210
+ if (left.length !== right.length)
211
+ return false;
212
+ for (let index = 0;index < left.length; index += 1) {
213
+ if (left[index] !== right[index])
214
+ return false;
215
+ }
216
+ return true;
217
+ }
218
+
219
+ export { dependencyOrder, catalog, enumType, domainType, isDomain, sameEnumLabels };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  open3
3
- } from "./75xkjv0t.js";
3
+ } from "./fa9vt245.js";
4
4
  import {
5
5
  PGLITE_CAPABILITIES,
6
6
  readCapability2,
@@ -0,0 +1,13 @@
1
+ import {
2
+ assertAuthorDrift2,
3
+ catalogQuery2,
4
+ managedRoleOptions2
5
+ } from "./xghkt7a9.js";
6
+ import"./n5y9zyj1.js";
7
+ import"./41kv2xdm.js";
8
+ import"./8jb4ymh2.js";
9
+ export {
10
+ assertAuthorDrift2 as assertAuthorDrift,
11
+ catalogQuery2 as catalogQuery,
12
+ managedRoleOptions2 as managedRoleOptions
13
+ };
@@ -0,0 +1,17 @@
1
+ // src/runtime/catalog-cache.ts
2
+ var inflight = new Map;
3
+ function loadOnce(key, load) {
4
+ const found = inflight.get(key);
5
+ if (found !== undefined)
6
+ return found;
7
+ const pending = Promise.resolve().then(load);
8
+ inflight.set(key, pending);
9
+ pending.catch(() => {
10
+ if (inflight.get(key) === pending)
11
+ inflight.delete(key);
12
+ });
13
+ return pending;
14
+ }
15
+ export {
16
+ loadOnce
17
+ };
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  withHttp,
6
6
  settleCall
7
- } from "./k34e0xf2.js";
7
+ } from "./kkzwp3d2.js";
8
8
  import {
9
9
  rejectKeys,
10
10
  isRecord,
@@ -60,6 +60,7 @@ var TABLE_KNOWN = new Set([
60
60
  "omitDefaults",
61
61
  "presets",
62
62
  "primaryKey",
63
+ "reference",
63
64
  "relations",
64
65
  "renamedFrom",
65
66
  "sqlName",
@@ -71,8 +72,7 @@ var TABLE_KNOWN = new Set([
71
72
  ]);
72
73
  var TABLE_LATER = {
73
74
  computed: "later",
74
- policies: "later",
75
- reference: "0.4"
75
+ policies: "later"
76
76
  };
77
77
  function table2(name, columns, options) {
78
78
  const source = callerLocation(1);
@@ -5,7 +5,7 @@ import {
5
5
  openClient,
6
6
  withHttp,
7
7
  settleCall
8
- } from "./k34e0xf2.js";
8
+ } from "./kkzwp3d2.js";
9
9
  import {
10
10
  rejectKeys,
11
11
  isRecord,
@@ -0,0 +1,19 @@
1
+ import {
2
+ applyTarget2,
3
+ applyProject2,
4
+ pushProject2,
5
+ backfillTiming2
6
+ } from "./s4mb6a4b.js";
7
+ import"./7nk68fj8.js";
8
+ import"./q6zsyh31.js";
9
+ import"./n5y9zyj1.js";
10
+ import"./n442h54c.js";
11
+ import"./41kv2xdm.js";
12
+ import"./8jb4ymh2.js";
13
+ import"./wqem4jed.js";
14
+ export {
15
+ applyProject2 as applyProject,
16
+ applyTarget2 as applyTarget,
17
+ backfillTiming2 as backfillTiming,
18
+ pushProject2 as pushProject
19
+ };