okmodel 0.5.0 → 0.5.1

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 (80) hide show
  1. package/README.md +9 -6
  2. package/dist/dialects/pg/filter-parse.d.ts +8 -2
  3. package/dist/dialects/pg/fn/index.js +8 -0
  4. package/dist/dialects/pg/index.js +8 -2
  5. package/dist/dialects/pg/ops/or.d.ts +1 -1
  6. package/dist/dialects/pg/temporal.d.ts +5 -76
  7. package/dist/dialects/pg/view/index.js +1 -1
  8. package/dist/runtime/client.d.ts +6 -1
  9. package/dist/runtime/conflict.d.ts +2 -1
  10. package/dist/runtime/pg/bun.js +5 -5
  11. package/dist/runtime/pg/pg.js +5 -5
  12. package/dist/runtime/pg/pglite.js +5 -5
  13. package/dist/runtime/pg/postgresjs.js +5 -5
  14. package/dist/runtime/plan.d.ts +13 -0
  15. package/dist/runtime/tenancy/index.js +1 -1
  16. package/dist/runtime/traits/index.js +4 -4
  17. package/dist/runtime/validate/index.js +3 -3
  18. package/dist/shared/{ckjbyrt9.js → 0t22wwh1.js} +5 -9
  19. package/dist/shared/{jdgqqx31.js → 2pqx26h8.js} +23 -8
  20. package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
  21. package/dist/shared/{9tgmr1gh.js → 32zqc1dw.js} +18 -7
  22. package/dist/shared/{47xzb9fc.js → 47x8x9ze.js} +1 -1
  23. package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
  24. package/dist/shared/{s6gqfn5j.js → 51mvgj8w.js} +27 -3
  25. package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
  26. package/dist/shared/{3p4b1fg0.js → 5enne4ma.js} +1 -1
  27. package/dist/shared/{bzsjtd7c.js → 6mnp99hx.js} +7 -6
  28. package/dist/shared/{ykv7wh5b.js → 6qd27mx0.js} +2 -2
  29. package/dist/shared/{h4nqvjkp.js → 6rk2gen5.js} +2 -2
  30. package/dist/shared/{r73pt594.js → 7zwwyh0v.js} +32 -10
  31. package/dist/shared/{fa9vt245.js → 87jvvhnt.js} +2 -2
  32. package/dist/shared/{c8aghh05.js → 8rsy3hxz.js} +7 -1
  33. package/dist/shared/{6p5k20sm.js → 96qcj21c.js} +3 -3
  34. package/dist/shared/{xsq7v6wp.js → aj2qxyhj.js} +2 -2
  35. package/dist/shared/{5vdm4sgp.js → caw8b9d8.js} +4 -4
  36. package/dist/shared/{xhh1srw3.js → cctw17hj.js} +23 -8
  37. package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
  38. package/dist/shared/{tq9afp8d.js → cr74e96e.js} +1 -1
  39. package/dist/shared/{b066g80h.js → efmy2gcg.js} +2 -2
  40. package/dist/shared/{gdh9mrtp.js → f5cw5q5r.js} +2 -2
  41. package/dist/shared/{ayz64vzj.js → ga3sfy93.js} +357 -3
  42. package/dist/shared/{qgvt8nx1.js → gbtcc2d7.js} +44 -3
  43. package/dist/shared/{kdkxh3kb.js → gde73es6.js} +5 -7
  44. package/dist/shared/{bt9sfq0z.js → hf22e0wt.js} +51 -17
  45. package/dist/shared/{990a291j.js → janx09m5.js} +6 -5
  46. package/dist/shared/{4ef7mmqf.js → jwtxdj06.js} +5 -5
  47. package/dist/shared/{sf8m6tfy.js → k8v5v5wj.js} +3 -3
  48. package/dist/shared/{r6pj4fq2.js → kfjp0gck.js} +2 -2
  49. package/dist/shared/{wqem4jed.js → krxx77g0.js} +7 -7
  50. package/dist/shared/{h8ne16fq.js → nkaxvt9q.js} +1 -1
  51. package/dist/shared/{jsd07prz.js → ntneex2a.js} +1 -1
  52. package/dist/shared/{nkc9sfqb.js → nxdp2ze3.js} +40 -4
  53. package/dist/shared/{tbszx8mz.js → pf0d8f77.js} +1 -1
  54. package/dist/shared/{qy9rdq0g.js → ps7a6h14.js} +6 -6
  55. package/dist/shared/{rasvvzgz.js → q58y17hy.js} +5 -5
  56. package/dist/shared/{p7pfx624.js → qm7cdtvr.js} +22 -9
  57. package/dist/shared/{xdpj5kkm.js → qv2ydck9.js} +149 -209
  58. package/dist/shared/{k7cc9nzk.js → r2sp8hrc.js} +6 -3
  59. package/dist/shared/{fb00y8fp.js → r8x5qhd7.js} +5 -5
  60. package/dist/shared/{fyqkk1m0.js → sdc7r2ww.js} +6 -5
  61. package/dist/shared/{wxn4b7pp.js → sycnx8gt.js} +42 -5
  62. package/dist/shared/{dxjp14rk.js → tm5ab8we.js} +2 -2
  63. package/dist/shared/{g516zdsb.js → v36c5g6p.js} +78 -19
  64. package/dist/shared/wq9n6pwr.js +139 -0
  65. package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
  66. package/dist/shared/{922qbgnw.js → zqrnhwsq.js} +3 -3
  67. package/dist/tooling/cli.js +68 -21
  68. package/dist/tooling/migrate/commands.d.ts +1 -2
  69. package/dist/tooling/migrate/drift.d.ts +1 -1
  70. package/dist/tooling/migrate/estimate.d.ts +14 -1
  71. package/dist/tooling/migrate/index.js +8 -7
  72. package/dist/tooling/migrate/lint.d.ts +3 -3
  73. package/dist/tooling/migrate/plan.d.ts +7 -3
  74. package/dist/tooling/migrate/policy.d.ts +11 -0
  75. package/dist/tooling/migrate/sql-lex.d.ts +22 -0
  76. package/dist/tooling/testing/factories.d.ts +10 -0
  77. package/dist/tooling/testing/index.js +1 -1
  78. package/dist/tooling/testing/isolation.d.ts +4 -3
  79. package/package.json +6 -5
  80. package/dist/shared/0v1g424p.js +0 -26
