okmodel 0.1.0 → 0.2.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 (181) hide show
  1. package/README.md +237 -19
  2. package/dist/adapters/capabilities.d.ts +34 -0
  3. package/dist/adapters/pg/bunsql-extra.d.ts +17 -0
  4. package/dist/adapters/pg/bunsql.d.ts +60 -0
  5. package/dist/adapters/pg/call.d.ts +2 -12
  6. package/dist/adapters/pg/nodepostgres-extra.d.ts +18 -0
  7. package/dist/adapters/pg/nodepostgres.d.ts +57 -0
  8. package/dist/adapters/pg/postgres-extra.d.ts +50 -1
  9. package/dist/adapters/pg/postgresjs.d.ts +0 -4
  10. package/dist/adapters/pg/session.d.ts +94 -0
  11. package/dist/adapters/pg/watch.d.ts +18 -0
  12. package/dist/contracts/catalog/object.d.ts +1 -0
  13. package/dist/contracts/catalog/types.d.ts +15 -8
  14. package/dist/contracts/connection.d.ts +13 -0
  15. package/dist/contracts/driver.d.ts +18 -5
  16. package/dist/contracts/error.d.ts +17 -1
  17. package/dist/contracts/generator.d.ts +63 -0
  18. package/dist/contracts/index.d.ts +2 -2
  19. package/dist/contracts/index.js +1 -1
  20. package/dist/contracts/internal.js +11 -9
  21. package/dist/contracts/location.d.ts +24 -0
  22. package/dist/contracts/rows.d.ts +15 -0
  23. package/dist/dialects/pg/archive-bind.d.ts +22 -0
  24. package/dist/dialects/pg/column.d.ts +126 -9
  25. package/dist/dialects/pg/compile.d.ts +1 -0
  26. package/dist/dialects/pg/custom.d.ts +6 -0
  27. package/dist/dialects/pg/filter-parse.d.ts +19 -0
  28. package/dist/dialects/pg/filters.d.ts +19 -0
  29. package/dist/dialects/pg/index.d.ts +11 -2
  30. package/dist/dialects/pg/index.js +159 -1063
  31. package/dist/dialects/pg/keys.d.ts +19 -3
  32. package/dist/dialects/pg/model.d.ts +223 -2
  33. package/dist/dialects/pg/operator-fit.d.ts +86 -0
  34. package/dist/dialects/pg/operators.d.ts +39 -1
  35. package/dist/dialects/pg/ops/arr-append.d.ts +9 -0
  36. package/dist/dialects/pg/ops/arr-ns.d.ts +7 -0
  37. package/dist/dialects/pg/ops/arr-remove.d.ts +9 -0
  38. package/dist/dialects/pg/ops/containedBy.d.ts +9 -0
  39. package/dist/dialects/pg/ops/contains.d.ts +8 -4
  40. package/dist/dialects/pg/ops/hasAnyKey.d.ts +8 -0
  41. package/dist/dialects/pg/ops/hasKey.d.ts +8 -0
  42. package/dist/dialects/pg/ops/json-ns.d.ts +6 -0
  43. package/dist/dialects/pg/ops/json-set.d.ts +12 -0
  44. package/dist/dialects/pg/ops/matches.d.ts +16 -0
  45. package/dist/dialects/pg/ops/overlaps.d.ts +9 -0
  46. package/dist/dialects/pg/ops/path.d.ts +13 -0
  47. package/dist/dialects/pg/preset.d.ts +76 -0
  48. package/dist/dialects/pg/relations.d.ts +59 -2
  49. package/dist/dialects/pg/schema.d.ts +109 -5
  50. package/dist/dialects/pg/table.d.ts +130 -11
  51. package/dist/dialects/pg/temporal.d.ts +9 -7
  52. package/dist/dialects/pg/tenancy.d.ts +94 -0
  53. package/dist/dialects/pg/through.d.ts +21 -0
  54. package/dist/dialects/pg/trait.d.ts +141 -0
  55. package/dist/dialects/pg/where.d.ts +37 -0
  56. package/dist/runtime/aggregate.d.ts +25 -0
  57. package/dist/runtime/archive-handle.d.ts +18 -0
  58. package/dist/runtime/archive-rules.d.ts +12 -0
  59. package/dist/runtime/archive.d.ts +73 -0
  60. package/dist/runtime/batch.d.ts +20 -0
  61. package/dist/runtime/client.d.ts +114 -5
  62. package/dist/runtime/conflict.d.ts +2 -1
  63. package/dist/runtime/exposure.d.ts +27 -0
  64. package/dist/runtime/hookm.d.ts +39 -0
  65. package/dist/runtime/ids/index.d.ts +11 -0
  66. package/dist/runtime/ids/index.js +193 -0
  67. package/dist/runtime/ids/okid-extended.d.ts +19 -0
  68. package/dist/runtime/ids/okid-generator.d.ts +21 -0
  69. package/dist/runtime/ids/okid-shared.d.ts +89 -0
  70. package/dist/runtime/ids/okid.d.ts +24 -0
  71. package/dist/runtime/ids/uuid.d.ts +21 -0
  72. package/dist/runtime/lock.d.ts +22 -0
  73. package/dist/runtime/operator-sql.d.ts +8 -0
  74. package/dist/runtime/page.d.ts +25 -0
  75. package/dist/runtime/pg/bun.d.ts +30 -0
  76. package/dist/runtime/pg/bun.js +52 -0
  77. package/dist/runtime/pg/pg.d.ts +29 -0
  78. package/dist/runtime/pg/pg.js +52 -0
  79. package/dist/runtime/pg/pglite.js +16 -17
  80. package/dist/runtime/pg/postgresjs.js +14 -24
  81. package/dist/runtime/plan.d.ts +146 -7
  82. package/dist/runtime/preset-stack.d.ts +17 -0
  83. package/dist/runtime/presets.d.ts +48 -0
  84. package/dist/runtime/read-stream.d.ts +26 -0
  85. package/dist/runtime/safety/index.d.ts +159 -0
  86. package/dist/runtime/safety/index.js +294 -0
  87. package/dist/runtime/safety-hook.d.ts +42 -0
  88. package/dist/runtime/server-check.d.ts +34 -0
  89. package/dist/runtime/tenancy/index.d.ts +37 -0
  90. package/dist/runtime/tenancy/index.js +365 -0
  91. package/dist/runtime/trait-read.d.ts +30 -0
  92. package/dist/runtime/traits/index.d.ts +128 -0
  93. package/dist/runtime/traits/index.js +509 -0
  94. package/dist/runtime/transaction.d.ts +19 -0
  95. package/dist/runtime/tx.d.ts +22 -7
  96. package/dist/runtime/types.d.ts +390 -25
  97. package/dist/runtime/validate/bind.d.ts +11 -0
  98. package/dist/runtime/validate/closed.d.ts +16 -0
  99. package/dist/runtime/validate/engine.d.ts +41 -0
  100. package/dist/runtime/validate/hook.d.ts +17 -0
  101. package/dist/runtime/validate/index.d.ts +21 -0
  102. package/dist/runtime/validate/index.js +175 -0
  103. package/dist/runtime/validate/load.d.ts +13 -0
  104. package/dist/runtime/validate/places.d.ts +41 -0
  105. package/dist/runtime/validate/rules.d.ts +108 -0
  106. package/dist/runtime/validate/surface.d.ts +102 -0
  107. package/dist/runtime/write.d.ts +30 -5
  108. package/dist/shared/{fgqgsb5z.js → 0qkxbm1f.js} +2 -2
  109. package/dist/shared/0v1g424p.js +26 -0
  110. package/dist/shared/10tjenmd.js +210 -0
  111. package/dist/shared/{tb4ze1tr.js → 1dp5knt7.js} +95 -10
  112. package/dist/shared/38crxbgn.js +229 -0
  113. package/dist/shared/3a5ne750.js +158 -0
  114. package/dist/shared/4b9da9d7.js +138 -0
  115. package/dist/shared/4e4c6vq2.js +62 -0
  116. package/dist/shared/{ckb1p0a4.js → 4jzzghr6.js} +416 -83
  117. package/dist/shared/{ycwz42az.js → 52szzakm.js} +6 -26
  118. package/dist/shared/{kynx7vbm.js → 5z4q2zda.js} +604 -21
  119. package/dist/shared/672t5vym.js +12 -0
  120. package/dist/shared/7m88x3e9.js +45 -0
  121. package/dist/shared/7mht49yd.js +1091 -0
  122. package/dist/shared/{mtkwv8wx.js → 7p2wbz7f.js} +1 -1
  123. package/dist/shared/83eaefwr.js +10 -0
  124. package/dist/shared/96ps4g1z.js +32 -0
  125. package/dist/shared/97g2jnrw.js +76 -0
  126. package/dist/shared/{xy79j5hq.js → 9k6vetvf.js} +25 -36
  127. package/dist/shared/{w8samvk1.js → a1gdp7j9.js} +1 -1
  128. package/dist/shared/a5sxz52y.js +13 -0
  129. package/dist/shared/{dynq16vb.js → b066g80h.js} +8 -6
  130. package/dist/shared/cstgkzdx.js +154 -0
  131. package/dist/shared/{ax16zpvm.js → d04j53v8.js} +6 -5
  132. package/dist/shared/{y9m15dfv.js → d2whjz4k.js} +15 -9
  133. package/dist/shared/dd4f8mnx.js +307 -0
  134. package/dist/shared/e6wg00wd.js +164 -0
  135. package/dist/shared/e7a268fy.js +195 -0
  136. package/dist/shared/{vkm3q4x1.js → eaxwrgby.js} +4 -3
  137. package/dist/shared/{7ck0dtry.js → ecdzej21.js} +160 -81
  138. package/dist/shared/{px90pf9p.js → edwqqryd.js} +21 -16
  139. package/dist/shared/{g2v6gnc8.js → eqmtn1bj.js} +57 -5
  140. package/dist/shared/g1b5zzj9.js +75 -0
  141. package/dist/shared/g9f0zqce.js +376 -0
  142. package/dist/shared/ge03bfca.js +64 -0
  143. package/dist/shared/{79ggp945.js → jns2wx4t.js} +55 -12
  144. package/dist/shared/k3ted12y.js +33 -0
  145. package/dist/shared/k7dwkhvq.js +31 -0
  146. package/dist/shared/{hd293j9g.js → kyxb62mc.js} +14 -2
  147. package/dist/shared/m9tv7xn7.js +404 -0
  148. package/dist/shared/mrq77sgw.js +55 -0
  149. package/dist/shared/mxmcdsh5.js +450 -0
  150. package/dist/shared/n442h54c.js +14 -0
  151. package/dist/shared/n47rd821.js +442 -0
  152. package/dist/shared/n4jqhpqz.js +27 -0
  153. package/dist/shared/{mkjmqjv4.js → nfzc5z0w.js} +2 -104
  154. package/dist/shared/nsva2a6b.js +42 -0
  155. package/dist/shared/ps43134h.js +69 -0
  156. package/dist/shared/{8b04d28c.js → py3nh7jy.js} +2 -2
  157. package/dist/shared/qa5dsc7z.js +86 -0
  158. package/dist/shared/{g5fhqmwe.js → qxa0yb9j.js} +6 -4
  159. package/dist/shared/r6nwkxnt.js +86 -0
  160. package/dist/shared/{4j72449d.js → rtnaqre0.js} +3 -3
  161. package/dist/shared/rwmrejjz.js +26 -0
  162. package/dist/shared/{zhc5vmgf.js → s6gqfn5j.js} +2 -2
  163. package/dist/shared/sacqha1y.js +70 -0
  164. package/dist/shared/{qk3hxfja.js → sfh4pk75.js} +5 -5
  165. package/dist/shared/{j5cn2963.js → t4tbsy9h.js} +13 -64
  166. package/dist/shared/{ngk8gj61.js → tbszx8mz.js} +15 -1
  167. package/dist/shared/v18jva5b.js +90 -0
  168. package/dist/shared/x9v1qgeb.js +201 -0
  169. package/dist/shared/xatfg0gn.js +193 -0
  170. package/dist/shared/xkz8g6by.js +225 -0
  171. package/dist/shared/{f5thc1s0.js → xrfnq5k8.js} +237 -418
  172. package/dist/shared/ytg3xf9p.js +149 -0
  173. package/dist/shared/yw5c610b.js +48 -0
  174. package/dist/shared/zd93g3rq.js +31 -0
  175. package/dist/shared/zt8y0wzb.js +132 -0
  176. package/dist/shared/{p0cbr3g2.js → zwmqxb0g.js} +1 -1
  177. package/dist/tooling/cli.js +14 -10
  178. package/dist/tooling/migrate/commands.d.ts +10 -0
  179. package/dist/tooling/migrate/engine.d.ts +36 -0
  180. package/package.json +56 -4
  181. package/dist/shared/gk7cs7sp.js +0 -566
