okmodel 0.8.0 → 0.9.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 (237) hide show
  1. package/README.md +23 -1
  2. package/dist/adapters/pg/bunsql-extra.d.ts +5 -5
  3. package/dist/adapters/pg/bunsql.d.ts +6 -2
  4. package/dist/adapters/pg/nodepostgres.d.ts +16 -1
  5. package/dist/adapters/pg/postgres-extra.d.ts +3 -17
  6. package/dist/adapters/pg/postgresjs.d.ts +2 -1
  7. package/dist/contracts/driver.d.ts +3 -2
  8. package/dist/contracts/error.d.ts +1 -1
  9. package/dist/dialects/pg/errors.d.ts +2 -0
  10. package/dist/dialects/pg/ext/btree-gin.d.ts +26 -0
  11. package/dist/dialects/pg/ext/btree-gin.js +20 -0
  12. package/dist/dialects/pg/ext/btree-gist.d.ts +26 -0
  13. package/dist/dialects/pg/ext/btree-gist.js +20 -0
  14. package/dist/dialects/pg/ext/call.d.ts +53 -0
  15. package/dist/dialects/pg/ext/citext.js +1 -1
  16. package/dist/dialects/pg/ext/feature.d.ts +26 -0
  17. package/dist/dialects/pg/ext/fuzzystrmatch.d.ts +34 -0
  18. package/dist/dialects/pg/ext/fuzzystrmatch.js +33 -0
  19. package/dist/dialects/pg/ext/hstore.d.ts +41 -0
  20. package/dist/dialects/pg/ext/hstore.js +29 -0
  21. package/dist/dialects/pg/ext/index.d.ts +56 -0
  22. package/dist/dialects/pg/ext/index.js +1 -1
  23. package/dist/dialects/pg/ext/ltree.d.ts +41 -0
  24. package/dist/dialects/pg/ext/ltree.js +29 -0
  25. package/dist/dialects/pg/ext/pg-trgm.js +4 -13
  26. package/dist/dialects/pg/ext/pgcrypto.d.ts +33 -0
  27. package/dist/dialects/pg/ext/pgcrypto.js +33 -0
  28. package/dist/dialects/pg/ext/unaccent.d.ts +26 -0
  29. package/dist/dialects/pg/ext/unaccent.js +21 -0
  30. package/dist/dialects/pg/ext/using.d.ts +23 -0
  31. package/dist/dialects/pg/ext/versions.d.ts +36 -0
  32. package/dist/dialects/pg/external.d.ts +19 -0
  33. package/dist/dialects/pg/filters.d.ts +2 -2
  34. package/dist/dialects/pg/fn/index.js +5 -5
  35. package/dist/dialects/pg/index.d.ts +1 -0
  36. package/dist/dialects/pg/index.js +19 -14
  37. package/dist/dialects/pg/introspect.d.ts +12 -0
  38. package/dist/dialects/pg/view/index.js +7 -7
  39. package/dist/dialects/pg/view/scratch.d.ts +4 -1
  40. package/dist/runtime/artifact-file.d.ts +22 -0
  41. package/dist/runtime/client.d.ts +10 -0
  42. package/dist/runtime/drift-detail.d.ts +2 -1
  43. package/dist/runtime/drift.d.ts +3 -10
  44. package/dist/runtime/param-limit.d.ts +20 -0
  45. package/dist/runtime/pg/bun.js +4 -4
  46. package/dist/runtime/pg/neon.js +4 -4
  47. package/dist/runtime/pg/pg.js +4 -4
  48. package/dist/runtime/pg/pglite.js +4 -4
  49. package/dist/runtime/pg/postgresjs.js +11 -11
  50. package/dist/runtime/server-check.d.ts +36 -6
  51. package/dist/runtime/tenancy/index.js +1 -1
  52. package/dist/runtime/traits/index.js +6 -6
  53. package/dist/runtime/validate/index.js +2 -2
  54. package/dist/runtime/write.d.ts +1 -1
  55. package/dist/shared/{4m6hskg8.js → 0j6qe91y.js} +4 -4
  56. package/dist/shared/{6m78c8ss.js → 1h5h1rf5.js} +1 -1
  57. package/dist/shared/{4vbf83xx.js → 2vcnpf0n.js} +1 -1
  58. package/dist/shared/{qd9hhd8f.js → 3ap5hxvj.js} +2 -1
  59. package/dist/shared/{v1wad97q.js → 3d7a80fq.js} +1 -1
  60. package/dist/shared/{wcrheftc.js → 3dr444ny.js} +58 -79
  61. package/dist/shared/{hm5f8jby.js → 3vvpcphd.js} +8 -7
  62. package/dist/shared/{760ppr76.js → 4a3mmhhe.js} +2 -2
  63. package/dist/shared/{mkrn359n.js → 4pkz5nvw.js} +222 -39
  64. package/dist/shared/{agsfb86s.js → 4te76xc5.js} +4 -4
  65. package/dist/shared/{63f4ksbz.js → 60y4ahmw.js} +1 -1
  66. package/dist/shared/61bmrmhr.js +35 -0
  67. package/dist/shared/{bxas03mq.js → 63qjmhnp.js} +100 -17
  68. package/dist/shared/{g5yc06ka.js → 67w3kn71.js} +1 -1
  69. package/dist/shared/{jgqj11w4.js → 6f0z3hzh.js} +4 -0
  70. package/dist/shared/{njga14v4.js → 6vfvsdkt.js} +10 -8
  71. package/dist/shared/{kj9d8maq.js → 6xqvesa5.js} +1 -1
  72. package/dist/shared/8gdbty5v.js +665 -0
  73. package/dist/shared/{pan0370z.js → 8ma6drzr.js} +211 -30
  74. package/dist/shared/{mnvjvqyp.js → 8p2bgye0.js} +124 -32
  75. package/dist/shared/{mfedzr74.js → 8qwqrxed.js} +14 -6
  76. package/dist/shared/{hfqmpasr.js → 8v7vzpgt.js} +11 -8
  77. package/dist/shared/{7ghwamrf.js → 99qp9vtg.js} +1 -1
  78. package/dist/shared/{zgex00fd.js → ak61ye1f.js} +5 -5
  79. package/dist/shared/{dpc2r7rb.js → bdekbvmt.js} +218 -95
  80. package/dist/shared/{jebgrkeq.js → bxjq6xqv.js} +2 -2
  81. package/dist/shared/c7dqd3gk.js +1806 -0
  82. package/dist/shared/ddh72ym5.js +20 -0
  83. package/dist/shared/e1c55v04.js +10 -0
  84. package/dist/shared/{mtw6jz76.js → e275q7sd.js} +1 -1
  85. package/dist/shared/{k86n8vq3.js → ekhq3ytz.js} +1 -1
  86. package/dist/shared/esy4dyxe.js +17 -0
  87. package/dist/shared/{gcefca6p.js → f9t9g54z.js} +22 -9
  88. package/dist/shared/{sjk86yrx.js → fk9vef30.js} +84 -73
  89. package/dist/shared/fkjne6gx.js +8 -0
  90. package/dist/shared/{e8zyaqvj.js → fne90dvz.js} +4 -1
  91. package/dist/shared/{needfe47.js → g89fmz5s.js} +1 -1
  92. package/dist/shared/{m4xff2a6.js → ghqzdp94.js} +2 -2
  93. package/dist/shared/{n0jmpgj1.js → gr9sn4za.js} +1 -1
  94. package/dist/shared/{5mtf6cj3.js → gzgfsspx.js} +5 -2
  95. package/dist/shared/h9811jjm.js +17 -0
  96. package/dist/shared/{z581d7ny.js → hgt69zjf.js} +2 -2
  97. package/dist/shared/{35q0n9tz.js → jvxcp1qk.js} +71 -87
  98. package/dist/shared/k73e8n9d.js +136 -0
  99. package/dist/shared/{195pzzzz.js → kzgrcy8s.js} +12 -8
  100. package/dist/shared/kzgrnckd.js +29 -0
  101. package/dist/shared/{p3xbdcvt.js → mz48rs74.js} +68 -8
  102. package/dist/shared/{zbqbmmrp.js → n6je5b28.js} +13 -26
  103. package/dist/shared/{b6d6p0zc.js → ns35jqz0.js} +75 -49
  104. package/dist/shared/{n05z9nwz.js → nstwgzn8.js} +1 -1
  105. package/dist/{tooling/shared/08kemxjd.js → shared/pgsak1yw.js} +1 -1
  106. package/dist/shared/py2zrrm6.js +451 -0
  107. package/dist/shared/{9dr2qnx7.js → qqm3ze6q.js} +1 -1
  108. package/dist/shared/{wvdjznah.js → rcjm0yn7.js} +31 -38
  109. package/dist/shared/rged92v6.js +17 -0
  110. package/dist/shared/{jp36k7dp.js → rw4tpcd0.js} +81 -51
  111. package/dist/shared/{bsek72fg.js → s5qe47a6.js} +3 -0
  112. package/dist/shared/{1d9q9cv2.js → smxzjz60.js} +2 -2
  113. package/dist/shared/{wz2a78mp.js → th5rnaxw.js} +1 -1
  114. package/dist/{tooling/shared/rs1jb051.js → shared/tr64w5p0.js} +1 -1
  115. package/dist/shared/{14x60fbf.js → tyqapcgk.js} +73 -10
  116. package/dist/shared/vsna85qf.js +8 -0
  117. package/dist/shared/{63qtkrca.js → vy47ryqa.js} +55 -5
  118. package/dist/shared/{jx2m62cs.js → wbdh9qbg.js} +7 -5
  119. package/dist/shared/{f4jy1j7x.js → wp7y8q3y.js} +946 -105
  120. package/dist/shared/{16kvjebh.js → xbd9xs6p.js} +7 -26
  121. package/dist/shared/{bah9nbg3.js → xbvz446k.js} +18 -23
  122. package/dist/shared/{p5nh3r8q.js → y6tfjk9q.js} +2 -2
  123. package/dist/shared/{3bb7wsqt.js → ycnft340.js} +1 -1
  124. package/dist/shared/{z7s6fpxv.js → ymenf259.js} +1 -1
  125. package/dist/shared/{0wrea3jq.js → ysdw2fg3.js} +10 -2
  126. package/dist/shared/zecrwdk9.js +61 -0
  127. package/dist/shared/{3t6kw83m.js → zm2rzqba.js} +2 -1
  128. package/dist/tooling/cli.js +200 -50
  129. package/dist/tooling/driver-run.js +109 -343
  130. package/dist/tooling/github/comment.d.ts +56 -0
  131. package/dist/tooling/github/post-comment.d.ts +17 -0
  132. package/dist/tooling/migrate/apply.d.ts +17 -2
  133. package/dist/tooling/migrate/commands.d.ts +2 -1
  134. package/dist/tooling/migrate/config.d.ts +13 -0
  135. package/dist/tooling/migrate/drift.d.ts +12 -0
  136. package/dist/tooling/migrate/driver-url.d.ts +17 -0
  137. package/dist/tooling/migrate/export.d.ts +165 -0
  138. package/dist/tooling/migrate/ext-surface.d.ts +80 -0
  139. package/dist/tooling/migrate/ext.d.ts +31 -4
  140. package/dist/tooling/migrate/import-drizzle.d.ts +40 -0
  141. package/dist/tooling/migrate/index.d.ts +3 -0
  142. package/dist/tooling/migrate/index.js +57 -10
  143. package/dist/tooling/migrate/lint-rules.d.ts +18 -1
  144. package/dist/tooling/migrate/lint.d.ts +14 -1
  145. package/dist/tooling/migrate/place.d.ts +18 -0
  146. package/dist/tooling/migrate/plan-json.d.ts +65 -0
  147. package/dist/tooling/migrate/policy.d.ts +17 -5
  148. package/dist/tooling/migrate/pooler-probe.d.ts +23 -0
  149. package/dist/tooling/migrate/project.d.ts +10 -1
  150. package/dist/tooling/migrate/pull-emit.d.ts +98 -0
  151. package/dist/tooling/migrate/pull-routines.d.ts +126 -0
  152. package/dist/tooling/migrate/pull-verify.d.ts +26 -0
  153. package/dist/tooling/migrate/pull.d.ts +65 -0
  154. package/dist/tooling/migrate/reprint.d.ts +32 -0
  155. package/dist/tooling/migrate/runner.d.ts +24 -1
  156. package/dist/tooling/migrate/sandbox.d.ts +8 -2
  157. package/dist/tooling/migrate/schema-api.d.ts +43 -0
  158. package/dist/tooling/migrate/sql-lex.d.ts +18 -0
  159. package/dist/tooling/shared/{ppnnnvj8.js → 09a6vsys.js} +1 -1
  160. package/dist/tooling/shared/{7ae7rjmn.js → 0v9ptnxp.js} +8 -8
  161. package/dist/tooling/shared/{fb9k2qwd.js → 0wtzps9p.js} +11 -30
  162. package/dist/tooling/shared/{a6pp9d12.js → 0yhf1db4.js} +6 -6
  163. package/dist/tooling/shared/{dy672fz6.js → 19msgrsc.js} +2 -2
  164. package/dist/{shared/q0cqt2sp.js → tooling/shared/1fwfm5m3.js} +1 -1
  165. package/dist/tooling/shared/{f9mphxej.js → 2jdn09e2.js} +401 -24
  166. package/dist/tooling/shared/{jne7b93p.js → 2qrte2ct.js} +5 -5
  167. package/dist/tooling/shared/{5bvw2nev.js → 2v4nwnkb.js} +1 -1
  168. package/dist/tooling/shared/{spk1jg3r.js → 33z39trp.js} +106 -23
  169. package/dist/tooling/shared/{z8d7ptht.js → 353vnw9z.js} +5 -5
  170. package/dist/tooling/shared/{dw08f3m8.js → 390kry9g.js} +2 -2
  171. package/dist/tooling/shared/{xjs6zqtc.js → 3f6etb9z.js} +18 -31
  172. package/dist/{shared/bnby0esn.js → tooling/shared/3t2cqmsx.js} +1 -1
  173. package/dist/tooling/shared/{wwnekq5n.js → 4dzvjtpy.js} +1 -1
  174. package/dist/tooling/shared/{y5656f0e.js → 4ec0nxvw.js} +10 -10
  175. package/dist/tooling/shared/{3m1hkg7z.js → 4tg2bn41.js} +59 -78
  176. package/dist/tooling/shared/{zvnhh1n0.js → 5rah55z0.js} +2 -2
  177. package/dist/tooling/shared/{pqy21k24.js → 804w1t69.js} +4 -4
  178. package/dist/tooling/shared/8g0m0rvd.js +134 -0
  179. package/dist/tooling/shared/8msbtw5s.js +8 -0
  180. package/dist/tooling/shared/{4d2w25vw.js → 8t22h3h7.js} +2 -2
  181. package/dist/tooling/shared/{smn6kz1p.js → 91c59b7b.js} +2 -2
  182. package/dist/tooling/shared/{aftb2mwr.js → adk9k6va.js} +1 -1
  183. package/dist/tooling/shared/{j9t5s12a.js → ajjd5cwz.js} +2 -2
  184. package/dist/tooling/shared/{cmptmb4f.js → angjck01.js} +1 -1
  185. package/dist/tooling/shared/{yw30vnq2.js → asnnc3v5.js} +1 -1
  186. package/dist/tooling/shared/{ma2340rx.js → bj1kyqc8.js} +2 -2
  187. package/dist/tooling/shared/{2wtvc7rw.js → bt3tjemb.js} +11 -3
  188. package/dist/tooling/shared/{ec71zqgt.js → chk2bh4f.js} +2 -2
  189. package/dist/tooling/shared/{gmcf9cxf.js → ckq4z5ax.js} +5 -2
  190. package/dist/tooling/shared/{w7qe8sj1.js → d3rhb24q.js} +1 -1
  191. package/dist/tooling/shared/{y031jct9.js → d8q00vxg.js} +4 -4
  192. package/dist/tooling/shared/{vs0t6qt8.js → dx188rxs.js} +2 -2
  193. package/dist/tooling/shared/{g9ad4n80.js → efq6p9df.js} +35 -42
  194. package/dist/tooling/shared/{ddjtfgnx.js → en6rwzq0.js} +3 -3
  195. package/dist/tooling/shared/{6nyfyw70.js → f2r5g0fs.js} +2 -2
  196. package/dist/tooling/shared/{03k6res6.js → f4gep0nk.js} +1 -1
  197. package/dist/tooling/shared/{gfvtj6d0.js → f8a010fm.js} +13 -11
  198. package/dist/tooling/shared/{3tq8pnb8.js → fdsek6pa.js} +1 -1
  199. package/dist/tooling/shared/fvmrxsd1.js +8 -0
  200. package/dist/tooling/shared/{733a1tq3.js → g3xybeqf.js} +4 -3
  201. package/dist/tooling/shared/{pwsytxm5.js → gefmpwsa.js} +1 -1
  202. package/dist/tooling/shared/{mtrqn2hn.js → gjj71hfh.js} +25 -30
  203. package/dist/tooling/shared/{8sqrsw3s.js → jgzt69pa.js} +5 -5
  204. package/dist/tooling/shared/{nf7jmhfa.js → jm30k2ca.js} +10 -8
  205. package/dist/tooling/shared/{vx86qjv8.js → kayqzpbk.js} +6 -3
  206. package/dist/tooling/shared/{zhwnfyb2.js → kvw1672d.js} +1 -1
  207. package/dist/tooling/shared/{rq5kk8j6.js → mr1kcgpw.js} +3 -3
  208. package/dist/tooling/shared/{72fyg4zv.js → mwbdf409.js} +1 -1
  209. package/dist/tooling/shared/{wt7j7nf6.js → ns9207v3.js} +1 -1
  210. package/dist/tooling/shared/{9xzm7hjg.js → p6tjv97k.js} +3 -3
  211. package/dist/tooling/shared/{yfr8hzhm.js → p73evwf0.js} +1 -1
  212. package/dist/tooling/shared/{dzx85q70.js → phc1nc4g.js} +1 -1
  213. package/dist/tooling/shared/{417zdsk7.js → ppybjw50.js} +79 -16
  214. package/dist/tooling/shared/{yk9mp7vb.js → qdrn8ghn.js} +5 -4
  215. package/dist/tooling/shared/rd8h01ta.js +20 -0
  216. package/dist/tooling/shared/{bz2dqhab.js → rwg8y90m.js} +3 -3
  217. package/dist/tooling/shared/{xhrnjch9.js → ry2f3w8p.js} +3 -3
  218. package/dist/tooling/shared/{a7q7t4n3.js → ryc5sa58.js} +8 -8
  219. package/dist/tooling/shared/{2revefqt.js → ss87qxwv.js} +2 -2
  220. package/dist/tooling/shared/{rf83gr2d.js → vg68k11z.js} +7 -3
  221. package/dist/tooling/shared/{p7f08ktn.js → vms9dta5.js} +73 -89
  222. package/dist/tooling/shared/{3rrw5w8f.js → xchpnbdv.js} +1 -1
  223. package/dist/tooling/shared/{djydjpqt.js → xn6p01fh.js} +5 -5
  224. package/dist/tooling/shared/{9kazs5p6.js → xwr6q618.js} +3 -3
  225. package/dist/tooling/shared/{ep8zh3bk.js → xx3gmq94.js} +1 -1
  226. package/dist/tooling/shared/{tn8gay14.js → y2bd7yfx.js} +21 -13
  227. package/dist/tooling/shared/{vraj6d74.js → y4gq9rbe.js} +11 -11
  228. package/dist/tooling/shared/y4vwft92.js +9 -0
  229. package/dist/tooling/shared/{345tbc2k.js → yz68xcmc.js} +1 -1
  230. package/dist/tooling/testing/factories.d.ts +1 -3
  231. package/dist/tooling/testing/index.js +2 -2
  232. package/package.json +39 -9
  233. package/dist/shared/3449rcae.js +0 -14
  234. package/dist/shared/fjpnhqan.js +0 -71
  235. package/dist/shared/n41w4fbz.js +0 -113
  236. package/dist/tooling/shared/pw8fgahx.js +0 -69
  237. /package/dist/shared/{ae4j5f42.js → mfvx6kjx.js} +0 -0