@@ -6,8 +6,10 @@ import {
6
6
  introspectSchema
7
7
  } from "./fancm5pt.js";
8
8
  import {
9
- planMigration
10
- } from "./ayz64vzj.js";
9
+ planMigration,
10
+ assertDirectConnection,
11
+ assertStableBackend
12
+ } from "./ga3sfy93.js";
11
13
  import {
12
14
  quoteIdent,
13
15
  createObjectSql
@@ -49,8 +51,11 @@ async function sealViews(runner, source) {
49
51
  return source;
50
52
  await dropScratch(runner);
51
53
  await runner.query(`create schema ${quoteIdent(SCRATCH)}`);
54
+ const previous = await runner.query(`select current_setting('search_path') as path`);
55
+ const saved = cellText(previous[0]?.path);
56
+ const path = await scratchSearchPath(runner, source);
52
57
  try {
53
- await runner.query(`set search_path to ${quoteIdent(SCRATCH)}`);
58
+ await runner.query(`select set_config('search_path', $1, false)`, [path]);
54
59
  await createObjects(runner, source, "type");
55
60
  for (const object of source.objects) {
56
61
  if (object.kind !== "table")
@@ -99,10 +104,37 @@ async function sealViews(runner, source) {
99
104
  });
100
105
  return catalog(next);
101
106
  } finally {
102
- await runner.query("set search_path to public");
107
+ await runner.query(`select set_config('search_path', $1, false)`, [saved]).catch(() => {
108
+ return;
109
+ });
103
110
  await dropScratch(runner);
104
111
  }
105
112
  }
113
+ async function scratchSearchPath(runner, source) {
114
+ const names = new Set;
115
+ for (const object of source.objects) {
116
+ if (object.kind !== "column")
117
+ continue;
118
+ const bare = object.definition.dataType.split(".").pop()?.replace(/\(.*$/, "").replace(/\[\]$/, "").trim().toLowerCase();
119
+ if (bare !== undefined && bare.length > 0)
120
+ names.add(bare);
121
+ }
122
+ const extra = [];
123
+ if (names.size > 0) {
124
+ const rows = await runner.query(`select distinct n.nspname as schema
125
+ from pg_type t
126
+ join pg_namespace n on n.oid = t.typnamespace
127
+ where t.typname = any(string_to_array($1, E'\\x1f'))
128
+ and n.nspname not in ('pg_catalog', 'information_schema', 'pg_toast')`, [[...names].join("\x1F")]);
129
+ for (const row of rows) {
130
+ const schema = cellText(row.schema);
131
+ if (schema.length > 0 && schema !== "public" && schema !== SCRATCH)
132
+ extra.push(schema);
133
+ }
134
+ }
135
+ extra.sort();
136
+ return [SCRATCH, ...extra, "public"].map((name) => quoteIdent(name)).join(", ");
137
+ }
106
138
  async function createObjects(runner, source, kind) {
107
139
  for (const object of source.objects) {
108
140
  if (object.kind !== kind)
@@ -193,7 +225,8 @@ async function dropScratch(runner) {
193
225
  }
194
226
 
195
227
  // src/tooling/migrate/drift.ts
196
- async function assertAuthorDrift2(url, author, roles) {
228
+ async function assertAuthorDrift2(url, author, roles, allowPooler = false) {
229
+ assertDirectConnection(url, allowPooler);
197
230
  const sql = postgres(url, {
198
231
  max: 1,
199
232
  connect_timeout: 5,
@@ -201,6 +234,10 @@ async function assertAuthorDrift2(url, author, roles) {
201
234
  onnotice: () => {}
202
235
  });
203
236
  try {
237
+ await assertStableBackend(async () => {
238
+ const rows = await sql`select pg_backend_pid()::text as pid`;
239
+ return rows[0]?.pid ?? "";
240
+ }, allowPooler);
204
241
  const present = await sql`
205
242
  select to_regclass('public.okm_meta')::text as reg
206
243
  `;
@@ -3,10 +3,10 @@ import {
3
3
  readHandle,
4
4
  readCall,
5
5
  attachHttp
6
- } from "./bt9sfq0z.js";
6
+ } from "./hf22e0wt.js";
7
7
  import {
8
8
  fail
9
- } from "./qgvt8nx1.js";
9
+ } from "./gbtcc2d7.js";
10
10
  import {
11
11
  OkmError2,
12
12
  throwNamed2
@@ -3,7 +3,7 @@ import {
3
3
  } from "./8xsbagad.js";
4
4
  import {
5
5
  createClient
6
- } from "./bt9sfq0z.js";
6
+ } from "./hf22e0wt.js";
7
7
  import {
8
8
  OkmError2
9
9
  } from "./52szzakm.js";
@@ -463,6 +463,9 @@ function fieldOf(facts, sql) {
463
463
  function cacheKey(table, tenant) {
464
464
  return `${tenant ?? "*"}:${table}`;
465
465
  }
466
+ function forgetCached(host, table, tenant) {
467
+ host.cache.delete(cacheKey(table, tenant));
468
+ }
466
469
  function refOf(value) {
467
470
  if (typeof value !== "object" || value === null || !(REF in value))
468
471
  return;
@@ -502,14 +505,14 @@ function isRow(value) {
502
505
  }
503
506
 
504
507
  // src/tooling/testing/isolation.ts
505
- var TENANT_A = "00000000-0000-4000-8000-00000000000a";
506
- var TENANT_B = "00000000-0000-4000-8000-00000000000b";
507
508
  async function isolation(host) {
508
509
  const saved = host.cache;
509
510
  host.cache = new Map;
510
511
  try {
511
512
  const checked = [];
512
513
  const skipped = [];
514
+ const tenantA = crypto.randomUUID();
515
+ const tenantB = crypto.randomUUID();
513
516
  for (const facts of host.facts.values()) {
514
517
  if (facts.globalReason !== undefined) {
515
518
  skipped.push({ table: facts.name, reason: facts.globalReason });
@@ -517,7 +520,7 @@ async function isolation(host) {
517
520
  }
518
521
  if (!facts.tenant)
519
522
  continue;
520
- await checkTable(host, facts);
523
+ await checkTable(host, facts, tenantA, tenantB);
521
524
  checked.push(facts.name);
522
525
  }
523
526
  return { checked, skipped };
@@ -525,25 +528,68 @@ async function isolation(host) {
525
528
  host.cache = saved;
526
529
  }
527
530
  }
528
- async function checkTable(host, facts) {
531
+ async function checkTable(host, facts, tenantA, tenantB) {
529
532
  const key = host.schema.tenancy?.key;
530
533
  if (key === undefined)
531
534
  return;
532
- const row = await insertRow(host, facts.name, { [key]: TENANT_B }, undefined, TENANT_B);
535
+ if (host.definitions.size > 0 && !host.definitions.has(facts.name)) {
536
+ throw new OkmError2("invalid", `Factory ${facts.name} is missing.`, {
537
+ fix: { summary: `Add ${facts.name} to factories().` }
538
+ });
539
+ }
540
+ const row = await insertRow(host, facts.name, { [key]: tenantB }, undefined, tenantB);
533
541
  const where = primaryWhere(facts, row, key);
534
- const client = openTenant(host, TENANT_A);
542
+ const client = openTenant(host, tenantA);
535
543
  const handle = tableHandle2(client, facts.name);
536
- const found = await handle.find({ where, limit: 1 });
537
- const rows = asRows(found);
544
+ try {
545
+ await readTenant(facts, handle, where);
546
+ const set = updateSet(host, facts, row);
547
+ if (set !== undefined) {
548
+ const updated = asCount(await handle.update({ where, set }));
549
+ if (updated > 0)
550
+ leak(facts.name, "update", { count: updated });
551
+ if (typeof client.batch === "function") {
552
+ const batched = await client.batch([handle.update({ where, set })]);
553
+ if (Array.isArray(batched) && batched.some((item) => asCount(item) > 0)) {
554
+ leak(facts.name, "batch", batched);
555
+ }
556
+ }
557
+ }
558
+ const removed = asCount(await handle.delete({ where }));
559
+ if (removed > 0)
560
+ leak(facts.name, "delete", { count: removed });
561
+ } finally {
562
+ const owner = tableHandle2(openTenant(host, tenantB), facts.name);
563
+ await owner.delete({ where }).catch(() => {
564
+ return;
565
+ });
566
+ forgetCached(host, facts.name, tenantB);
567
+ }
568
+ }
569
+ async function readTenant(facts, handle, where) {
570
+ const query = handle.find({ where, limit: 1 });
571
+ const rows = asRows(await query);
538
572
  if (rows.length > 0)
539
573
  leak(facts.name, "find", rows[0]);
574
+ const streamed = handle.find({ where, limit: 1 });
575
+ if (typeof streamed.stream === "function") {
576
+ try {
577
+ for await (const item of streamed.stream()) {
578
+ if (isRow2(item))
579
+ leak(facts.name, "stream", item);
580
+ }
581
+ } catch (error) {
582
+ if (!(error instanceof OkmError2) || error.code !== "OKM1111")
583
+ throw error;
584
+ }
585
+ }
540
586
  const one = await handle.one({ where });
541
587
  if (isRow2(one))
542
588
  leak(facts.name, "one", one);
543
- const counted = asCount(await handle.count());
589
+ const counted = asCount(await handle.count({ where }));
544
590
  if (counted > 0)
545
591
  leak(facts.name, "count", { count: counted });
546
- const exists = await handle.exists();
592
+ const exists = await handle.exists({ where });
547
593
  if (exists === true)
548
594
  leak(facts.name, "exists", { exists: true });
549
595
  const relation = facts.relations[0];
@@ -552,15 +598,28 @@ async function checkTable(host, facts) {
552
598
  if (isRow2(included))
553
599
  leak(facts.name, "include", included);
554
600
  }
555
- const set = updateSet(host, facts, row);
556
- if (set !== undefined) {
557
- const updated = asCount(await handle.update({ where, set }));
558
- if (updated > 0)
559
- leak(facts.name, "update", { count: updated });
601
+ if (typeof handle.aggregate === "function") {
602
+ const aggregated = await handle.aggregate({ where, count: true });
603
+ if (Array.isArray(aggregated)) {
604
+ for (const item of aggregated) {
605
+ if (isRow2(item) && asCount(item) > 0)
606
+ leak(facts.name, "aggregate", item);
607
+ }
608
+ }
609
+ }
610
+ if (typeof handle.page === "function") {
611
+ const orderField = Object.keys(where)[0];
612
+ if (orderField !== undefined) {
613
+ const paged = await handle.page({
614
+ where,
615
+ orderBy: { [orderField]: "asc" },
616
+ limit: 5
617
+ });
618
+ if (isRow2(paged) && Array.isArray(paged.items) && paged.items.length > 0) {
619
+ leak(facts.name, "page", paged.items[0]);
620
+ }
621
+ }
560
622
  }
561
- const deleted = asCount(await handle.delete({ where }));
562
- if (deleted > 0)
563
- leak(facts.name, "delete", { count: deleted });
564
623
  }
565
624
  function includeOf(relation) {
566
625
  if (relation.kind === "many")
@@ -0,0 +1,139 @@
1
+ import {
2
+ tag
3
+ } from "./cn0n68ft.js";
4
+ import {
5
+ OkmError2
6
+ } from "./52szzakm.js";
7
+
8
+ // src/dialects/pg/filter-parse.ts
9
+ var PARSED_OPS = ",eq,in,inList,notIn,lt,lte,gt,gte,between,startsWith,endsWith,contains,like,ilike,";
10
+ var PARSED_FIX = "Use eq, in, notIn, lt, lte, gt, gte, between, startsWith, endsWith, contains, like, or ilike.";
11
+ function parseQuery(allow, sort, query) {
12
+ if (typeof query !== "object" || query === null || Array.isArray(query)) {
13
+ throw new OkmError2("OKM1121", "filters parse expects an object of fields.");
14
+ }
15
+ for (const field of Object.keys(allow)) {
16
+ for (const op of allow[field] ?? []) {
17
+ if (PARSED_OPS.includes(`,${op},`))
18
+ continue;
19
+ throw new OkmError2("OKM1120", `filters() cannot apply ${op} on ${field}.`, {
20
+ fix: { summary: PARSED_FIX }
21
+ });
22
+ }
23
+ }
24
+ const whereParts = [];
25
+ let orderBy;
26
+ for (const key of Object.keys(query)) {
27
+ const value = query[key];
28
+ if (key === "sort" && typeof value === "string" && sort.includes(value)) {
29
+ orderBy = { [value]: "asc" };
30
+ continue;
31
+ }
32
+ if (!Object.hasOwn(allow, key)) {
33
+ throw new OkmError2("OKM1123", `Field ${key} is not in the allow list.`, {
34
+ fix: { summary: "Remove the field, or add it to filters({ allow })." }
35
+ });
36
+ }
37
+ const ops = allow[key] ?? [];
38
+ whereParts.push(...clauses(key, ops, value));
39
+ }
40
+ const where = combine(whereParts);
41
+ return orderBy === undefined ? { where } : { where, orderBy };
42
+ }
43
+ function clauses(field, ops, value) {
44
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
45
+ if (!ops.includes("eq")) {
46
+ throw new OkmError2("OKM1120", `filters() cannot apply eq on ${field}.`, {
47
+ fix: { summary: "Add eq to that field, or send an object of allowed operators." }
48
+ });
49
+ }
50
+ return [{ [field]: value }];
51
+ }
52
+ const applied = [];
53
+ for (const op of Object.keys(value)) {
54
+ if (!Object.hasOwn(value, op))
55
+ continue;
56
+ if (!ops.includes(op) && !(op === "inList" && ops.includes("in"))) {
57
+ throw new OkmError2("OKM1120", `filters() cannot apply ${op} on ${field}.`, {
58
+ fix: { summary: "Send an operator that field's allow list names." }
59
+ });
60
+ }
61
+ applied.push(one(field, op, value[op]));
62
+ }
63
+ if (applied.length === 0) {
64
+ throw new OkmError2("OKM1120", `filters() cannot apply a value on ${field}.`, {
65
+ fix: { summary: "Send eq as a scalar, or an object of allowed operators." }
66
+ });
67
+ }
68
+ return applied;
69
+ }
70
+ function one(field, op, value) {
71
+ switch (op) {
72
+ case "eq":
73
+ return {
74
+ [field]: value !== null && typeof value === "object" ? tag("eq", value) : value
75
+ };
76
+ case "in":
77
+ case "inList":
78
+ return { [field]: tag("inList", list(value, op)) };
79
+ case "notIn":
80
+ return { [field]: tag("notIn", list(value, op)) };
81
+ case "lt":
82
+ return { [field]: tag("lt", value) };
83
+ case "lte":
84
+ return { [field]: tag("lte", value) };
85
+ case "gt":
86
+ return { [field]: tag("gt", value) };
87
+ case "gte":
88
+ return { [field]: tag("gte", value) };
89
+ case "between": {
90
+ if (!Array.isArray(value) || value.length !== 2) {
91
+ throw new OkmError2("OKM1120", `between on ${field} needs two bounds.`, {
92
+ fix: { summary: "Send between as [from, to]." }
93
+ });
94
+ }
95
+ return { [field]: tag("between", [value[0], value[1]]) };
96
+ }
97
+ case "startsWith":
98
+ return { [field]: tag("startsWith", text(value, op)) };
99
+ case "endsWith":
100
+ return { [field]: tag("endsWith", text(value, op)) };
101
+ case "contains":
102
+ return { [field]: tag("contains", value) };
103
+ case "like":
104
+ return { [field]: tag("like", text(value, op)) };
105
+ case "ilike":
106
+ return { [field]: tag("ilike", text(value, op)) };
107
+ default:
108
+ throw new OkmError2("OKM1120", `filters() cannot apply ${op} on ${field}.`, {
109
+ fix: { summary: "Use an operator parse applies." }
110
+ });
111
+ }
112
+ }
113
+ function combine(parts) {
114
+ const first = parts[0];
115
+ if (first === undefined)
116
+ return {};
117
+ if (parts.length === 1)
118
+ return first;
119
+ return tag("and", parts);
120
+ }
121
+ function list(value, op) {
122
+ if (!Array.isArray(value)) {
123
+ throw new OkmError2("OKM1120", `${op} needs an array.`, {
124
+ fix: { summary: "Send that operator an array of values." }
125
+ });
126
+ }
127
+ return value;
128
+ }
129
+ function text(value, op) {
130
+ if (typeof value !== "string") {
131
+ throw new OkmError2("OKM1120", `${op} needs a string.`, {
132
+ fix: { summary: "Send that operator a string." }
133
+ });
134
+ }
135
+ return value;
136
+ }
137
+ export {
138
+ parseQuery
139
+ };
@@ -6,7 +6,7 @@ import {
6
6
  acquireTimeout2,
7
7
  errorField2,
8
8
  mapDriverError2
9
- } from "./kyxb62mc.js";
9
+ } from "./31vm914h.js";
10
10
  export {
11
11
  DriverError2 as DriverError,
12
12
  acquireTimeout2 as acquireTimeout,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  withHttp
3
- } from "./bt9sfq0z.js";
3
+ } from "./hf22e0wt.js";
4
4
  import {
5
5
  decodeResult
6
- } from "./qgvt8nx1.js";
6
+ } from "./gbtcc2d7.js";
7
7
  import {
8
8
  OkmError2
9
9
  } from "./52szzakm.js";
@@ -17,7 +17,7 @@ async function* streamRows(session, call, prepare, log) {
17
17
  })));
18
18
  }
19
19
  const { plan, params } = await prepare();
20
- const decode = plan.outputs.includes.length === 0 ? decodeResult : (await import("./h8ne16fq.js")).decodeIncluded;
20
+ const decode = plan.outputs.includes.length === 0 ? decodeResult : (await import("./nkaxvt9q.js")).decodeIncluded;
21
21
  for await (const chunk of session.pool.stream(plan.text, params)) {
22
22
  const decoded = decode(plan, chunk, call.table);
23
23
  if (!Array.isArray(decoded))
@@ -1,6 +1,6 @@
1
1
  import"../shared/8bwbwy8h.js";
2
2
  import"../shared/n442h54c.js";
3
- import"../shared/wqem4jed.js";
3
+ import"../shared/krxx77g0.js";
4
4
  import {
5
5
  formatFindings,
6
6
  hasError,
@@ -9,8 +9,8 @@ import {
9
9
  generateProject,
10
10
  planProject,
11
11
  checkProject
12
- } from "../shared/xdpj5kkm.js";
13
- import"../shared/ayz64vzj.js";
12
+ } from "../shared/qv2ydck9.js";
13
+ import"../shared/ga3sfy93.js";
14
14
  import"../shared/8xsbagad.js";
15
15
  import {
16
16
  OkmError2
@@ -22,14 +22,28 @@ import { dirname, join } from "node:path";
22
22
  import { fileURLToPath } from "node:url";
23
23
 
24
24
  // src/tooling/migrate/commands.ts
25
- function formatFailure(error) {
26
- return `${error.code}: ${error.message}
27
- ${error.fix.summary}
25
+ function formatFailure(error, verbose = false) {
26
+ const mapped = OkmError2.from(error);
27
+ let text = `error ${mapped.code}: ${mapped.message}
28
+ ${mapped.fix.summary}
28
29
  `;
30
+ if (verbose && error instanceof Error && error.stack !== undefined) {
31
+ text += `${error.stack}
32
+ `;
33
+ }
34
+ return text;
29
35
  }
30
36
  async function run(argv, io) {
31
37
  const cwd = io?.cwd ?? process.cwd();
32
38
  const stdout = io?.stdout ?? ((text) => process.stdout.write(text));
39
+ if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
40
+ stdout(commandsTable());
41
+ return;
42
+ }
43
+ if (argv.includes("--help") || argv.includes("-h")) {
44
+ stdout(commandHelp(argv.filter((arg) => arg !== "--help" && arg !== "-h")));
45
+ return;
46
+ }
33
47
  const [command, ...rest] = argv;
34
48
  if (command === "build") {
35
49
  const directory = await buildProject(cwd);
@@ -57,12 +71,12 @@ async function run(argv, io) {
57
71
  return;
58
72
  }
59
73
  if (command === "dev") {
60
- const { devProject } = await import("../shared/fyqkk1m0.js");
74
+ const { devProject } = await import("../shared/sdc7r2ww.js");
61
75
  stdout(await devProject(cwd));
62
76
  return;
63
77
  }
64
78
  if (command === "push") {
65
- const { pushProject } = await import("../shared/rasvvzgz.js");
79
+ const { pushProject } = await import("../shared/q58y17hy.js");
66
80
  stdout(await pushProject(cwd, invoke(splitFlags(rest))));
67
81
  return;
68
82
  }
@@ -81,17 +95,17 @@ async function run(argv, io) {
81
95
  return;
82
96
  }
83
97
  if (sub === "apply") {
84
- const { applyProject } = await import("../shared/rasvvzgz.js");
98
+ const { applyProject } = await import("../shared/q58y17hy.js");
85
99
  stdout(await applyProject(cwd, invoke(parsed), stdout));
86
100
  return;
87
101
  }
88
102
  if (sub === "status") {
89
- const { statusProject } = await import("../shared/bzsjtd7c.js");
103
+ const { statusProject } = await import("../shared/6mnp99hx.js");
90
104
  stdout(await statusProject(cwd, invoke(parsed)));
91
105
  return;
92
106
  }
93
107
  if (sub === "check") {
94
- const { checkMigrations } = await import("../shared/r73pt594.js");
108
+ const { checkMigrations } = await import("../shared/7zwwyh0v.js");
95
109
  const provision = parsed.flags.includes("--provision") || rest.slice(1).includes("--provision");
96
110
  stdout(await checkMigrations(cwd, invoke(parsed), { provision }));
97
111
  return;
@@ -104,13 +118,13 @@ async function run(argv, io) {
104
118
  fix: { summary: "Pass okm seed <file>." }
105
119
  });
106
120
  }
107
- const { seedProject } = await import("../shared/jdgqqx31.js");
121
+ const { seedProject } = await import("../shared/2pqx26h8.js");
108
122
  stdout(await seedProject(cwd, parsed.name, invoke(parsed)));
109
123
  return;
110
124
  }
111
125
  if (command === "doctor") {
112
126
  const parsed = splitFlags(rest);
113
- const { doctorProject } = await import("../shared/qy9rdq0g.js");
127
+ const { doctorProject } = await import("../shared/ps7a6h14.js");
114
128
  stdout(await doctorProject(cwd, parsed.name, invoke(parsed)));
115
129
  return;
116
130
  }
@@ -122,7 +136,7 @@ async function run(argv, io) {
122
136
  });
123
137
  }
124
138
  if (sub === "list" || sub === "check") {
125
- const { extProject } = await import("../shared/990a291j.js");
139
+ const { extProject } = await import("../shared/janx09m5.js");
126
140
  stdout(await extProject(cwd, sub, invoke(splitFlags(rest.slice(1)))));
127
141
  return;
128
142
  }
@@ -210,6 +224,42 @@ function splitFlags(args) {
210
224
  statementTimeoutMs
211
225
  };
212
226
  }
227
+ var COMMANDS = [
228
+ ["build", "Write the schema build."],
229
+ ["check", "Lint the schema and compare it with one target."],
230
+ ["dev", "Watch the schema and reload."],
231
+ ["doctor [code]", "Print a target report, or one error code."],
232
+ ["ext list", "List extensions the server can install."],
233
+ ["ext check", "Compare declared extensions with the server."],
234
+ ["generate [name]", "Write the next migration."],
235
+ ["migrate plan <name>", "Print the plan and its lock estimates."],
236
+ ["migrate apply", "Apply pending migrations."],
237
+ ["migrate status", "Print whether the target is current."],
238
+ ["migrate check", "Replay migration history in a scratch schema."],
239
+ ["push", "Apply the schema diff to one target."],
240
+ ["seed <file>", "Run a seed file on one target."]
241
+ ];
242
+ function commandsTable() {
243
+ const lines = ["okm <command>", ""];
244
+ for (const [name, summary] of COMMANDS)
245
+ lines.push(` ${name.padEnd(24)} ${summary}`);
246
+ lines.push("", "okm <command> --help prints that command.", "");
247
+ return `${lines.join(`
248
+ `)}
249
+ `;
250
+ }
251
+ function commandHelp(argv) {
252
+ const key = argv.join(" ");
253
+ const found = COMMANDS.find(([name]) => name === key || name.startsWith(`${key} `));
254
+ if (found === undefined) {
255
+ return `Unknown command ${key}.
256
+
257
+ ${commandsTable()}`;
258
+ }
259
+ return `${found[0]}
260
+ ${found[1]}
261
+ `;
262
+ }
213
263
  function numberFlag(flag, value) {
214
264
  const parsed = value === undefined ? Number.NaN : Number(value);
215
265
  if (!Number.isFinite(parsed) || parsed < 0) {
@@ -226,16 +276,13 @@ var packageJsonPath = join(here, "..", "..", "package.json");
226
276
  if (process.argv.includes("--version")) {
227
277
  process.stdout.write(`${readPackageVersion(packageJsonPath)}
228
278
  `);
229
- } else if (process.argv.length > 2) {
279
+ } else {
230
280
  try {
231
281
  await run(process.argv.slice(2));
232
282
  } catch (error) {
233
- if (error instanceof OkmError2) {
234
- process.stderr.write(formatFailure(error));
235
- process.exitCode = 1;
236
- } else {
237
- throw error;
238
- }
283
+ const verbose = process.argv.includes("--verbose") || process.env.OKM_DEBUG === "1";
284
+ process.stderr.write(formatFailure(error, verbose));
285
+ process.exitCode = 1;
239
286
  }
240
287
  }
241
288
  function readPackageVersion(path) {
@@ -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
  *
@@ -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
  *
@@ -1,24 +1,25 @@
1
- import"../../shared/wqem4jed.js";
1
+ import"../../shared/krxx77g0.js";
2
2
  import"../../shared/8bwbwy8h.js";
3
3
  import"../../shared/n442h54c.js";
4
4
  import {
5
5
  applyTarget2,
6
6
  backfillTiming2
7
- } from "../../shared/xhh1srw3.js";
7
+ } from "../../shared/cctw17hj.js";
8
8
  import {
9
9
  loadMigrations,
10
10
  loadHeadSnapshot,
11
11
  readReference
12
- } from "../../shared/kdkxh3kb.js";
12
+ } from "../../shared/gde73es6.js";
13
13
  import"../../shared/szm0cksa.js";
14
14
  import"../../shared/fancm5pt.js";
15
15
  import {
16
- assertTargetPolicy,
17
- selectTarget,
18
16
  loadBuiltSchema,
19
17
  loadConfig
20
- } from "../../shared/xdpj5kkm.js";
21
- import"../../shared/ayz64vzj.js";
18
+ } from "../../shared/qv2ydck9.js";
19
+ import {
20
+ assertTargetPolicy,
21
+ selectTarget
22
+ } from "../../shared/ga3sfy93.js";
22
23
  import"../../shared/8xsbagad.js";
23
24
  import {
24
25
  OkmError2