@@ -5,25 +5,40 @@ import {
5
5
  serializeCatalog2,
6
6
  catalogHash2,
7
7
  parseCatalog2
8
- } from "./y9m15dfv.js";
8
+ } from "./d2whjz4k.js";
9
9
  import {
10
- quoteLiteral,
11
- ColumnBuilder2,
12
- snakeCase,
13
- emittedTypeName
14
- } from "./gk7cs7sp.js";
10
+ VALIDATION_NOT_IMPORTED2,
11
+ VALIDATION_NOT_IMPORTED_FIX2
12
+ } from "./n442h54c.js";
13
+ import {
14
+ contributedFields
15
+ } from "./g1b5zzj9.js";
16
+ import {
17
+ assertValidation,
18
+ writeWouldValidate
19
+ } from "./3a5ne750.js";
15
20
  import {
16
21
  catalog,
17
22
  sameEnumLabels
18
- } from "./g5fhqmwe.js";
23
+ } from "./qxa0yb9j.js";
24
+ import {
25
+ snakeCase,
26
+ emittedTypeName
27
+ } from "./x9v1qgeb.js";
28
+ import {
29
+ ColumnBuilder2
30
+ } from "./dd4f8mnx.js";
31
+ import {
32
+ quoteLiteral
33
+ } from "./xatfg0gn.js";
19
34
  import {
20
35
  staticNamespace2,
21
36
  identityKey2,
22
37
  compareText
23
- } from "./j5cn2963.js";
38
+ } from "./t4tbsy9h.js";
24
39
  import {
25
40
  OkmError2
26
- } from "./ycwz42az.js";
41
+ } from "./52szzakm.js";
27
42
 