@@ -1,7 +1,3 @@
1
- import {
2
- VALIDATION_NOT_IMPORTED2,
3
- VALIDATION_NOT_IMPORTED_FIX2
4
- } from "./n442h54c.js";
5
1
  import {
6
2
  roleObject,
7
3
  grantObject,
@@ -9,13 +5,22 @@ import {
9
5
  serializeCatalog2,
10
6
  catalogHash2,
11
7
  startupCatalog2,
8
+ startupHash2,
12
9
  catalogFileText2,
13
10
  parseCatalog2
14
11
  } from "./12z7aznj.js";
12
+ import {
13
+ quoteIdent2
14
+ } from "./fk9vef30.js";
15
+ import {
16
+ VALIDATION_NOT_IMPORTED2,
17
+ VALIDATION_NOT_IMPORTED_FIX2
18
+ } from "./n442h54c.js";
15
19
  import {
16
20
  open2
17
- } from "./35q0n9tz.js";
21
+ } from "./jvxcp1qk.js";
18
22
  import {
23
+ driverUrl,
19
24
  sqlStatements,
20
25
  transactionControl,
21
26
  canonicalTypeName,
@@ -35,7 +40,7 @@ import {
35
40
  assertTargetAlias,
36
41
  listTargets,
37
42
  selectTarget
38
- } from "./dpc2r7rb.js";
43
+ } from "./bdekbvmt.js";
39
44
  import {
40
45
  contributedFields
41
46
  } from "./n0dgs0pb.js";
@@ -54,7 +59,7 @@ import {
54
59
  import {
55
60
  snakeCase,
56
61
  emittedTypeName
57
- } from "./gcefca6p.js";
62
+ } from "./f9t9g54z.js";
58
63
  import {
59
64
  ColumnBuilder2
60
65
  } from "./606g0nbf.js";
@@ -78,6 +83,379 @@ function projectPath(cwd, ...parts) {
78
83
  return resolve(cwd, ...parts);
79
84
  }
80
85
 
86
+ // src/tooling/migrate/export.ts
87
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
88
+ import { isAbsolute, join } from "node:path";
89
+ var EXPORT_FORMAT = 1;
90
+ var LOGICAL_FORMAT = 1;
91
+ function snapshotFromCatalog(source, namespace, database) {
92
+ return {
93
+ format: EXPORT_FORMAT,
94
+ logical: { namespace, name: namespace, format: LOGICAL_FORMAT },
95
+ catalogHash: startupHash2(source),
96
+ ...database !== undefined ? { database } : {},
97
+ tables: exportTables(source)
98
+ };
99
+ }
100
+ function serializeSnapshot(snapshot) {
101
+ return `${JSON.stringify(snapshot, null, 2)}
102
+ `;
103
+ }
104
+ function parseSnapshot(path, text) {
105
+ let parsed;
106
+ try {
107
+ parsed = JSON.parse(text);
108
+ } catch {
109
+ throw snapshotError(path, "stale", `Catalog snapshot ${path} is not JSON.`);
110
+ }
111
+ if (!isSnapshot(parsed)) {
112
+ throw snapshotError(path, "stale", `Catalog snapshot ${path} is stale: the file format is not ${String(EXPORT_FORMAT)}.`);
113
+ }
114
+ return parsed;
115
+ }
116
+ function databaseName(url) {
117
+ try {
118
+ const name = decodeURIComponent(new URL(url).pathname.replace(/^\//, ""));
119
+ return name.length > 0 ? name : undefined;
120
+ } catch {
121
+ return;
122
+ }
123
+ }
124
+ function catalogNamespace(config) {
125
+ return config.namespace ?? "public";
126
+ }
127
+ function loadSnapshots(cwd, config) {
128
+ const paths = config.external ?? [];
129
+ return paths.map((path) => {
130
+ const file = resolveSnapshot(cwd, path);
131
+ if (!existsSync(file)) {
132
+ throw snapshotError(path, "missing", `Catalog snapshot ${path} is missing.`);
133
+ }
134
+ return { path, snapshot: parseSnapshot(path, readFileSync(file, "utf8")) };
135
+ });
136
+ }
137
+ function externalNamespaceMap(snapshots) {
138
+ const names = new Map;
139
+ for (const loaded of snapshots) {
140
+ for (const table of loaded.snapshot.tables)
141
+ names.set(table.name, table.namespace);
142
+ }
143
+ return names;
144
+ }
145
+ function couplingFindings(source) {
146
+ const findings = [];
147
+ for (const object of source.objects) {
148
+ if (object.kind !== "constraint" || object.definition.constraintKind !== "foreignKey")
149
+ continue;
150
+ const references = object.definition.references;
151
+ if (references === undefined)
152
+ continue;
153
+ const from = object.identity.parent.namespace;
154
+ const to = references.parent.namespace;
155
+ if (from.form !== "static" || to.form !== "static" || from.name === to.name)
156
+ continue;
157
+ findings.push({
158
+ severity: "warning",
159
+ code: "coupling",
160
+ place: `${from.name}.${object.identity.parent.name}.${object.definition.columns.join(",")}`,
161
+ reason: `references ${to.name}.${references.parent.name}.${references.columns.join(",")}`,
162
+ fix: "Cross-catalog foreign keys are allowed inside one database."
163
+ });
164
+ }
165
+ return findings;
166
+ }
167
+ function assertSnapshots(cwd, config, source) {
168
+ if ((config.external ?? []).length === 0)
169
+ return;
170
+ const snapshots = loadSnapshots(cwd, config);
171
+ for (const table of externalTables(source)) {
172
+ const found = findTable(snapshots, table.namespace, table.name);
173
+ if (found === undefined) {
174
+ throw snapshotError(table.name, "missing", `External table ${table.namespace}.${table.name} is missing from the catalog snapshots.`);
175
+ }
176
+ const differ = columnDifference(table.columns, found.table.columns);
177
+ if (differ !== undefined) {
178
+ throw snapshotError(found.path, "stale", `Catalog snapshot ${found.path} is stale: ${table.namespace}.${table.name}.${differ} does not match the export.`);
179
+ }
180
+ }
181
+ }
182
+ async function assertLiveExternal(url, source, snapshots) {
183
+ if (snapshots.length === 0 && externalTables(source).length === 0)
184
+ return;
185
+ const postgres = (await import("postgres")).default;
186
+ const sql = postgres(driverUrl(url), {
187
+ max: 1,
188
+ connect_timeout: 5,
189
+ idle_timeout: 1,
190
+ onnotice: () => {}
191
+ });
192
+ try {
193
+ const current = await sql`select current_database() as name`;
194
+ const database = current[0]?.name;
195
+ if (database !== undefined)
196
+ assertSameDatabase(source, snapshots, database);
197
+ for (const table of externalTables(source)) {
198
+ const found = findTable(snapshots, table.namespace, table.name);
199
+ if (found === undefined)
200
+ continue;
201
+ await assertLiveTable(sql, found.path, found.snapshot, table);
202
+ }
203
+ } finally {
204
+ await sql.end({ timeout: 5 });
205
+ }
206
+ }
207
+ function renderExport(cwd, config, source, args) {
208
+ const flags = exportFlags(args);
209
+ const database = exportDatabase(config);
210
+ const snapshot = snapshotFromCatalog(source, catalogNamespace(config), database);
211
+ const text = serializeSnapshot(snapshot);
212
+ if (flags.out !== undefined) {
213
+ const path = isAbsolute(flags.out) ? flags.out : join(cwd, flags.out);
214
+ writeFileSync(path, text);
215
+ }
216
+ if (flags.json || flags.out === undefined)
217
+ return text;
218
+ return `${flags.out}
219
+ `;
220
+ }
221
+ function exportTables(source) {
222
+ const tables = [];
223
+ for (const object of source.objects) {
224
+ if (object.kind !== "table" || object.owner === "external" || object.owner === "ignored") {
225
+ continue;
226
+ }
227
+ if (object.identity.namespace.form !== "static")
228
+ continue;
229
+ const columns = [];
230
+ for (const column of source.objects) {
231
+ if (!isColumnOf(column, object.identity.namespace.name, object.identity.name))
232
+ continue;
233
+ columns.push({
234
+ name: column.identity.name,
235
+ type: column.definition.dataType,
236
+ nullable: column.definition.nullable
237
+ });
238
+ }
239
+ tables.push({
240
+ namespace: object.identity.namespace.name,
241
+ name: object.identity.name,
242
+ columns
243
+ });
244
+ }
245
+ return tables;
246
+ }
247
+ function isColumnOf(object, namespace, table) {
248
+ return object.kind === "column" && object.identity.parent.name === table && object.identity.parent.namespace.form === "static" && object.identity.parent.namespace.name === namespace;
249
+ }
250
+ function externalTables(source) {
251
+ const tables = [];
252
+ for (const object of source.objects) {
253
+ if (object.kind !== "table" || object.owner !== "external")
254
+ continue;
255
+ if (object.identity.namespace.form !== "static")
256
+ continue;
257
+ const columns = [];
258
+ for (const column of source.objects) {
259
+ if (!isColumnOf(column, object.identity.namespace.name, object.identity.name))
260
+ continue;
261
+ columns.push({
262
+ name: column.identity.name,
263
+ type: column.definition.dataType,
264
+ nullable: column.definition.nullable
265
+ });
266
+ }
267
+ tables.push({ namespace: object.identity.namespace.name, name: object.identity.name, columns });
268
+ }
269
+ return tables;
270
+ }
271
+ function findTable(snapshots, namespace, name) {
272
+ for (const loaded of snapshots) {
273
+ for (const table of loaded.snapshot.tables) {
274
+ if (table.name === name && (table.namespace === namespace || namespace === "public")) {
275
+ return { path: loaded.path, snapshot: loaded.snapshot, table };
276
+ }
277
+ }
278
+ }
279
+ for (const loaded of snapshots) {
280
+ for (const table of loaded.snapshot.tables) {
281
+ if (table.name === name)
282
+ return { path: loaded.path, snapshot: loaded.snapshot, table };
283
+ }
284
+ }
285
+ return;
286
+ }
287
+ function columnDifference(left, right) {
288
+ const byName = new Map(right.map((column) => [column.name, column]));
289
+ for (const column of left) {
290
+ const other = byName.get(column.name);
291
+ if (other === undefined)
292
+ return column.name;
293
+ if (other.type !== column.type || other.nullable !== column.nullable)
294
+ return column.name;
295
+ }
296
+ for (const column of right) {
297
+ if (!left.some((item) => item.name === column.name))
298
+ return column.name;
299
+ }
300
+ return;
301
+ }
302
+ function assertSameDatabase(source, snapshots, database) {
303
+ if (!hasCrossCatalogForeignKey(source))
304
+ return;
305
+ for (const loaded of snapshots) {
306
+ const name = loaded.snapshot.database;
307
+ if (name !== undefined && name !== database) {
308
+ throw new OkmError2("OKM1842", `A foreign key references ${loaded.snapshot.logical.namespace} in database ${name}. This database is ${database}.`, {
309
+ kind: "invalid",
310
+ fieldReason: "database",
311
+ fix: {
312
+ summary: "Keep the foreign key inside one database. Cross-catalog keys are allowed only there."
313
+ }
314
+ });
315
+ }
316
+ }
317
+ }
318
+ function hasCrossCatalogForeignKey(source) {
319
+ for (const object of source.objects) {
320
+ if (object.kind !== "constraint" || object.definition.constraintKind !== "foreignKey")
321
+ continue;
322
+ const references = object.definition.references;
323
+ if (references === undefined)
324
+ continue;
325
+ const from = object.identity.parent.namespace;
326
+ const to = references.parent.namespace;
327
+ if (from.form === "static" && to.form === "static" && from.name !== to.name)
328
+ return true;
329
+ }
330
+ return false;
331
+ }
332
+ async function assertLiveTable(sql, path, snapshot, table) {
333
+ const hash = await liveHash(sql, table.namespace);
334
+ if (hash !== undefined && hash !== snapshot.catalogHash) {
335
+ throw snapshotError(path, "stale", `Catalog snapshot ${path} is stale: the catalog hash changed.`);
336
+ }
337
+ const live = await liveColumns(sql, table.namespace, table.name);
338
+ if (live === undefined) {
339
+ throw snapshotError(path, "missing", `External table ${table.namespace}.${table.name} is missing on the database.`);
340
+ }
341
+ const differ = columnDifference(snapshotTableColumns(snapshot, table), live);
342
+ if (differ !== undefined) {
343
+ throw snapshotError(path, "stale", `Catalog snapshot ${path} is stale: ${table.namespace}.${table.name}.${differ} is missing on the database.`);
344
+ }
345
+ }
346
+ function snapshotTableColumns(snapshot, table) {
347
+ for (const item of snapshot.tables) {
348
+ if (item.name === table.name)
349
+ return item.columns;
350
+ }
351
+ return table.columns;
352
+ }
353
+ async function liveHash(sql, namespace) {
354
+ const reg = `${quoteIdent2(namespace)}.${quoteIdent2("okm_meta")}`;
355
+ const present = await sql.unsafe(`select to_regclass('${reg}')::text as reg`);
356
+ if (present[0]?.reg == null)
357
+ return;
358
+ const rows = await sql.unsafe(`select catalog_hash from ${reg} where id = 'head'`);
359
+ return rows[0]?.catalog_hash;
360
+ }
361
+ async function liveColumns(sql, namespace, table) {
362
+ const rows = await sql`select a.attname as name, format_type(a.atttypid, a.atttypmod) as type, a.attnotnull as not_null
363
+ from pg_attribute a
364
+ join pg_class c on c.oid = a.attrelid
365
+ join pg_namespace n on n.oid = c.relnamespace
366
+ where n.nspname = ${namespace} and c.relname = ${table}
367
+ and a.attnum > 0 and not a.attisdropped
368
+ order by a.attnum`;
369
+ if (rows.length === 0) {
370
+ const exists = await sql`
371
+ select to_regclass(${`${quoteIdent2(namespace)}.${quoteIdent2(table)}`})::text as reg
372
+ `;
373
+ if (exists[0]?.reg == null)
374
+ return;
375
+ }
376
+ return rows.map((row) => ({ name: row.name, type: row.type, nullable: !row.not_null }));
377
+ }
378
+ function resolveSnapshot(cwd, path) {
379
+ return isAbsolute(path) ? path : projectPath(cwd, path);
380
+ }
381
+ function exportDatabase(config) {
382
+ const target = config.database;
383
+ if (typeof target === "string")
384
+ return databaseName(target);
385
+ if (target?.url !== undefined)
386
+ return databaseName(target.url);
387
+ return;
388
+ }
389
+ function exportFlags(args) {
390
+ let json = false;
391
+ let out;
392
+ for (let index = 0;index < args.length; index += 1) {
393
+ const arg = args[index] ?? "";
394
+ if (arg === "--json") {
395
+ json = true;
396
+ continue;
397
+ }
398
+ if (arg === "--out") {
399
+ const value = args[index + 1];
400
+ if (value === undefined || value.startsWith("--")) {
401
+ throw new OkmError2("invalid", "--out needs a file.", {
402
+ fix: { summary: "Pass --out <file>." }
403
+ });
404
+ }
405
+ out = value;
406
+ index += 1;
407
+ continue;
408
+ }
409
+ if (arg.startsWith("--out=")) {
410
+ out = arg.slice("--out=".length);
411
+ continue;
412
+ }
413
+ throw new OkmError2("invalid", `Unknown flag ${arg}.`, {
414
+ fix: { summary: "okm catalog export accepts --json and --out <file>." }
415
+ });
416
+ }
417
+ return out === undefined ? { json } : { json, out };
418
+ }
419
+ function snapshotError(path, reason, message) {
420
+ return new OkmError2("OKM1841", message, {
421
+ kind: reason === "missing" ? "not_found" : "schema_drift",
422
+ fieldReason: reason,
423
+ columns: [path],
424
+ fix: {
425
+ summary: reason === "missing" ? "Point external at the file okm catalog export wrote, and apply the owning catalog." : "Run okm catalog export in the owning catalog and update the dependent."
426
+ }
427
+ });
428
+ }
429
+ function isSnapshot(value) {
430
+ if (!isRecord(value) || value.format !== EXPORT_FORMAT)
431
+ return false;
432
+ if (typeof value.catalogHash !== "string")
433
+ return false;
434
+ if (!isLogical(value.logical))
435
+ return false;
436
+ if (value.database !== undefined && typeof value.database !== "string")
437
+ return false;
438
+ if (!Array.isArray(value.tables))
439
+ return false;
440
+ return value.tables.every(isTable);
441
+ }
442
+ function isLogical(value) {
443
+ if (!isRecord(value))
444
+ return false;
445
+ return typeof value.namespace === "string" && typeof value.name === "string" && value.format === LOGICAL_FORMAT;
446
+ }
447
+ function isTable(value) {
448
+ if (!isRecord(value) || typeof value.namespace !== "string" || typeof value.name !== "string") {
449
+ return false;
450
+ }
451
+ if (!Array.isArray(value.columns))
452
+ return false;
453
+ return value.columns.every((column) => isRecord(column) && typeof column.name === "string" && typeof column.type === "string" && typeof column.nullable === "boolean");
454
+ }
455
+ function isRecord(value) {
456
+ return typeof value === "object" && value !== null;
457
+ }
458
+
81
459
  // src/tooling/errors/registry.ts
82
460
  var ERROR_DOCS = [
83
461
  {
@@ -209,7 +587,7 @@ var ERROR_DOCS = [
209
587
  {
210
588
  code: "OKM1120",
211
589
  title: "Unknown field",
212
- summary: "The name is not a field or an option. timeout and signal go in the second argument of update. Options go first: tx(options, fn).",
590
+ summary: "The name is not a field or an option on a write, read, or update. An unknown key on insert, update, upsert, or first-or-create is refused and names the key and the table. timeout and signal go in the second argument of update. Options go first: tx(options, fn).",
213
591
  fix: "Use a field from the catalog, or move the option. The error includes a nearest-name hint when one is close."
214
592
  },
215
593
  {
@@ -239,8 +617,14 @@ var ERROR_DOCS = [
239
617
  {
240
618
  code: "OKM1125",
241
619
  title: "Bad SQL fragment",
242
- summary: "A composed fragment is empty, unbalanced, or interpolates a value it cannot bind. A select fragment that is not a number is the same code.",
243
- fix: "Interpolate a string, number, boolean, null, ident(name), fragment.column, or another fragment. Keep parentheses and quotes balanced. A select fragment is a numeric expression named with as()."
620
+ summary: "A composed fragment is empty, unbalanced, includes a backslash, or interpolates a value it cannot bind. A select fragment that is not a number is the same code.",
621
+ fix: "Interpolate a string, number, boolean, null, ident(name), fragment.column, or another fragment. Keep parentheses and quotes balanced. Pass a value that needs a backslash as an interpolation. A select fragment is a numeric expression named with as()."
622
+ },
623
+ {
624
+ code: "OKM1126",
625
+ title: "Parameter limit",
626
+ summary: "The finished statement has more than 65,535 parameters. The limit is for the whole statement (filters, inList, limit, values), not for inList alone.",
627
+ fix: "Chunk the work, or filter with = any(array) instead of a long inList."
244
628
  },
245
629
  {
246
630
  code: "OKM1130",
@@ -455,7 +839,7 @@ var ERROR_DOCS = [
455
839
  {
456
840
  code: "OKM1538",
457
841
  title: "Type rewrite",
458
- summary: "A statement changes a column type in a way that rewrites the table.",
842
+ summary: "A statement changes a column type in a way that rewrites the table. Binary-coercible changes, varchar widening, varchar to text, and a numeric precision increase at the same scale do not.",
459
843
  fix: "There is no safe form for a rewriting type change in this version. A column swap is expand and contract work for a later step. OKM1524 already requires a reason for the type change."
460
844
  },
461
845
  {
@@ -536,6 +920,12 @@ var ERROR_DOCS = [
536
920
  summary: "A step contains COMMIT, ROLLBACK, or other transaction control, including inside a DO body. A function or procedure body is not this.",
537
921
  fix: "Remove the transaction control. okm migrate check replays one transaction and rolls it back. Apply accepts the step only with -- okm-allow OKM1551: reason."
538
922
  },
923
+ {
924
+ code: "OKM1552",
925
+ title: "Reprinted declaration",
926
+ summary: "A domain check or a LANGUAGE sql function body is stored differently from the text in the migration catalog.",
927
+ fix: "Write the check or the body the way Postgres prints it, or allow OKM1552 with a reason. The warning does not fail apply. A domain whose check differs is still OKM1547."
928
+ },
539
929
  {
540
930
  code: "OKM1601",
541
931
  title: "Stale typed SQL",
@@ -751,6 +1141,18 @@ var ERROR_DOCS = [
751
1141
  title: "Pooler session",
752
1142
  summary: "Two statements on the same connection saw different backend pids. A transaction-mode pooler is in the way.",
753
1143
  fix: "Use a direct connection, or pass --allow-pooler."
1144
+ },
1145
+ {
1146
+ code: "OKM1855",
1147
+ title: "Pull would overwrite",
1148
+ summary: "okm pull or okm import drizzle found a file it would replace. It writes nothing.",
1149
+ fix: "Pass --out <directory> to write somewhere else, or move the file."
1150
+ },
1151
+ {
1152
+ code: "OKM1856",
1153
+ title: "Drizzle is not installed",
1154
+ summary: "okm import drizzle could not resolve drizzle-orm from the project.",
1155
+ fix: "Install drizzle-orm in the project that owns the schema."
754
1156
  }
755
1157
  ];
756
1158
  var byCode;
@@ -767,25 +1169,25 @@ function indexDocs() {
767
1169
  }
768
1170
 
769
1171
  // src/tooling/migrate/files.ts
770
- import { existsSync, readdirSync, readFileSync } from "node:fs";
771
- import { join } from "node:path";
1172
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
1173
+ import { join as join2 } from "node:path";
772
1174
  function loadMigrations(directory) {
773
- if (!existsSync(directory))
1175
+ if (!existsSync2(directory))
774
1176
  return [];
775
1177
  const names = readdirSync(directory).filter((file) => file.endsWith(".sql")).sort();
776
1178
  const migrations = [];
777
1179
  for (const file of names) {
778
1180
  const id = file.slice(0, -".sql".length);
779
- const catalogPath = join(directory, `${id}.catalog.json`);
780
- if (!existsSync(catalogPath)) {
1181
+ const catalogPath = join2(directory, `${id}.catalog.json`);
1182
+ if (!existsSync2(catalogPath)) {
781
1183
  throw new OkmError2("invalid", `Migration ${id} is missing its catalog snapshot.`, {
782
1184
  fix: {
783
1185
  summary: "Generate the migration again so the SQL and the catalog are written together."
784
1186
  }
785
1187
  });
786
1188
  }
787
- const plan = readMigrationPlan(join(directory, file));
788
- const stored = parseCatalog2(readFileSync(catalogPath, "utf8"));
1189
+ const plan = readMigrationPlan(join2(directory, file));
1190
+ const stored = parseCatalog2(readFileSync2(catalogPath, "utf8"));
789
1191
  const startup = startupCatalog2(stored);
790
1192
  const hash = catalogHash2(startup);
791
1193
  migrations.push(startup === stored ? { id, catalogHash: hash, steps: plan.steps } : { id, catalogHash: hash, legacyHash: catalogHash2(stored), steps: plan.steps });
@@ -793,7 +1195,7 @@ function loadMigrations(directory) {
793
1195
  return migrations;
794
1196
  }
795
1197
  function readMigrationPlan(path) {
796
- const text = prepareMigrationText(readFileSync(path, "utf8"), path);
1198
+ const text = prepareMigrationText(readFileSync2(path, "utf8"), path);
797
1199
  const plan = parsePlan(text);
798
1200
  assertMigrationSteps(text, plan.steps.length, path);
799
1201
  return plan;
@@ -806,13 +1208,13 @@ function loadHeadSnapshot(directory) {
806
1208
  return {
807
1209
  id: last.id,
808
1210
  catalogHash: last.catalogHash,
809
- catalog: parseCatalog2(readFileSync(join(directory, `${last.id}.catalog.json`), "utf8"))
1211
+ catalog: parseCatalog2(readFileSync2(join2(directory, `${last.id}.catalog.json`), "utf8"))
810
1212
  };
811
1213
  }
812
1214
 
813
1215
  // src/tooling/migrate/lint.ts
814
- import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
815
- import { join as join2 } from "node:path";
1216
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "node:fs";
1217
+ import { join as join3 } from "node:path";
816
1218
 
817
1219
  // src/tooling/migrate/lint-rules.ts
818
1220
  var INTEGER_WIDTH = {
@@ -899,7 +1301,7 @@ function rule(code, category, severity, doc, check) {
899
1301
  function hit(matched, reason) {
900
1302
  return matched ? [reason] : [];
901
1303
  }
902
- function normalized(sql) {
1304
+ function normalizedSql(sql) {
903
1305
  const collapsed = sql.trim().replace(/\s+/g, " ");
904
1306
  let out = "";
905
1307
  let quoted = false;
@@ -914,7 +1316,7 @@ function normalized(sql) {
914
1316
  return out;
915
1317
  }
916
1318
  function weakensRls(input) {
917
- const text = normalized(input.step.sql);
1319
+ const text = normalizedSql(input.step.sql);
918
1320
  if (/\bdisable row level security\b/.test(text))
919
1321
  return ["disables row level security"];
920
1322
  if (/\bno force row level security\b/.test(text))
@@ -1031,7 +1433,7 @@ function parenthesized(text, open) {
1031
1433
  }
1032
1434
  function policyExpression(input, table, name) {
1033
1435
  const expression = expressionOf(input.after, table, name) ?? expressionOf(input.before, table, name);
1034
- return expression === undefined ? undefined : normalized(expression);
1436
+ return expression === undefined ? undefined : normalizedSql(expression);
1035
1437
  }
1036
1438
  function expressionOf(source, table, name) {
1037
1439
  for (const object of source.objects) {
@@ -1054,7 +1456,7 @@ function hasPolicy(source, table) {
1054
1456
  return source.objects.some((object) => object.kind === "policy" && object.identity.parent.name === table);
1055
1457
  }
1056
1458
  function dataStatement(step) {
1057
- const text = normalized(step.sql);
1459
+ const text = normalizedSql(step.sql);
1058
1460
  if (/^(?:do|call)\b/.test(text))
1059
1461
  return ["the body is not analysed"];
1060
1462
  if (step.backfill !== undefined)
@@ -1068,16 +1470,16 @@ function dataStatement(step) {
1068
1470
  return [];
1069
1471
  }
1070
1472
  function matches(step, kind, pattern) {
1071
- return pattern.test(normalized(step.sql)) || step.kind === kind;
1473
+ return pattern.test(normalizedSql(step.sql)) || step.kind === kind;
1072
1474
  }
1073
1475
  function renamesTable(step) {
1074
- const text = normalized(step.sql);
1476
+ const text = normalizedSql(step.sql);
1075
1477
  if (/^alter table\s+\S+\s+rename to\b/.test(text))
1076
1478
  return true;
1077
1479
  return step.kind === "rename-table" && !/^alter table\s+\S+\s+rename (column|constraint)\b/.test(text);
1078
1480
  }
1079
1481
  function droppedType(input) {
1080
- const text = normalized(input.step.sql);
1482
+ const text = normalizedSql(input.step.sql);
1081
1483
  const dropping = /^drop type\b/.test(text);
1082
1484
  if (!dropping && input.step.kind !== "drop-enum" && input.step.kind !== "drop-domain") {
1083
1485
  return;
@@ -1093,7 +1495,7 @@ function droppedType(input) {
1093
1495
  return "enum";
1094
1496
  }
1095
1497
  function requiredColumn(input) {
1096
- const text = normalized(input.step.sql);
1498
+ const text = normalizedSql(input.step.sql);
1097
1499
  const adding = /^alter table\s+\S+\s+add column\b/.test(text) || input.step.kind === "add-column-required";
1098
1500
  if (!adding)
1099
1501
  return false;
@@ -1109,7 +1511,7 @@ function requiredColumn(input) {
1109
1511
  function uniqueConstraint(input) {
1110
1512
  if (!tableExists(input))
1111
1513
  return false;
1112
- const text = normalized(input.step.sql);
1514
+ const text = normalizedSql(input.step.sql);
1113
1515
  if (/\busing index\b/.test(text))
1114
1516
  return false;
1115
1517
  if (/^alter table\s+\S+\s+add constraint\b/.test(text) && /\b(unique|primary key)\b/.test(text)) {
@@ -1120,16 +1522,16 @@ function uniqueConstraint(input) {
1120
1522
  const name = createdIndexName(text);
1121
1523
  if (name === undefined)
1122
1524
  return false;
1123
- return input.later.some((step) => usesIndex(normalized(step.sql), name));
1525
+ return input.later.some((step) => usesIndex(normalizedSql(step.sql), name));
1124
1526
  }
1125
1527
  function uniqueIndex(input) {
1126
- const text = normalized(input.step.sql);
1528
+ const text = normalizedSql(input.step.sql);
1127
1529
  if (!/^create unique index\b/.test(text))
1128
1530
  return false;
1129
1531
  if (!tableExists(input))
1130
1532
  return false;
1131
1533
  const name = createdIndexName(text);
1132
- if (name !== undefined && input.later.some((step) => usesIndex(normalized(step.sql), name))) {
1534
+ if (name !== undefined && input.later.some((step) => usesIndex(normalizedSql(step.sql), name))) {
1133
1535
  return false;
1134
1536
  }
1135
1537
  return true;
@@ -1142,7 +1544,7 @@ function usesIndex(text, name) {
1142
1544
  return text.includes(`using index "${name}"`) || text.includes(`using index ${name}`);
1143
1545
  }
1144
1546
  function unsafeSetNotNull(input) {
1145
- const text = normalized(input.step.sql);
1547
+ const text = normalizedSql(input.step.sql);
1146
1548
  const match = /^alter table\s+(\S+)\s+alter column\s+(\S+)\s+set not null\b/.exec(text);
1147
1549
  if (match === null && input.step.kind !== "set-not-null")
1148
1550
  return false;
@@ -1158,21 +1560,21 @@ function unsafeSetNotNull(input) {
1158
1560
  function validatedNotNullCheck(earlier, table, column) {
1159
1561
  const needle = `check (${column} is not null) not valid`;
1160
1562
  for (const step of earlier) {
1161
- const text = normalized(step.sql);
1563
+ const text = normalizedSql(step.sql);
1162
1564
  if (!text.startsWith(`alter table ${table} `) || !text.includes(needle))
1163
1565
  continue;
1164
1566
  const added = /^alter table\s+\S+\s+add constraint\s+(\S+)\s+check\b/.exec(text);
1165
1567
  const name = added?.[1];
1166
1568
  if (name === undefined)
1167
1569
  continue;
1168
- const validated = earlier.some((item) => normalized(item.sql).startsWith(`alter table ${table} validate constraint ${name}`));
1570
+ const validated = earlier.some((item) => normalizedSql(item.sql).startsWith(`alter table ${table} validate constraint ${name}`));
1169
1571
  if (validated)
1170
1572
  return true;
1171
1573
  }
1172
1574
  return false;
1173
1575
  }
1174
1576
  function blockingIndex(input) {
1175
- const text = normalized(input.step.sql);
1577
+ const text = normalizedSql(input.step.sql);
1176
1578
  if (!/^create(?: unique)? index\b/.test(text) && input.step.kind !== "create-index")
1177
1579
  return false;
1178
1580
  if (/\bconcurrently\b/.test(text))
@@ -1180,20 +1582,40 @@ function blockingIndex(input) {
1180
1582
  return tableExists(input);
1181
1583
  }
1182
1584
  function checkWithoutNotValid(input) {
1183
- return addedConstraint(input, "check") && !/\bnot valid\b/.test(normalized(input.step.sql));
1585
+ return addedConstraint(input, "check") && !/\bnot valid\b/.test(normalizedSql(input.step.sql));
1184
1586
  }
1185
1587
  function foreignKeyWithoutNotValid(input) {
1186
- return addedConstraint(input, "foreign key") && !/\bnot valid\b/.test(normalized(input.step.sql));
1588
+ return addedConstraint(input, "foreign key") && !/\bnot valid\b/.test(normalizedSql(input.step.sql));
1187
1589
  }
1188
1590
  function validatingCheck(input) {
1189
1591
  if (widenValidate(input))
1190
1592
  return false;
1593
+ if (notNullFloorValidate(input))
1594
+ return false;
1191
1595
  if (validates(input, "check"))
1192
1596
  return true;
1193
1597
  return checkWithoutNotValid(input);
1194
1598
  }
1599
+ function notNullFloorValidate(input) {
1600
+ const text = normalizedSql(input.step.sql);
1601
+ const name = /^alter table\s+(\S+)\s+validate constraint\s+(\S+)/.exec(text);
1602
+ if (name === null)
1603
+ return false;
1604
+ const table = name[1];
1605
+ const constraint = name[2];
1606
+ if (table === undefined || constraint === undefined)
1607
+ return false;
1608
+ return input.earlier.some((step) => {
1609
+ const prior = normalizedSql(step.sql);
1610
+ if (!prior.startsWith(`alter table ${table} `))
1611
+ return false;
1612
+ if (!prior.includes(`add constraint ${constraint} `))
1613
+ return false;
1614
+ return /check\s*\(\s*\S+\s+is not null\s*\)\s*not valid/.test(prior);
1615
+ });
1616
+ }
1195
1617
  function widenValidate(input) {
1196
- const text = normalized(input.step.sql);
1618
+ const text = normalizedSql(input.step.sql);
1197
1619
  if (!/^alter table\s+\S+\s+validate constraint\b/.test(text))
1198
1620
  return false;
1199
1621
  if (input.step.kind === "widen-check")
@@ -1204,7 +1626,7 @@ function widenValidate(input) {
1204
1626
  return input.earlier.some((step) => {
1205
1627
  if (step.kind !== "widen-check")
1206
1628
  return false;
1207
- const prior = normalized(step.sql);
1629
+ const prior = normalizedSql(step.sql);
1208
1630
  return prior.includes(`add constraint ${name} `);
1209
1631
  });
1210
1632
  }
@@ -1216,13 +1638,13 @@ function validatingForeignKey(input) {
1216
1638
  function addedConstraint(input, kind) {
1217
1639
  if (!tableExists(input))
1218
1640
  return false;
1219
- const text = normalized(input.step.sql);
1641
+ const text = normalizedSql(input.step.sql);
1220
1642
  if (!/^alter table\s+\S+\s+add constraint\b/.test(text))
1221
1643
  return false;
1222
1644
  return new RegExp(`\\b${kind}\\b`).test(text);
1223
1645
  }
1224
1646
  function validates(input, kind) {
1225
- const text = normalized(input.step.sql);
1647
+ const text = normalizedSql(input.step.sql);
1226
1648
  const table = /^alter table\s+\S+\s+validate constraint\s+(\S+)/.exec(text);
1227
1649
  if (table !== null) {
1228
1650
  const token = table[1];
@@ -1242,7 +1664,7 @@ function validates(input, kind) {
1242
1664
  }
1243
1665
  var TYPE_CHANGE = /^alter table\s+(\S+)\s+alter column\s+(\S+)\s+(?:set data type|type)\b/;
1244
1666
  function typeChange(input, mode) {
1245
- const text = normalized(input.step.sql);
1667
+ const text = normalizedSql(input.step.sql);
1246
1668
  const changing = TYPE_CHANGE.test(text) || input.step.kind === "set-column-type";
1247
1669
  if (!changing)
1248
1670
  return [];
@@ -1258,18 +1680,19 @@ function typeChange(input, mode) {
1258
1680
  const shrink = lengthShrink(before, after);
1259
1681
  if (mode === "shrink")
1260
1682
  return shrink ? ["shortens a character length"] : [];
1261
- if (shrink)
1262
- return [];
1683
+ if (shrink) {
1684
+ return mode === "rewrite" && tableRewrites(before, after, input) ? ["rewrites the table"] : [];
1685
+ }
1263
1686
  if (mode === "narrow")
1264
1687
  return narrows(before, after) ? ["narrows a type"] : [];
1265
- if (lengthGrow(before, after))
1688
+ if (lengthGrow(before, after) && mode !== "rewrite")
1266
1689
  return [];
1267
1690
  if (mode === "incompatible")
1268
1691
  return ["changes a column type"];
1269
- return rewrites(before, after) ? ["rewrites the table"] : [];
1692
+ return tableRewrites(before, after, input) ? ["rewrites the table"] : [];
1270
1693
  }
1271
1694
  function columnTypes(input) {
1272
- const text = normalized(input.step.sql);
1695
+ const text = normalizedSql(input.step.sql);
1273
1696
  const match = TYPE_CHANGE.exec(text);
1274
1697
  const table = lastName(match?.[1]);
1275
1698
  const column = lastName(match?.[2]);
@@ -1294,13 +1717,115 @@ function lengthGrow(before, after) {
1294
1717
  const right = charLength(after);
1295
1718
  return left !== undefined && right !== undefined && left.family === right.family && right.length > left.length;
1296
1719
  }
1297
- function rewrites(before, after) {
1298
- if (lengthShrink(before, after) || lengthGrow(before, after))
1720
+ function tableRewrites(before, after, input) {
1721
+ if (usingRewrites(input))
1722
+ return true;
1723
+ if (varyingRelaxation(before, after))
1724
+ return false;
1725
+ if (isVarying(before) && after === "text")
1726
+ return false;
1727
+ if (before === "text" && unconstrainedVarying(after))
1728
+ return false;
1729
+ if ((before === "text" || isVarying(before)) && unconstrainedCharacter(after))
1730
+ return false;
1731
+ if (numericRelaxation(before, after))
1732
+ return false;
1733
+ if (binaryPair(before, after))
1299
1734
  return false;
1300
- if (isCharacter(before) && after === "text")
1735
+ if (domainSkipsRewrite(before, after, input))
1301
1736
  return false;
1302
1737
  return true;
1303
1738
  }
1739
+ function statementMayReprint(sql) {
1740
+ const text = normalizedSql(sql);
1741
+ if (/^(?:create|alter) domain\b/.test(text))
1742
+ return true;
1743
+ return /^create(?: or replace)? function\b/.test(text) && /\blanguage sql\b/.test(text);
1744
+ }
1745
+ function usingRewrites(input) {
1746
+ const text = normalizedSql(input.step.sql);
1747
+ const at = text.search(/\busing\b/);
1748
+ if (at < 0)
1749
+ return false;
1750
+ const column = lastName(TYPE_CHANGE.exec(text)?.[2]);
1751
+ if (column === undefined)
1752
+ return true;
1753
+ const expression = text.slice(at + "using".length).trim().replace(/;$/, "");
1754
+ return !plainCast(expression, column);
1755
+ }
1756
+ function plainCast(expression, column) {
1757
+ const bare = column.toLowerCase();
1758
+ const quoted = `"${column}"`;
1759
+ return expression.startsWith(`${bare}::`) || expression.startsWith(`${quoted}::`) || expression.startsWith(`cast(${bare} as `) || expression.startsWith(`cast(${quoted} as `);
1760
+ }
1761
+ function isVarying(type) {
1762
+ return type === "character varying" || type === "varchar" || type.startsWith("character varying(") || type.startsWith("varchar(");
1763
+ }
1764
+ function unconstrainedVarying(type) {
1765
+ return type === "character varying" || type === "varchar";
1766
+ }
1767
+ function unconstrainedCharacter(type) {
1768
+ return type === "bpchar" || type === "character";
1769
+ }
1770
+ function varyingRelaxation(before, after) {
1771
+ const left = charLength(before);
1772
+ if (left?.family === "varying" && unconstrainedVarying(after))
1773
+ return true;
1774
+ const right = charLength(after);
1775
+ return left?.family === "varying" && right?.family === "varying" && right.length > left.length;
1776
+ }
1777
+ function numericRelaxation(before, after) {
1778
+ const left = numericBounds(before);
1779
+ const right = numericBounds(after);
1780
+ if (left === undefined || right === undefined)
1781
+ return false;
1782
+ if (right.precision === undefined)
1783
+ return left.precision !== undefined;
1784
+ if (left.precision === undefined || left.scale === undefined || right.scale === undefined) {
1785
+ return false;
1786
+ }
1787
+ return right.scale === left.scale && right.precision > left.precision;
1788
+ }
1789
+ function numericBounds(type) {
1790
+ const match = /^numeric(?:\((\d+)(?:,(\d+))?\))?$/.exec(type);
1791
+ if (match === null)
1792
+ return;
1793
+ if (match[1] === undefined)
1794
+ return { precision: undefined, scale: undefined };
1795
+ return {
1796
+ precision: Number(match[1]),
1797
+ scale: match[2] === undefined ? 0 : Number(match[2])
1798
+ };
1799
+ }
1800
+ function binaryPair(before, after) {
1801
+ if (before === "integer" && after === "oid")
1802
+ return true;
1803
+ if (before === "oid" && after === "integer")
1804
+ return true;
1805
+ if (before === "cidr" && after === "inet")
1806
+ return true;
1807
+ return before === "xml" && after === "text";
1808
+ }
1809
+ function domainSkipsRewrite(before, after, input) {
1810
+ const next = domainNamed(input, after);
1811
+ if (next !== undefined && next.check.trim().length === 0 && canonical(next.base) === before) {
1812
+ return true;
1813
+ }
1814
+ const previous = domainNamed(input, before);
1815
+ return previous !== undefined && canonical(previous.base) === after;
1816
+ }
1817
+ function domainNamed(input, name) {
1818
+ for (const source of [input.after, input.before]) {
1819
+ for (const object of source.objects) {
1820
+ if (object.kind !== "type" || object.identity.name !== name)
1821
+ continue;
1822
+ if (!isDomain(object.definition))
1823
+ continue;
1824
+ return object.definition;
1825
+ }
1826
+ }
1827
+ return;
1828
+ }
1304
1829
  function narrows(before, after) {
1305
1830
  const leftInt = INTEGER_WIDTH[before];
1306
1831
  const rightInt = INTEGER_WIDTH[after];
@@ -1325,9 +1850,6 @@ function charLength(type) {
1325
1850
  return { family: "fixed", length: Number(fixed[1]) };
1326
1851
  return;
1327
1852
  }
1328
- function isCharacter(type) {
1329
- return type === "text" || charLength(type) !== undefined;
1330
- }
1331
1853
  function numericSpec(type) {
1332
1854
  const match = /^numeric(?:\((\d+)(?:,(\d+))?\))?$/.exec(type);
1333
1855
  if (match === null)
@@ -1344,7 +1866,7 @@ function tableExists(input) {
1344
1866
  return name !== undefined && input.existingTables.has(name);
1345
1867
  }
1346
1868
  function statementTable(step) {
1347
- const text = normalized(step.sql);
1869
+ const text = normalizedSql(step.sql);
1348
1870
  const altered = /^(?:alter|drop) table\s+(\S+)/.exec(text);
1349
1871
  if (altered?.[1] !== undefined)
1350
1872
  return lastName(altered[1]);
@@ -1380,6 +1902,134 @@ function isSerial(column) {
1380
1902
  return /nextval\s*\(/i.test(column.definition.defaultExpression ?? "");
1381
1903
  }
1382
1904
 
1905
+ // src/tooling/migrate/reprint.ts
1906
+ var REPRINT_CODE = "OKM1552";
1907
+ function reprintHits(declared, stored, steps) {
1908
+ const hits = [];
1909
+ const silenced = new Set;
1910
+ for (const difference of differences(declared, stored)) {
1911
+ const index = stepIndex(steps, difference.kind, difference.name);
1912
+ if (index === undefined)
1913
+ continue;
1914
+ const step = steps[index];
1915
+ const allow = step?.allows?.find((item) => item.code === REPRINT_CODE);
1916
+ if (allow !== undefined && allow.reason.trim().length > 0) {
1917
+ silenced.add(index);
1918
+ continue;
1919
+ }
1920
+ hits.push({
1921
+ severity: "warning",
1922
+ code: REPRINT_CODE,
1923
+ place: `step ${String(index + 1)}`,
1924
+ reason: difference.reason
1925
+ });
1926
+ }
1927
+ steps.forEach((step, index) => {
1928
+ for (const allow of step.allows ?? []) {
1929
+ if (allow.code !== REPRINT_CODE)
1930
+ continue;
1931
+ const place = `step ${String(index + 1)}`;
1932
+ if (allow.reason.trim().length === 0) {
1933
+ hits.push({
1934
+ severity: "error",
1935
+ code: "OKM1510",
1936
+ place,
1937
+ reason: "override needs a reason"
1938
+ });
1939
+ continue;
1940
+ }
1941
+ if (silenced.has(index))
1942
+ continue;
1943
+ if (hits.some((item) => item.place === place && item.code === REPRINT_CODE))
1944
+ continue;
1945
+ if (!statementMayReprint(step.sql))
1946
+ continue;
1947
+ hits.push({
1948
+ severity: "error",
1949
+ code: "OKM1510",
1950
+ place,
1951
+ reason: `override ${REPRINT_CODE} does not match this statement`
1952
+ });
1953
+ }
1954
+ });
1955
+ return hits;
1956
+ }
1957
+ function differences(declared, stored) {
1958
+ const found = [];
1959
+ const storedDomains = domainChecks(stored);
1960
+ for (const [name, check] of domainChecks(declared)) {
1961
+ const live = storedDomains.get(name);
1962
+ if (live === undefined || live === check)
1963
+ continue;
1964
+ found.push({
1965
+ kind: "domain",
1966
+ name,
1967
+ reason: `domain ${name} check reprints differently`
1968
+ });
1969
+ }
1970
+ const storedFunctions = sqlBodies(stored);
1971
+ for (const [key, body] of sqlBodies(declared)) {
1972
+ const live = storedFunctions.get(key);
1973
+ if (live === undefined || live.body === body.body)
1974
+ continue;
1975
+ found.push({
1976
+ kind: "function",
1977
+ name: body.name,
1978
+ reason: `function ${body.name} body reprints differently`
1979
+ });
1980
+ }
1981
+ return found;
1982
+ }
1983
+ function domainChecks(source) {
1984
+ const checks = new Map;
1985
+ for (const object of source.objects) {
1986
+ if (object.kind !== "type" || !isDomain(object.definition))
1987
+ continue;
1988
+ checks.set(object.identity.name, object.definition.check.trim());
1989
+ }
1990
+ return checks;
1991
+ }
1992
+ function sqlBodies(source) {
1993
+ const bodies = new Map;
1994
+ for (const object of source.objects) {
1995
+ if (object.kind !== "function" || object.definition.language !== "sql")
1996
+ continue;
1997
+ bodies.set(functionKey(object), {
1998
+ name: object.identity.name,
1999
+ body: object.definition.body.trim()
2000
+ });
2001
+ }
2002
+ return bodies;
2003
+ }
2004
+ function functionKey(object) {
2005
+ const args = object.identity.argTypes.map((typeName) => canonicalTypeName(typeName)).join(",");
2006
+ return `${object.identity.name}(${args})`;
2007
+ }
2008
+ function stepIndex(steps, kind, name) {
2009
+ let found;
2010
+ steps.forEach((step, index) => {
2011
+ if (mentions(normalizedSql(step.sql), kind, name))
2012
+ found = index;
2013
+ });
2014
+ return found;
2015
+ }
2016
+ function mentions(text, kind, name) {
2017
+ if (kind === "domain" && !/^(?:create|alter) domain\b/.test(text))
2018
+ return false;
2019
+ if (kind === "function") {
2020
+ if (!/^create(?: or replace)? function\b/.test(text) || !/\blanguage sql\b/.test(text)) {
2021
+ return false;
2022
+ }
2023
+ }
2024
+ const quoted = `"${name}"`;
2025
+ if (text.includes(quoted))
2026
+ return true;
2027
+ return new RegExp(`\\b${escapeRegExp(name.toLowerCase())}\\b`).test(text);
2028
+ }
2029
+ function escapeRegExp(value) {
2030
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2031
+ }
2032
+
1383
2033
  // src/tooling/migrate/lint.ts
1384
2034
  function lintPlan(plan, before, after) {
1385
2035
  const existingTables = tableNames(before);
@@ -1389,6 +2039,9 @@ function lintPlan(plan, before, after) {
1389
2039
  });
1390
2040
  return findings;
1391
2041
  }
2042
+ function lintReprints(declared, stored, steps) {
2043
+ return reprintHits(declared, stored, steps).map((hit) => finding(hit.severity, hit.code, hit.place, hit.reason));
2044
+ }
1392
2045
  function lintCatalog(source) {
1393
2046
  const findings = [];
1394
2047
  for (const object of source.objects) {
@@ -1404,15 +2057,15 @@ function lintCatalog(source) {
1404
2057
  return findings;
1405
2058
  }
1406
2059
  function lintMigrationDirectory(directory, pending) {
1407
- if (!existsSync2(directory))
2060
+ if (!existsSync3(directory))
1408
2061
  return [];
1409
2062
  const names = readdirSync2(directory).filter((file) => file.endsWith(".sql")).sort();
1410
2063
  let before = catalog([]);
1411
2064
  const findings = [];
1412
2065
  for (const file of names) {
1413
2066
  const id = file.slice(0, -".sql".length);
1414
- const plan = readMigrationPlan(join2(directory, file));
1415
- const after = parseCatalog2(readFileSync2(join2(directory, `${id}.catalog.json`), "utf8"));
2067
+ const plan = readMigrationPlan(join3(directory, file));
2068
+ const after = parseCatalog2(readFileSync3(join3(directory, `${id}.catalog.json`), "utf8"));
1416
2069
  if (pending === undefined || pending.has(id)) {
1417
2070
  for (const item of lintPlan(plan, before, after)) {
1418
2071
  findings.push({ ...item, place: `${id} ${item.place}` });
@@ -1462,6 +2115,8 @@ function lintStep(step, number, before, after, existingTables, earlier, later) {
1462
2115
  triggered.add(hit.code);
1463
2116
  }
1464
2117
  }
2118
+ if (statementMayReprint(statement.sql))
2119
+ triggered.add(REPRINT_CODE);
1465
2120
  const place = multi ? `step ${String(number)} statement ${String(index + 1)}` : `step ${String(number)}`;
1466
2121
  perStatement.push({ place, hits });
1467
2122
  });
@@ -1531,8 +2186,16 @@ function columnPlace(column) {
1531
2186
  }
1532
2187
 
1533
2188
  // src/tooling/migrate/project.ts
1534
- import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync, writeFileSync } from "node:fs";
1535
- import { basename, dirname, join as join3 } from "node:path";
2189
+ import {
2190
+ existsSync as existsSync4,
2191
+ mkdirSync,
2192
+ readdirSync as readdirSync3,
2193
+ readFileSync as readFileSync4,
2194
+ statSync,
2195
+ unlinkSync,
2196
+ writeFileSync as writeFileSync2
2197
+ } from "node:fs";
2198
+ import { basename, dirname, join as join4 } from "node:path";
1536
2199
  import { pathToFileURL } from "node:url";
1537
2200
 
1538
2201
  // src/dialects/pg/declarations.ts
@@ -2143,6 +2806,131 @@ function columnNames(target, built, table) {
2143
2806
  return built.flatMap((object) => object.kind === "column" && object.identity.parent.name === table ? [object.identity.name] : []);
2144
2807
  }
2145
2808
 
2809
+ // src/tooling/migrate/place.ts
2810
+ function placeCatalog(source, namespace, externalNamespaces) {
2811
+ if (namespace === "public" && externalNamespaces.size === 0)
2812
+ return source;
2813
+ const homes = tableHomes(source, namespace, externalNamespaces);
2814
+ let changed = false;
2815
+ const objects = source.objects.map((object) => {
2816
+ const next = relocateObject(object, homes, namespace);
2817
+ if (next !== object)
2818
+ changed = true;
2819
+ return next;
2820
+ });
2821
+ if (!changed)
2822
+ return source;
2823
+ return catalog(objects);
2824
+ }
2825
+ function tableHomes(source, namespace, externalNamespaces) {
2826
+ const homes = new Map;
2827
+ for (const object of source.objects) {
2828
+ if (object.kind !== "table" && object.kind !== "view" && object.kind !== "materializedView") {
2829
+ continue;
2830
+ }
2831
+ const home = object.owner === "external" ? externalNamespaces.get(object.identity.name) ?? namespace : namespace;
2832
+ homes.set(object.identity.name, home);
2833
+ }
2834
+ return homes;
2835
+ }
2836
+ function relocateObject(object, homes, namespace) {
2837
+ const dependencies = relocateEdges(object.dependencies, homes, namespace);
2838
+ if (object.kind === "extension") {
2839
+ if (object.definition.schema === "public" && namespace !== "public") {
2840
+ return { ...object, definition: { ...object.definition, schema: namespace }, dependencies };
2841
+ }
2842
+ return dependencies === object.dependencies ? object : { ...object, dependencies };
2843
+ }
2844
+ if (object.kind === "role") {
2845
+ return dependencies === object.dependencies ? object : { ...object, dependencies };
2846
+ }
2847
+ const identity = relocateIdentity(object.identity, homes, namespace);
2848
+ if (object.kind === "constraint") {
2849
+ const references = relocateReferences(object, homes);
2850
+ if (identity === object.identity && references === object.definition.references && dependencies === object.dependencies) {
2851
+ return object;
2852
+ }
2853
+ if (references === undefined || references === object.definition.references) {
2854
+ return { ...object, identity, dependencies };
2855
+ }
2856
+ return {
2857
+ ...object,
2858
+ identity,
2859
+ definition: { ...object.definition, references },
2860
+ dependencies
2861
+ };
2862
+ }
2863
+ if (identity === object.identity && dependencies === object.dependencies)
2864
+ return object;
2865
+ return { ...object, identity, dependencies };
2866
+ }
2867
+ function relocateReferences(object, homes) {
2868
+ const references = object.definition.references;
2869
+ if (references === undefined)
2870
+ return;
2871
+ const home = homes.get(references.parent.name);
2872
+ if (home === undefined)
2873
+ return references;
2874
+ const parent = moveRef(references.parent, home);
2875
+ if (parent === references.parent)
2876
+ return references;
2877
+ return { ...references, parent };
2878
+ }
2879
+ function relocateEdges(edges, homes, namespace) {
2880
+ let changed = false;
2881
+ const next = edges.map((edge) => {
2882
+ const target = relocateIdentity(edge.target, homes, namespace);
2883
+ if (target === edge.target)
2884
+ return edge;
2885
+ changed = true;
2886
+ return { target };
2887
+ });
2888
+ return changed ? next : edges;
2889
+ }
2890
+ function relocateIdentity(identity, homes, namespace) {
2891
+ if (identity.kind === "role" || identity.kind === "extension")
2892
+ return identity;
2893
+ if (identity.kind === "column" || identity.kind === "index" || identity.kind === "constraint" || identity.kind === "trigger" || identity.kind === "policy") {
2894
+ const home = homes.get(identity.parent.name) ?? namespace;
2895
+ const parent = moveRef(identity.parent, home);
2896
+ if (parent === identity.parent)
2897
+ return identity;
2898
+ return { ...identity, parent };
2899
+ }
2900
+ if (identity.kind === "grant") {
2901
+ const home = homes.get(identity.object.name) ?? namespace;
2902
+ const moved = moveNamespace(identity.object.namespace, home);
2903
+ if (moved === identity.object.namespace)
2904
+ return identity;
2905
+ return { ...identity, object: { ...identity.object, namespace: moved } };
2906
+ }
2907
+ if (identity.kind === "defaultPrivilege") {
2908
+ const moved = moveNamespace(identity.namespace, namespace);
2909
+ if (moved === identity.namespace)
2910
+ return identity;
2911
+ return { ...identity, namespace: moved };
2912
+ }
2913
+ if (identity.kind === "table" || identity.kind === "view" || identity.kind === "materializedView" || identity.kind === "sequence" || identity.kind === "type" || identity.kind === "function") {
2914
+ const home = homes.get(identity.name) ?? namespace;
2915
+ const moved = moveNamespace(identity.namespace, home);
2916
+ if (moved === identity.namespace)
2917
+ return identity;
2918
+ return { ...identity, namespace: moved };
2919
+ }
2920
+ return identity;
2921
+ }
2922
+ function moveRef(ref, namespace) {
2923
+ const moved = moveNamespace(ref.namespace, namespace);
2924
+ if (moved === ref.namespace)
2925
+ return ref;
2926
+ return { ...ref, namespace: moved };
2927
+ }
2928
+ function moveNamespace(namespace, name) {
2929
+ if (namespace.form !== "static" || namespace.name === name)
2930
+ return namespace;
2931
+ return staticNamespace2(name);
2932
+ }
2933
+
2146
2934
  // src/tooling/migrate/estimate.ts
2147
2935
  var LARGE_TABLE_ROWS = 1e6;
2148
2936
  var ESTIMATE_SQL = `
@@ -2154,7 +2942,7 @@ var ESTIMATE_SQL = `
2154
2942
  and c.relkind in ('r', 'p', 'f', 'm')
2155
2943
  `;
2156
2944
  async function readRowEstimates(url, schema, tables) {
2157
- const pool = open2({ url, max: 1 });
2945
+ const pool = open2({ url: driverUrl(url), max: 1 });
2158
2946
  try {
2159
2947
  const connection = await pool.reserve?.();
2160
2948
  if (connection === undefined) {
@@ -2336,11 +3124,35 @@ function trimZero(text) {
2336
3124
  return text.endsWith(".0") ? text.slice(0, -2) : text;
2337
3125
  }
2338
3126
 
3127
+ // src/tooling/migrate/plan-json.ts
3128
+ function planDocument(plan, findings, lockText) {
3129
+ return {
3130
+ name: plan.name,
3131
+ class: plan.class,
3132
+ steps: plan.steps.map((step) => ({
3133
+ class: step.class,
3134
+ lock: lockText === undefined ? step.lock : lockText(step),
3135
+ sql: step.sql
3136
+ })),
3137
+ findings: findings.map((finding) => ({
3138
+ severity: finding.severity,
3139
+ code: finding.code,
3140
+ place: finding.place,
3141
+ reason: finding.reason,
3142
+ fix: finding.fix
3143
+ }))
3144
+ };
3145
+ }
3146
+ function formatPlanJson(plan, findings, lockText) {
3147
+ return `${JSON.stringify(planDocument(plan, findings, lockText))}
3148
+ `;
3149
+ }
3150
+
2339
3151
  // src/tooling/migrate/project.ts
2340
3152
  async function buildProject(cwd) {
2341
3153
  const config = await loadConfig(cwd);
2342
3154
  const built = await loadSchema(cwd, config.schema);
2343
- return writeArtifact(cwd, config, withRoles(built, config));
3155
+ return writeArtifact(cwd, config, withRoles(built, config, cwd));
2344
3156
  }
2345
3157
  async function generateProject(cwd, name, flags) {
2346
3158
  const prepared = await prepare(cwd, name, flags);
@@ -2352,17 +3164,20 @@ async function generateProject(cwd, name, flags) {
2352
3164
  const directory = projectPath(cwd, prepared.config.migrations ?? "migrations");
2353
3165
  mkdirSync(directory, { recursive: true });
2354
3166
  const base = `${nextNumber(directory)}_${safeName(name)}`;
2355
- const sqlPath = join3(directory, `${base}.sql`);
2356
- writeFileSync(sqlPath, formatPlan(prepared.plan));
2357
- writeFileSync(join3(directory, `${base}.catalog.json`), serializeCatalog2(prepared.built.catalog));
3167
+ const sqlPath = join4(directory, `${base}.sql`);
3168
+ writeFileSync2(sqlPath, formatPlan(prepared.plan));
3169
+ writeFileSync2(join4(directory, `${base}.catalog.json`), serializeCatalog2(prepared.built.catalog));
2358
3170
  return { path: sqlPath, findings: lintPrepared(prepared) };
2359
3171
  }
2360
3172
  async function planProject(cwd, name, flags, invoke) {
2361
3173
  const prepared = await prepare(cwd, name, flags);
3174
+ const findings = lintPrepared(prepared);
3175
+ const lockText = await planLockText(prepared.config, prepared.plan, invoke);
2362
3176
  return {
2363
3177
  plan: prepared.plan,
2364
- findings: lintPrepared(prepared),
2365
- text: await displayPlan(prepared.config, prepared.plan, invoke)
3178
+ findings,
3179
+ text: formatPlan(prepared.plan, lockText),
3180
+ json: formatPlanJson(prepared.plan, findings, lockText)
2366
3181
  };
2367
3182
  }
2368
3183
  async function checkProject(cwd, flags) {
@@ -2378,8 +3193,8 @@ async function checkProject(cwd, flags) {
2378
3193
  assertTargetAlias(targets);
2379
3194
  if (opened.config.tables !== undefined) {
2380
3195
  const directory = projectPath(cwd, opened.config.tables);
2381
- const files = existsSync3(directory) ? readdirSync3(directory) : [];
2382
- const source = readFileSync3(projectPath(cwd, opened.config.schema), "utf8");
3196
+ const files = existsSync4(directory) ? readdirSync3(directory) : [];
3197
+ const source = readFileSync4(projectPath(cwd, opened.config.schema), "utf8");
2383
3198
  const missing = unlistedTableFiles(source, files);
2384
3199
  if (missing.length > 0) {
2385
3200
  throw new OkmError2("OKM1024", `Table file not in schema: ${missing.join(", ")}.`, {
@@ -2387,7 +3202,8 @@ async function checkProject(cwd, flags) {
2387
3202
  });
2388
3203
  }
2389
3204
  }
2390
- const findings = lintOpened(opened);
3205
+ assertSnapshots(cwd, opened.config, opened.built.catalog);
3206
+ const findings = [...lintOpened(opened), ...couplingFindings(opened.built.catalog)];
2391
3207
  if (hasError(findings))
2392
3208
  throw lintRefusal(findings);
2393
3209
  const named = flags?.target;
@@ -2398,8 +3214,9 @@ async function checkProject(cwd, flags) {
2398
3214
  const target = selectTarget(opened.config, named);
2399
3215
  assertTargetPolicy(target, "check", flags?.allowProtected ?? false);
2400
3216
  assertTargetPolicy(target, "drift", flags?.allowProtected ?? false);
2401
- const { assertAuthorDrift } = await import("./3449rcae.js");
3217
+ const { assertAuthorDrift } = await import("./rged92v6.js");
2402
3218
  const allowPooler = flags?.allowPooler === true || opened.config.allowPooler === true;
3219
+ await assertLiveExternal(target.url, opened.built.catalog, loadSnapshots(cwd, opened.config));
2403
3220
  await assertAuthorDrift(target.url, opened.built.catalog, opened.config.roles, allowPooler);
2404
3221
  return findings;
2405
3222
  }
@@ -2429,6 +3246,7 @@ async function prepare(cwd, name, flags) {
2429
3246
  renames: opened.renames,
2430
3247
  replacements: flags.map((flag) => parseReplace(flag)),
2431
3248
  name,
3249
+ ...placedSchema(opened.config),
2432
3250
  ...batchSizeField(opened.config.backfill?.batchSize)
2433
3251
  });
2434
3252
  return {
@@ -2442,26 +3260,25 @@ async function prepare(cwd, name, flags) {
2442
3260
  function lintPrepared(prepared) {
2443
3261
  return lintPlan(prepared.plan, prepared.before, prepared.after);
2444
3262
  }
2445
- async function displayPlan(config, plan, invoke) {
2446
- const offline = formatPlan(plan);
3263
+ async function planLockText(config, plan, invoke) {
2447
3264
  const targets = listTargets(config);
2448
3265
  if (targets.length === 0)
2449
- return offline;
3266
+ return;
2450
3267
  assertTargetAlias(targets);
2451
3268
  const named = invoke?.target;
2452
3269
  if (named === undefined && targets.length !== 1)
2453
- return offline;
3270
+ return;
2454
3271
  const target = selectTarget(config, named);
2455
3272
  assertTargetPolicy(target, "plan", invoke?.allowProtected ?? false);
2456
3273
  let estimates;
2457
3274
  try {
2458
3275
  const names = plan.steps.flatMap((step) => step.tables ?? []);
2459
- estimates = await readRowEstimates(target.url, "public", names);
3276
+ estimates = await readRowEstimates(target.url, catalogNamespace(config), names);
2460
3277
  } catch {
2461
- return offline;
3278
+ return;
2462
3279
  }
2463
3280
  const aliases = renameAliases(plan.steps);
2464
- return formatPlan(plan, (step) => annotateLock(step, estimates, aliases));
3281
+ return (step) => annotateLock(step, estimates, aliases);
2465
3282
  }
2466
3283
  function lintOpened(opened) {
2467
3284
  const plan = planMigration({
@@ -2469,6 +3286,7 @@ function lintOpened(opened) {
2469
3286
  after: opened.built.catalog,
2470
3287
  renames: opened.renames,
2471
3288
  name: "check",
3289
+ ...placedSchema(opened.config),
2472
3290
  ...batchSizeField(opened.config.backfill?.batchSize)
2473
3291
  });
2474
3292
  return [
@@ -2478,7 +3296,7 @@ function lintOpened(opened) {
2478
3296
  }
2479
3297
  async function openProject(cwd) {
2480
3298
  const config = await loadConfig(cwd);
2481
- const built = withRoles(await loadSchema(cwd, config.schema), config);
3299
+ const built = await loadBuiltSchema(cwd, config);
2482
3300
  const declarations = schemaDeclarations(built);
2483
3301
  const previous = readPrevious(projectPath(cwd, config.migrations ?? "migrations"));
2484
3302
  const stale = staleRenames(previous?.catalog, declarations.renames);
@@ -2494,15 +3312,22 @@ async function openProject(cwd) {
2494
3312
  renames: declarations.renames
2495
3313
  };
2496
3314
  }
2497
- function withRoles(built, config) {
3315
+ function withRoles(built, config, cwd) {
2498
3316
  const marks = ownershipMarks(built);
2499
3317
  const stamped = marks.size === 0 ? built.catalog : stampOwnership(built.catalog, marks);
3318
+ const placed = placeCatalog(stamped, catalogNamespace(config), externalNamespaceMap(loadSnapshots(cwd, config)));
2500
3319
  const roles = config.roles;
2501
- const catalog = roles === undefined ? stamped : attachRoles(stamped, roles, exposureFor(built, roles));
3320
+ const catalog = roles === undefined ? placed : attachRoles(placed, roles, exposureFor(built, roles));
2502
3321
  if (catalog === built.catalog)
2503
3322
  return built;
2504
3323
  return { ...built, catalog };
2505
3324
  }
3325
+ function placedSchema(config) {
3326
+ const namespace = catalogNamespace(config);
3327
+ if (namespace === "public")
3328
+ return;
3329
+ return { schema: namespace };
3330
+ }
2506
3331
  function ownershipMarks(built) {
2507
3332
  const marks = new Map;
2508
3333
  const models = built.model;
@@ -2539,9 +3364,9 @@ var GIT_ATTRIBUTES = [
2539
3364
  `);
2540
3365
  function writeGitAttributes(cwd) {
2541
3366
  const path = projectPath(cwd, ".gitattributes");
2542
- if (existsSync3(path))
3367
+ if (existsSync4(path))
2543
3368
  return;
2544
- writeFileSync(path, GIT_ATTRIBUTES);
3369
+ writeFileSync2(path, GIT_ATTRIBUTES);
2545
3370
  }
2546
3371
  function typesText(cwd, spec, built) {
2547
3372
  return `${emitRowTypes(built, jsonColumnTypes(readSchemaSource(cwd, spec)))}${referenceAugmentation(built)}`;
@@ -2550,7 +3375,7 @@ function readSchemaSource(cwd, spec) {
2550
3375
  const parts = [];
2551
3376
  const seen = new Set;
2552
3377
  const visit = (file) => {
2553
- if (seen.has(file) || !existsSync3(file))
3378
+ if (seen.has(file) || !existsSync4(file))
2554
3379
  return;
2555
3380
  let stat;
2556
3381
  try {
@@ -2561,16 +3386,16 @@ function readSchemaSource(cwd, spec) {
2561
3386
  if (!stat.isFile())
2562
3387
  return;
2563
3388
  seen.add(file);
2564
- const text = readFileSync3(file, "utf8");
3389
+ const text = readFileSync4(file, "utf8");
2565
3390
  parts.push(text);
2566
3391
  for (const match of text.matchAll(/from\s+["'](\.[^"']+)["']/g)) {
2567
3392
  const rel = match[1];
2568
3393
  if (rel === undefined)
2569
3394
  continue;
2570
- const base = join3(dirname(file), rel);
2571
- const candidates = [base, `${base}.ts`, `${base}.tsx`, `${base}.mts`, join3(base, "index.ts")];
3395
+ const base = join4(dirname(file), rel);
3396
+ const candidates = [base, `${base}.ts`, `${base}.tsx`, `${base}.mts`, join4(base, "index.ts")];
2572
3397
  for (const candidate of candidates) {
2573
- if (existsSync3(candidate)) {
3398
+ if (existsSync4(candidate)) {
2574
3399
  visit(candidate);
2575
3400
  break;
2576
3401
  }
@@ -2586,19 +3411,35 @@ function writeArtifact(cwd, config, built) {
2586
3411
  mkdirSync(directory, { recursive: true });
2587
3412
  const startup = startupCatalog2(built.catalog);
2588
3413
  const text = catalogFileText2(serializeCatalog2(startup));
2589
- writeFileSync(join3(directory, "catalog.json"), text);
2590
- writeFileSync(join3(directory, "catalog.hash"), `${sha2562(text)}
3414
+ writeFileSync2(join4(directory, "catalog.json"), text);
3415
+ writeFileSync2(join4(directory, "catalog.hash"), `${sha2562(text)}
2591
3416
  `);
2592
- writeFileSync(join3(directory, "types.d.ts"), typesText(cwd, config.schema, built));
3417
+ writeFileSync2(join4(directory, "types.d.ts"), typesText(cwd, config.schema, built));
3418
+ writeNamespace(directory, catalogNamespace(config));
2593
3419
  return directory;
2594
3420
  }
3421
+ function writeNamespace(directory, namespace) {
3422
+ const path = join4(directory, "namespace");
3423
+ if (namespace === "public") {
3424
+ if (existsSync4(path))
3425
+ unlinkSync(path);
3426
+ return;
3427
+ }
3428
+ writeFileSync2(path, `${namespace}
3429
+ `);
3430
+ }
2595
3431
  async function loadBuiltSchema(cwd, config) {
2596
- return withRoles(await loadSchema(cwd, config.schema), config);
3432
+ return withRoles(await loadSchema(cwd, config.schema), config, cwd);
3433
+ }
3434
+ async function exportProject(cwd, args) {
3435
+ const config = await loadConfig(cwd);
3436
+ const built = await loadBuiltSchema(cwd, config);
3437
+ return renderExport(cwd, config, built.catalog, args);
2597
3438
  }
2598
3439
  async function loadConfig(cwd) {
2599
3440
  const path = projectPath(cwd, "okmodel.config.ts");
2600
3441
  const imported = await import(`${pathToFileURL(path).href}?okm=${Date.now()}`);
2601
- const record = isRecord(imported) ? imported : {};
3442
+ const record = isRecord2(imported) ? imported : {};
2602
3443
  const config = record.default ?? record.config;
2603
3444
  if (!isConfig(config)) {
2604
3445
  throw new OkmError2("invalid", "okmodel.config.ts must default-export defineConfig(...).");
@@ -2608,7 +3449,7 @@ async function loadConfig(cwd) {
2608
3449
  async function loadSchema(cwd, spec) {
2609
3450
  const path = projectPath(cwd, spec);
2610
3451
  const imported = await import(`${pathToFileURL(path).href}?okm=${Date.now()}`);
2611
- const record = isRecord(imported) ? imported : {};
3452
+ const record = isRecord2(imported) ? imported : {};
2612
3453
  for (const value of Object.values(record)) {
2613
3454
  if (!isBuilt(value))
2614
3455
  continue;
@@ -2618,13 +3459,13 @@ async function loadSchema(cwd, spec) {
2618
3459
  throw new OkmError2("invalid", `${spec} must export a schema().`);
2619
3460
  }
2620
3461
  function readPrevious(directory) {
2621
- if (!existsSync3(directory))
3462
+ if (!existsSync4(directory))
2622
3463
  return;
2623
3464
  const catalogs = readdirSync3(directory).filter((file) => file.endsWith(".catalog.json")).sort();
2624
3465
  const latest = catalogs.at(-1);
2625
3466
  if (latest === undefined)
2626
3467
  return;
2627
- const text = readFileSync3(join3(directory, latest), "utf8");
3468
+ const text = readFileSync4(join4(directory, latest), "utf8");
2628
3469
  return { catalog: parseCatalog2(text) };
2629
3470
  }
2630
3471
  function referenceAugmentation(source) {
@@ -2649,7 +3490,7 @@ function referenceAugmentation(source) {
2649
3490
  }
2650
3491
  function nextNumber(directory) {
2651
3492
  let max = 0;
2652
- if (existsSync3(directory)) {
3493
+ if (existsSync4(directory)) {
2653
3494
  for (const file of readdirSync3(directory)) {
2654
3495
  const match = /^(\d+)_/.exec(file);
2655
3496
  const value = match?.[1];
@@ -2666,14 +3507,14 @@ function safeName(name) {
2666
3507
  return name;
2667
3508
  }
2668
3509
  function isBuilt(value) {
2669
- if (!isRecord(value) || !Array.isArray(value.tables))
3510
+ if (!isRecord2(value) || !Array.isArray(value.tables))
2670
3511
  return false;
2671
3512
  return "catalog" in value;
2672
3513
  }
2673
3514
  function isConfig(value) {
2674
- return isRecord(value) && typeof value.schema === "string";
3515
+ return isRecord2(value) && typeof value.schema === "string";
2675
3516
  }
2676
- function isRecord(value) {
3517
+ function isRecord2(value) {
2677
3518
  return typeof value === "object" && value !== null;
2678
3519
  }
2679
3520
  var SKIP_SCAN = new Set(["node_modules", "dist", "coverage"]);
@@ -2698,7 +3539,7 @@ function directoryImportsValidate(dir) {
2698
3539
  for (const entry of entries) {
2699
3540
  if (entry.name.startsWith(".") || SKIP_SCAN.has(entry.name))
2700
3541
  continue;
2701
- const path = join3(dir, entry.name);
3542
+ const path = join4(dir, entry.name);
2702
3543
  if (entry.isDirectory()) {
2703
3544
  if (directoryImportsValidate(path))
2704
3545
  return true;
@@ -2706,7 +3547,7 @@ function directoryImportsValidate(dir) {
2706
3547
  }
2707
3548
  if (!/\.(?:ts|tsx|mts|cts)$/.test(entry.name))
2708
3549
  continue;
2709
- if (runtimeImportsValidate(readFileSync3(path, "utf8")))
3550
+ if (runtimeImportsValidate(readFileSync4(path, "utf8")))
2710
3551
  return true;
2711
3552
  }
2712
3553
  return false;
@@ -2728,4 +3569,4 @@ function runtimeImportsValidate(source) {
2728
3569
  return false;
2729
3570
  }
2730
3571
 
2731
- export { projectPath, errorDoc, loadMigrations, readMigrationPlan, loadHeadSnapshot, lintMigrationDirectory, formatFindings, hasError, lintRefusal, buildProject, generateProject, planProject, checkProject, projectHead, openProject, loadBuiltSchema, loadConfig, loadSchema };
3572
+ export { projectPath, LOGICAL_FORMAT, catalogNamespace, loadSnapshots, assertSnapshots, assertLiveExternal, errorDoc, loadMigrations, readMigrationPlan, loadHeadSnapshot, lintReprints, lintMigrationDirectory, formatFindings, hasError, lintRefusal, buildProject, generateProject, planProject, checkProject, projectHead, openProject, loadBuiltSchema, exportProject, loadConfig, loadSchema };