28
43
  // src/dialects/pg/ddl.ts
29
44
  function quoteIdent(name) {
@@ -71,8 +86,11 @@ function alterColumnSql(before, after, schema) {
71
86
  const statements = [];
72
87
  const table = qualify(schema, after.identity.parent.name);
73
88
  const name = quoteIdent(after.identity.name);
74
- if (before.definition.dataType !== after.definition.dataType) {
75
- statements.push(`alter table ${table} alter column ${name} set data type ${after.definition.dataType} using ${name}::${after.definition.dataType}`);
89
+ const typeChanged = before.definition.dataType !== after.definition.dataType;
90
+ const collationChanged = before.definition.collation !== after.definition.collation;
91
+ if (typeChanged || collationChanged) {
92
+ const using = typeChanged ? ` using ${name}::${after.definition.dataType}` : "";
93
+ statements.push(`alter table ${table} alter column ${name} set data type ${after.definition.dataType}${collateSql(after.definition.collation)}${using}`);
76
94
  }
77
95
  if (before.definition.nullable !== after.definition.nullable) {
78
96
  statements.push(`alter table ${table} alter column ${name} ${after.definition.nullable ? "drop not null" : "set not null"}`);
@@ -106,13 +124,18 @@ function createTableSql(tableObject, source, schema) {
106
124
  `)}
107
125
  )${tail}`;
108
126
  }
127
+ function collateSql(collation) {
128
+ if (collation === undefined)
129
+ return "";
130
+ return ` collate ${quoteIdent(collation)}`;
131
+ }
109
132
  function createEnumSql(object, schema) {
110
133
  const labels = object.definition.labels.map((label) => quoteLiteral(label)).join(", ");
111
134
  return `create type ${qualify(schema, object.identity.name)} as enum (${labels})`;
112
135
  }
113
136
  function columnSql(column) {
114
137
  const definition = column.definition;
115
- let sql = `${quoteIdent(column.identity.name)} ${definition.dataType}`;
138
+ let sql = `${quoteIdent(column.identity.name)} ${definition.dataType}${collateSql(definition.collation)}`;
116
139
  if (definition.identity !== undefined) {
117
140
  sql += definition.identity.always ? " generated always as identity" : " generated by default as identity";
118
141
  } else if (definition.generated !== undefined) {
@@ -253,6 +276,8 @@ function planMigration(request) {
253
276
  const enums = enumEdits(beforeBy, afterBy, schema);
254
277
  requireReplacements(picklists, enums, replacements);
255
278
  recreateDependents(beforeList, afterList, alters, dropKeys, createKeys, picklists);
279
+ omitOwnedSequences(createKeys, afterBy, request.after);
280
+ omitOwnedSequences(dropKeys, beforeBy, renamed);
256
281
  const steps = [];
257
282
  steps.push(...renameSteps(request.before, renames, schema));
258
283
  steps.push(...keptNames.map((item) => renameShapeStep(item, schema)));
@@ -284,6 +309,9 @@ function planMigration(request) {
284
309
  for (const sql of alterColumnSql(change.before, change.after, schema)) {
285
310
  steps.push(step(sql, alterClass(sql), "ddl", ACCESS));
286
311
  }
312
+ const identitySql = identityChangeSql(change.before, change.after, schema);
313
+ if (identitySql !== undefined)
314
+ steps.push(step(identitySql, "expand", "ddl", ACCESS));
287
315
  }
288
316
  const createdTables = new Set([...createKeys].map((key) => afterBy.get(key)).filter((object) => object?.kind === "table").map((object) => object.identity.name));
289
317
  const emitted = new Set;
@@ -875,7 +903,29 @@ function columnNullable(source, table, column) {
875
903
  return false;
876
904
  }
877
905
  function columnAlterable(before, after) {
878
- return stable(before.definition.identity) === stable(after.definition.identity) && stable(before.definition.generated) === stable(after.definition.generated);
906
+ return stable(before.definition.generated) === stable(after.definition.generated);
907
+ }
908
+ function identityChangeSql(before, after, schema) {
909
+ if (stable(before.definition.identity) === stable(after.definition.identity))
910
+ return;
911
+ const table = qualify2(schema, after.identity.parent.name);
912
+ const name = quoteIdent(after.identity.name);
913
+ const next = after.definition.identity;
914
+ const previous = before.definition.identity;
915
+ if (next === undefined)
916
+ return `alter table ${table} alter column ${name} drop identity`;
917
+ if (previous === undefined) {
918
+ const generated = next.always ? "always" : "by default";
919
+ return `alter table ${table} alter column ${name} add generated ${generated} as identity`;
920
+ }
921
+ return `alter table ${table} alter column ${name} set generated ${next.always ? "always" : "by default"}`;
922
+ }
923
+ function omitOwnedSequences(keys, by, source) {
924
+ for (const key of keys) {
925
+ const object = by.get(key);
926
+ if (object?.kind === "sequence" && folded(object, source))
927
+ keys.delete(key);
928
+ }
879
929
  }
880
930
  function sameDefinition(left, right) {
881
931
  return stable(left.definition) === stable(right.definition);
@@ -894,9 +944,16 @@ function folded(object, source) {
894
944
  return true;
895
945
  if (object.kind === "constraint" && object.definition.constraintKind === "primaryKey")
896
946
  return true;
897
- if (object.kind === "sequence" && identitySequence(object, source))
947
+ if (object.kind !== "sequence")
948
+ return false;
949
+ if (identitySequence(object, source))
898
950
  return true;
899
- return false;
951
+ const name = object.identity.name;
952
+ return source.objects.some((column) => {
953
+ if (column.kind !== "column" || column.definition.identity === undefined)
954
+ return false;
955
+ return name === `${column.identity.parent.name}_${column.identity.name}_seq`;
956
+ });
900
957
  }
901
958
  function anchoredParent(object) {
902
959
  if (object.kind === "column" || object.kind === "index" || object.kind === "constraint") {
@@ -1102,6 +1159,460 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from
1102
1159
  import { basename, join } from "node:path";
1103
1160
  import { pathToFileURL } from "node:url";
1104
1161
 
1162
+ // src/tooling/errors/registry.ts
1163
+ var ERROR_DOCS = [
1164
+ {
1165
+ code: "OKM1012",
1166
+ title: "Trait field conflict",
1167
+ summary: "A trait added a field that the table already declares.",
1168
+ fix: "Rename the table field or drop the trait that introduces it. The error names both."
1169
+ },
1170
+ {
1171
+ code: "OKM1020",
1172
+ title: "Unknown table",
1173
+ summary: "A table, dependency, or catalog object is missing or does not match its identity.",
1174
+ fix: "Use a name that is in the schema. The error lists the accepted names and a nearest-name hint when one is close."
1175
+ },
1176
+ {
1177
+ code: "OKM1021",
1178
+ title: "Foreign key target",
1179
+ summary: "A foreign key is missing its target table or column, or the target is ambiguous.",
1180
+ fix: "Name the referenced table and columns. The error lists the columns that exist on the target."
1181
+ },
1182
+ {
1183
+ code: "OKM1022",
1184
+ title: "Foreign key type",
1185
+ summary: "A foreign key column and the column it references have different types.",
1186
+ fix: "Give both columns the same type. The error names the type that is accepted."
1187
+ },
1188
+ {
1189
+ code: "OKM1023",
1190
+ title: "Duplicate name",
1191
+ summary: "Two catalog objects share an identity, or two tables emit the same type name.",
1192
+ fix: "Rename one of them. Table names and emitted type names must be unique in the schema."
1193
+ },
1194
+ {
1195
+ code: "OKM1024",
1196
+ title: "Table file not in schema",
1197
+ summary: "A table file is not passed to schema().",
1198
+ fix: "Add the file's table to the schema() call that okm check reads."
1199
+ },
1200
+ {
1201
+ code: "OKM1025",
1202
+ title: "Multiple Register",
1203
+ summary: "More than one Register augmentation is in the project.",
1204
+ fix: 'Keep a single declare module "okmodel" that sets schema. Pass an explicit schema type everywhere else.'
1205
+ },
1206
+ {
1207
+ code: "OKM1026",
1208
+ title: "Dependency cycle",
1209
+ summary: "The catalog dependency graph contains a cycle, so objects cannot be ordered.",
1210
+ fix: "Break the cycle. The error is raised while ordering the catalog."
1211
+ },
1212
+ {
1213
+ code: "OKM1027",
1214
+ title: "Unreadable catalog",
1215
+ summary: "The catalog document is malformed or uses a version this build cannot read.",
1216
+ fix: "Regenerate the catalog with this version of okmodel. Do not hand-edit the document."
1217
+ },
1218
+ {
1219
+ code: "OKM1030",
1220
+ title: "Validation in two places",
1221
+ summary: "A field is validated both on the column and somewhere else.",
1222
+ fix: "Keep one validation for the field. The error names the field."
1223
+ },
1224
+ {
1225
+ code: "OKM1040",
1226
+ title: "Preset name collision",
1227
+ summary: "A preset uses the name of a client method or a reserved name, or a table and its traits (or two traits) define the same preset.",
1228
+ fix: "Rename the preset. The error names both sources when two define it. okm upgrade renames a preset when a later release claims its name."
1229
+ },
1230
+ {
1231
+ code: "OKM1051",
1232
+ title: "Archive foreign key",
1233
+ summary: "A foreign key into a table-strategy archivable table uses CASCADE, SET NULL, or SET DEFAULT and is not listed in cascade.",
1234
+ fix: "List the child table, the foreign key, and its action in cascade, or use RESTRICT or NO ACTION."
1235
+ },
1236
+ {
1237
+ code: "OKM1052",
1238
+ title: "Not archivable",
1239
+ summary: "archive or restore was called on a table that is not archivable.",
1240
+ fix: "Call it only on an archivable table. The methods are absent from the types of other tables."
1241
+ },
1242
+ {
1243
+ code: "OKM1060",
1244
+ title: "Invalid column",
1245
+ summary: "A column definition has a bad length, precision, scale, array rank, interval qualifier, or an empty or repeated picklist or enum.",
1246
+ fix: "Use a value the error lists as accepted."
1247
+ },
1248
+ {
1249
+ code: "OKM1061",
1250
+ title: "Reserved option",
1251
+ summary: "The option exists in the types but this version does not implement it.",
1252
+ fix: "Remove the option until the prompt or milestone named in the error adds it."
1253
+ },
1254
+ {
1255
+ code: "OKM1101",
1256
+ title: "Find without limit",
1257
+ summary: "find was called without a limit.",
1258
+ fix: "Pass limit, or call .all(reason) when the full set is intentional."
1259
+ },
1260
+ {
1261
+ code: "OKM1102",
1262
+ title: "Unfiltered write",
1263
+ summary: "update or delete would run with an empty where.",
1264
+ fix: "Pass a filter. undefined in a write filter does not mean every row."
1265
+ },
1266
+ {
1267
+ code: "OKM1104",
1268
+ title: "Upsert target",
1269
+ summary: "onConflict names columns that are not a unique constraint.",
1270
+ fix: "Set on to the columns of a unique constraint or the primary key."
1271
+ },
1272
+ {
1273
+ code: "OKM1105",
1274
+ title: "Include without limit",
1275
+ summary: "A to-many include has no limit.",
1276
+ fix: "Pass limit on the include, or call .all(reason)."
1277
+ },
1278
+ {
1279
+ code: "OKM1110",
1280
+ title: "Engine version",
1281
+ summary: "The feature needs a newer engine than schema({ requires }) allows.",
1282
+ fix: "Raise requires, or stop using the feature. The error names both versions."
1283
+ },
1284
+ {
1285
+ code: "OKM1111",
1286
+ title: "Missing capability",
1287
+ summary: "The call needs a driver capability this driver does not have.",
1288
+ fix: "Use a driver that declares the capability, or drop the call. The error names the flag."
1289
+ },
1290
+ {
1291
+ code: "OKM1120",
1292
+ title: "Unknown field",
1293
+ summary: "A field name in where, select, orderBy, or include is not on the table.",
1294
+ fix: "Use a field from the catalog. The error includes a nearest-name hint when one is close."
1295
+ },
1296
+ {
1297
+ code: "OKM1121",
1298
+ title: "Object as value",
1299
+ summary: "A plain object was passed where a value is required. JSON cannot create an operator.",
1300
+ fix: "Wrap the value with eq. eq is the equality form for json and jsonb."
1301
+ },
1302
+ {
1303
+ code: "OKM1122",
1304
+ title: "Bad identifier",
1305
+ summary: "An identifier is empty, too long, contains NUL or another control character, or is an unquoted reserved word.",
1306
+ fix: "Use a shorter name without control characters. Quote is not accepted here. The error names the rule that failed."
1307
+ },
1308
+ {
1309
+ code: "OKM1123",
1310
+ title: "Hidden field allowlist",
1311
+ summary: "A hidden field was added to a filter or sort allowlist.",
1312
+ fix: "Remove the hidden field from the allowlist. Hidden fields stay out of caller filters and sorts."
1313
+ },
1314
+ {
1315
+ code: "OKM1124",
1316
+ title: "Operator mismatch",
1317
+ summary: "An operator does not apply to the column type.",
1318
+ fix: "Use an operator the error lists for that column type. Text search needs a tsvector column."
1319
+ },
1320
+ {
1321
+ code: "OKM1130",
1322
+ title: "Cursor order",
1323
+ summary: "A cursor was reused with a different orderBy than the one that created it, or is not a cursor page() returned.",
1324
+ fix: "Request the next page with the same orderBy and the exact next value the previous page returned. The cursor encodes that order."
1325
+ },
1326
+ {
1327
+ code: "OKM1190",
1328
+ title: "Safety violation",
1329
+ summary: "A query composition broke a core invariant. violations lists every broken rule in stable order.",
1330
+ fix: "Change the contribution named on the first violation, or pass an explicit escape hatch (unscoped, all, trusted, allow) and record the reason."
1331
+ },
1332
+ {
1333
+ code: "OKM1191",
1334
+ title: "No snapshot plan",
1335
+ summary: "A multi-statement read was requested inside READ COMMITTED and no single-statement plan exists.",
1336
+ fix: "Run the read in REPEATABLE READ or SERIALIZABLE, or shape it so it is one statement."
1337
+ },
1338
+ {
1339
+ code: "OKM1200",
1340
+ title: "Validation failed",
1341
+ summary: "A write or a standalone check failed a validation rule. issues lists each path and message key.",
1342
+ fix: "Fix the fields named in issues. Each issue message is a key, not display text."
1343
+ },
1344
+ {
1345
+ code: "OKM1201",
1346
+ title: "Validation not imported",
1347
+ summary: VALIDATION_NOT_IMPORTED2,
1348
+ fix: VALIDATION_NOT_IMPORTED_FIX2
1349
+ },
1350
+ {
1351
+ code: "OKM1210",
1352
+ title: "Codec rejected a value",
1353
+ summary: "A codec refused a value: not numeric text, not a valid temporal value, or the wrong shape.",
1354
+ fix: "Pass a value the error lists as accepted. A missing Temporal global tells you to assign one to globalThis.Temporal."
1355
+ },
1356
+ {
1357
+ code: "OKM1401",
1358
+ title: "Outcome unknown",
1359
+ summary: "A commit or batch was sent and its result never arrived, so the write may or may not have happened.",
1360
+ fix: "Check whether the write landed, using an idempotency key, before retrying. This error is never retried automatically."
1361
+ },
1362
+ {
1363
+ code: "OKM1510",
1364
+ title: "Unsafe migration",
1365
+ summary: "A migration is not expand-safe and has no reason.",
1366
+ fix: "Split the change into expand and contract, or pass a reason that CI can record."
1367
+ },
1368
+ {
1369
+ code: "OKM1520",
1370
+ title: "Schema drift",
1371
+ summary: "The database has drifted past what an expand migration can repair.",
1372
+ fix: "Inspect the diff and repair the database, or generate a migration that matches the drift."
1373
+ },
1374
+ {
1375
+ code: "OKM1521",
1376
+ title: "Snapshot mismatch",
1377
+ summary: "Provisioning from the snapshot does not match the replayed migration history.",
1378
+ fix: "Regenerate the snapshot from the history. okm migrate check is the command that reports this."
1379
+ },
1380
+ {
1381
+ code: "OKM1522",
1382
+ title: "Apply lock held",
1383
+ summary: "Another apply already holds the target's migration lock.",
1384
+ fix: "Wait for that apply to finish. Do not start a second apply against the same target."
1385
+ },
1386
+ {
1387
+ code: "OKM1530",
1388
+ title: "Ambiguous rename",
1389
+ summary: "A rename could match more than one object.",
1390
+ fix: "Declare the rename explicitly so the plan has one pairing."
1391
+ },
1392
+ {
1393
+ code: "OKM1541",
1394
+ title: "Picklist removal",
1395
+ summary: "A picklist or enum value was removed and the replacement was not given.",
1396
+ fix: "Pass --replace <table>.<column>.<old>=<new>, or =null on a nullable column. The replacement must be in the new list."
1397
+ },
1398
+ {
1399
+ code: "OKM1542",
1400
+ title: "Data statement",
1401
+ summary: "A migration contains a data statement outside backfill().",
1402
+ fix: "Move the statement into backfill(). Data changes are batched and resumable there."
1403
+ },
1404
+ {
1405
+ code: "OKM1601",
1406
+ title: "Stale typed SQL",
1407
+ summary: "A typed SQL signature no longer matches the database.",
1408
+ fix: "Regenerate the typed SQL and commit the new signature."
1409
+ },
1410
+ {
1411
+ code: "OKM1701",
1412
+ title: "Tenant context missing",
1413
+ summary: "A tenant table was used on a client that has no tenant context.",
1414
+ fix: "Open the client with the tenant context before reading or writing that table."
1415
+ },
1416
+ {
1417
+ code: "OKM1702",
1418
+ title: "Unverifiable tenant SQL",
1419
+ summary: "Raw SQL touches a tenant table and the tenant predicate cannot be proved.",
1420
+ fix: "Use the query builder, or mark the fragment trusted with a reason if it is reviewed."
1421
+ },
1422
+ {
1423
+ code: "OKM1704",
1424
+ title: "Tenant key change",
1425
+ summary: "An update tried to change the tenant key.",
1426
+ fix: "Remove the tenant key from the update. Moving a row between tenants is not an update."
1427
+ },
1428
+ {
1429
+ code: "OKM1705",
1430
+ title: "Global references tenant",
1431
+ summary: "A global table references a tenant table.",
1432
+ fix: "Drop the reference, or make the referencing table a tenant table."
1433
+ },
1434
+ {
1435
+ code: "OKM1706",
1436
+ title: "Tenant index",
1437
+ summary: "An index on a tenant table does not lead with the tenant key.",
1438
+ fix: "Put the tenant key first in the index columns."
1439
+ },
1440
+ {
1441
+ code: "OKM1707",
1442
+ title: "RLS role",
1443
+ summary: "connect() was given a role that owns the tables or is a superuser, under the rls strategy.",
1444
+ fix: "Connect as a role that is neither the owner nor a superuser."
1445
+ },
1446
+ {
1447
+ code: "OKM1801",
1448
+ title: "Dialect mismatch",
1449
+ summary: "The schema dialect and the driver dialect are not the same.",
1450
+ fix: "Open the schema with the driver package that matches it."
1451
+ },
1452
+ {
1453
+ code: "OKM1802",
1454
+ title: "Server version",
1455
+ summary: "The connected server does not satisfy schema({ requires }).",
1456
+ fix: "Upgrade the server, or lower requires to a version the server meets."
1457
+ },
1458
+ {
1459
+ code: "OKM1803",
1460
+ title: "Postgres floor",
1461
+ summary: "The connected server is older than PostgreSQL 15, and the schema does not set requires to that older major.",
1462
+ fix: "Upgrade the server to PostgreSQL 15 or newer, or set schema({ requires }) to the older major on purpose."
1463
+ },
1464
+ {
1465
+ code: "OKM1810",
1466
+ title: "Extension not declared",
1467
+ summary: "An extension builder was used but the extension is not declared on the schema.",
1468
+ fix: "Declare the extension next to the schema that uses it."
1469
+ },
1470
+ {
1471
+ code: "OKM1811",
1472
+ title: "Extension unavailable",
1473
+ summary: "A declared extension is not installed on the server.",
1474
+ fix: "Install the extension, or remove the declaration. okm doctor reports this against the connected server."
1475
+ },
1476
+ {
1477
+ code: "OKM1812",
1478
+ title: "Feature version",
1479
+ summary: "A feature needs a newer Postgres or extension version than the one declared or connected.",
1480
+ fix: 'Raise the declared version, or avoid the feature. uuidv7() below 18 can use t.id({ default: "uuidv4" }), defaults.id set to "uuidv4", or a client generator. Core features are never gated on an extension.'
1481
+ },
1482
+ {
1483
+ code: "OKM1813",
1484
+ title: "Extension defined twice",
1485
+ summary: "The same extension definition was registered twice.",
1486
+ fix: "Keep one definition. A second copy fails at build."
1487
+ },
1488
+ {
1489
+ code: "OKM1814",
1490
+ title: "Extension change refused",
1491
+ summary: "The plan would downgrade an extension, move a non-relocatable one, or drop one that still has dependents.",
1492
+ fix: "Drop or move the dependents first. Postgres cannot lower an extension version."
1493
+ },
1494
+ {
1495
+ code: "OKM1820",
1496
+ title: "View tenancy",
1497
+ summary: "A view over tenant tables does not expose the tenant key and is not marked global.",
1498
+ fix: "Expose the tenant key so the predicate can be applied, or declare global(reason)."
1499
+ },
1500
+ {
1501
+ code: "OKM1821",
1502
+ title: "Replace needs recreate",
1503
+ summary: "An incompatible replace would have to recreate dependents. CASCADE is never used.",
1504
+ fix: "Review the dependents the plan lists and recreate them explicitly."
1505
+ },
1506
+ {
1507
+ code: "OKM1822",
1508
+ title: "Concurrent refresh",
1509
+ summary: "REFRESH CONCURRENTLY was declared without a unique index on the materialized view.",
1510
+ fix: "Add a unique index, or refresh without CONCURRENTLY."
1511
+ },
1512
+ {
1513
+ code: "OKM1823",
1514
+ title: "Security definer path",
1515
+ summary: "A SECURITY DEFINER function has no search_path.",
1516
+ fix: "Set search_path on the function to the schemas it is allowed to see."
1517
+ },
1518
+ {
1519
+ code: "OKM1824",
1520
+ title: "Function dependencies",
1521
+ summary: "A plpgsql function has no dependsOn. Postgres does not record dependencies inside the body.",
1522
+ fix: "List dependsOn. LANGUAGE sql with BEGIN ATOMIC is inferred and does not need the list."
1523
+ },
1524
+ {
1525
+ code: "OKM1825",
1526
+ title: "Missing privilege",
1527
+ summary: "The application role lacks a privilege on a managed object.",
1528
+ fix: "Grant the privilege, or change the role. okm doctor reports the object and the privilege."
1529
+ },
1530
+ {
1531
+ code: "OKM1830",
1532
+ title: "Lock outside transaction",
1533
+ summary: "find was called with a row lock outside tx().",
1534
+ fix: "Take the lock inside tx(). Advisory locks are transaction-scoped too."
1535
+ },
1536
+ {
1537
+ code: "OKM1840",
1538
+ title: "Replica not allowed",
1539
+ summary: ".replica() was used on an operation that requires the primary, or inside tx().",
1540
+ fix: "Run the operation on the primary. Writes, batch, locks, and tx() never go to a replica."
1541
+ },
1542
+ {
1543
+ code: "OKM1841",
1544
+ title: "External object differs",
1545
+ summary: "An external object does not match the owner's exported catalog.",
1546
+ fix: "Update the dependency's export, or stop treating the object as external."
1547
+ },
1548
+ {
1549
+ code: "OKM1842",
1550
+ title: "Cross-database foreign key",
1551
+ summary: "A foreign key points at a table in another database.",
1552
+ fix: "Keep the foreign key inside one database. Cross-catalog keys are allowed only there."
1553
+ },
1554
+ {
1555
+ code: "OKM1843",
1556
+ title: "No eligible replica",
1557
+ summary: "A replica was required and none is eligible: unhealthy, behind the session, or none configured.",
1558
+ fix: "Configure a replica or drop .replica(). This call does not read the primary."
1559
+ },
1560
+ {
1561
+ code: "OKM1844",
1562
+ title: "Fallback refused",
1563
+ summary: "An automatic read found no eligible replica and fallback is error.",
1564
+ fix: "Restore a replica, or set fallback to primary if the primary should absorb the read."
1565
+ },
1566
+ {
1567
+ code: "OKM1845",
1568
+ title: "Target unresolved",
1569
+ summary: "The target could not be resolved. The tenant is unknown or the resolver failed.",
1570
+ fix: "Check the tenant id and the resolver. Connection details are not guessed."
1571
+ },
1572
+ {
1573
+ code: "OKM1846",
1574
+ title: "Acquire timeout",
1575
+ summary: "The pool did not hand out a connection within timeouts.acquire.",
1576
+ fix: "Raise timeouts.acquire, or stop holding connections. The wait does not spill into another pool."
1577
+ },
1578
+ {
1579
+ code: "OKM1850",
1580
+ title: "Protected target",
1581
+ summary: "The operation is blocked on a protected target and --allow-protected was not passed.",
1582
+ fix: "Run a read-only command or an expand migration, or pass --allow-protected for this invocation."
1583
+ },
1584
+ {
1585
+ code: "OKM1851",
1586
+ title: "Target not empty",
1587
+ summary: "Provisioning was asked to fill a target that already has objects.",
1588
+ fix: "Provision an empty schema or an empty database. A non-empty target is refused."
1589
+ },
1590
+ {
1591
+ code: "OKM1852",
1592
+ title: "Protection mismatch",
1593
+ summary: "Targets that resolve to the same database disagree about protection, or tenants of one schema-per-tenant database do.",
1594
+ fix: "Give those targets the same protection flag. okm check and okm doctor both report this."
1595
+ },
1596
+ {
1597
+ code: "OKM1853",
1598
+ title: "Target not named",
1599
+ summary: "Several targets are configured and the command was not given --target.",
1600
+ fix: "Pass --target <name>. The command does not guess."
1601
+ }
1602
+ ];
1603
+ var byCode;
1604
+ function errorDoc(code) {
1605
+ const index = byCode ?? indexDocs();
1606
+ return index.get(code);
1607
+ }
1608
+ function indexDocs() {
1609
+ const index = new Map;
1610
+ for (const doc of ERROR_DOCS)
1611
+ index.set(doc.code, doc);
1612
+ byCode = index;
1613
+ return index;
1614
+ }
1615
+
1105
1616
  // src/dialects/pg/declarations.ts
1106
1617
  function schemaDeclarations(source) {
1107
1618
  const renames = [];
@@ -1159,13 +1670,13 @@ function emitRowTypes(source) {
1159
1670
  inserts.push(` readonly ${key}: ${name}Insert;`);
1160
1671
  updates.push(` readonly ${key}: ${name}Update;`);
1161
1672
  lines.push(`export interface ${name} {`);
1162
- lines.push(fields(item, "row"));
1673
+ lines.push(fields(item, "row", source));
1163
1674
  lines.push("}", "");
1164
1675
  lines.push(`export interface ${name}Insert {`);
1165
- lines.push(fields(item, "insert"));
1676
+ lines.push(fields(item, "insert", source));
1166
1677
  lines.push("}", "");
1167
1678
  lines.push(`export interface ${name}Update {`);
1168
- lines.push(fields(item, "update"));
1679
+ lines.push(fields(item, "update", source));
1169
1680
  lines.push("}", "");
1170
1681
  }
1171
1682
  lines.push("export interface Rows {");
@@ -1187,20 +1698,29 @@ function emitRowTypes(source) {
1187
1698
  function propertyName(name) {
1188
1699
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : JSON.stringify(name);
1189
1700
  }
1190
- function fields(item, kind) {
1701
+ function fields(item, kind, source) {
1191
1702
  const printed = [];
1192
- for (const [field, builder] of Object.entries(item.columns)) {
1703
+ const options = item.options;
1704
+ const entries = Object.entries(item.columns);
1705
+ for (const pair of contributedFields(source.traits, options)) {
1706
+ if (!Object.hasOwn(item.columns, pair[0]))
1707
+ entries.push([pair[0], pair[1]]);
1708
+ }
1709
+ if (source.tenancy !== undefined && options?.tenancy?.kind !== "global" && !Object.hasOwn(item.columns, source.tenancy.key)) {
1710
+ entries.push([source.tenancy.key, source.tenancy.column()]);
1711
+ }
1712
+ for (const [field, builder] of entries) {
1193
1713
  if (!(builder instanceof ColumnBuilder2)) {
1194
1714
  continue;
1195
1715
  }
1196
1716
  const state = builder.state;
1197
- const written = insertMode(state);
1198
1717
  if (kind === "row") {
1199
1718
  if (!state.hidden) {
1200
1719
  printed.push(` readonly ${field}: ${valueType(state, "required")};`);
1201
1720
  }
1202
1721
  continue;
1203
1722
  }
1723
+ const written = kind === "update" ? updateMode(state) : insertMode(state);
1204
1724
  if (written === "omit") {
1205
1725
  continue;
1206
1726
  }
@@ -1219,6 +1739,11 @@ function insertMode(state) {
1219
1739
  }
1220
1740
  return "required";
1221
1741
  }
1742
+ function updateMode(state) {
1743
+ if (state.omitUpdate || insertMode(state) === "omit")
1744
+ return "omit";
1745
+ return "optional";
1746
+ }
1222
1747
  function valueType(state, mode) {
1223
1748
  let label = scalarLabel(state);
1224
1749
  if (state.dims > 0) {
@@ -1336,6 +1861,13 @@ async function planProject(cwd, name, flags) {
1336
1861
  }
1337
1862
  async function checkProject(cwd) {
1338
1863
  const opened = await openProject(cwd);
1864
+ assertValidation(opened.built);
1865
+ if (schemaRequestsValidation(opened.built) && !projectImportsValidate(cwd)) {
1866
+ const doc = errorDoc("OKM1201");
1867
+ if (doc === undefined)
1868
+ throw new Error("OKM1201 missing from the error registry.");
1869
+ throw new OkmError2(doc.code, doc.summary, { fix: { summary: doc.fix } });
1870
+ }
1339
1871
  assertTargetAlias(listTargets(opened.config));
1340
1872
  if (opened.config.tables !== undefined) {
1341
1873
  const directory = join(cwd, opened.config.tables);
@@ -1486,5 +2018,56 @@ function isConfig(value) {
1486
2018
  function isRecord(value) {
1487
2019
  return typeof value === "object" && value !== null;
1488
2020
  }
2021
+ var SKIP_SCAN = new Set(["node_modules", "dist", "coverage"]);
2022
+ function schemaRequestsValidation(built) {
2023
+ const flag = built.validation;
2024
+ for (const item of built.tables) {
2025
+ if (writeWouldValidate(flag, item, {}))
2026
+ return true;
2027
+ }
2028
+ return false;
2029
+ }
2030
+ function projectImportsValidate(cwd) {
2031
+ return directoryImportsValidate(cwd);
2032
+ }
2033
+ function directoryImportsValidate(dir) {
2034
+ let entries;
2035
+ try {
2036
+ entries = readdirSync(dir, { withFileTypes: true });
2037
+ } catch {
2038
+ return false;
2039
+ }
2040
+ for (const entry of entries) {
2041
+ if (entry.name.startsWith(".") || SKIP_SCAN.has(entry.name))
2042
+ continue;
2043
+ const path = join(dir, entry.name);
2044
+ if (entry.isDirectory()) {
2045
+ if (directoryImportsValidate(path))
2046
+ return true;
2047
+ continue;
2048
+ }
2049
+ if (!/\.(?:ts|tsx|mts|cts)$/.test(entry.name))
2050
+ continue;
2051
+ if (runtimeImportsValidate(readFileSync(path, "utf8")))
2052
+ return true;
2053
+ }
2054
+ return false;
2055
+ }
2056
+ function runtimeImportsValidate(source) {
2057
+ const text = source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\n]*/g, "");
2058
+ const pattern = /["']okmodel\/validate(?:\.js)?["']/g;
2059
+ for (const match of text.matchAll(pattern)) {
2060
+ const index = match.index ?? 0;
2061
+ const lead = text.slice(Math.max(0, index - 240), index);
2062
+ const start = Math.max(lead.lastIndexOf(`
2063
+ `), lead.lastIndexOf(";"));
2064
+ const head = lead.slice(start + 1);
2065
+ if (/\bimport\s+type\b/.test(head) || /\bexport\s+type\b/.test(head))
2066
+ continue;
2067
+ if (/\bimport\b/.test(head) || /\bexport\b/.test(head))
2068
+ return true;
2069
+ }
2070
+ return false;
2071
+ }
1489
2072
 
1490
2073
  export { quoteIdent, planMigration, formatPlan, parsePlan, assertTargetPolicy, assertDirectConnection, listTargets, selectTarget, buildProject, generateProject, planProject, checkProject, projectHead, loadConfig };