okmodel 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/README.md +14 -0
  2. package/dist/adapters/capabilities.d.ts +32 -0
  3. package/dist/adapters/failure.d.ts +1 -1
  4. package/dist/adapters/pg/bun-sql.d.ts +53 -0
  5. package/dist/adapters/pg/bunsql-extra.d.ts +1 -1
  6. package/dist/adapters/pg/bunsql.d.ts +1 -1
  7. package/dist/adapters/pg/neon-extra.d.ts +18 -0
  8. package/dist/adapters/pg/neon.d.ts +104 -0
  9. package/dist/adapters/pg/no-inflight-timeout.d.ts +1 -1
  10. package/dist/contracts/error.d.ts +1 -1
  11. package/dist/dialects/pg/ext/btree-gin.d.ts +26 -0
  12. package/dist/dialects/pg/ext/btree-gin.js +20 -0
  13. package/dist/dialects/pg/ext/btree-gist.d.ts +26 -0
  14. package/dist/dialects/pg/ext/btree-gist.js +20 -0
  15. package/dist/dialects/pg/ext/call.d.ts +53 -0
  16. package/dist/dialects/pg/ext/citext.js +1 -1
  17. package/dist/dialects/pg/ext/feature.d.ts +26 -0
  18. package/dist/dialects/pg/ext/fuzzystrmatch.d.ts +34 -0
  19. package/dist/dialects/pg/ext/fuzzystrmatch.js +33 -0
  20. package/dist/dialects/pg/ext/hstore.d.ts +41 -0
  21. package/dist/dialects/pg/ext/hstore.js +29 -0
  22. package/dist/dialects/pg/ext/index.d.ts +56 -0
  23. package/dist/dialects/pg/ext/index.js +1 -1
  24. package/dist/dialects/pg/ext/ltree.d.ts +41 -0
  25. package/dist/dialects/pg/ext/ltree.js +29 -0
  26. package/dist/dialects/pg/ext/pg-trgm.js +4 -13
  27. package/dist/dialects/pg/ext/pgcrypto.d.ts +33 -0
  28. package/dist/dialects/pg/ext/pgcrypto.js +33 -0
  29. package/dist/dialects/pg/ext/unaccent.d.ts +26 -0
  30. package/dist/dialects/pg/ext/unaccent.js +21 -0
  31. package/dist/dialects/pg/ext/using.d.ts +23 -0
  32. package/dist/dialects/pg/ext/versions.d.ts +36 -0
  33. package/dist/dialects/pg/external.d.ts +19 -0
  34. package/dist/dialects/pg/fn/index.d.ts +39 -1
  35. package/dist/dialects/pg/fn/index.js +118 -26
  36. package/dist/dialects/pg/index.d.ts +3 -0
  37. package/dist/dialects/pg/index.js +394 -14
  38. package/dist/dialects/pg/introspect.d.ts +11 -0
  39. package/dist/dialects/pg/operators.d.ts +1 -1
  40. package/dist/dialects/pg/ops/fragment.d.ts +119 -0
  41. package/dist/dialects/pg/ops/lane.d.ts +49 -0
  42. package/dist/dialects/pg/view/client.d.ts +14 -6
  43. package/dist/dialects/pg/view/index.d.ts +73 -1
  44. package/dist/dialects/pg/view/index.js +156 -5
  45. package/dist/dialects/pg/view/scratch.d.ts +11 -5
  46. package/dist/runtime/artifact-file.d.ts +22 -0
  47. package/dist/runtime/client.d.ts +10 -0
  48. package/dist/runtime/drift-detail.d.ts +2 -1
  49. package/dist/runtime/drift.d.ts +3 -10
  50. package/dist/runtime/otel.d.ts +105 -0
  51. package/dist/runtime/otel.js +97 -0
  52. package/dist/runtime/pg/bun.js +4 -4
  53. package/dist/runtime/pg/neon.d.ts +38 -0
  54. package/dist/runtime/pg/neon.js +64 -0
  55. package/dist/runtime/pg/pg.js +4 -4
  56. package/dist/runtime/pg/pglite.js +4 -4
  57. package/dist/runtime/pg/postgresjs.js +4 -4
  58. package/dist/runtime/plan.d.ts +1 -1
  59. package/dist/runtime/server-check.d.ts +36 -6
  60. package/dist/runtime/sql-fragment.d.ts +43 -0
  61. package/dist/runtime/tenancy/index.js +10 -10
  62. package/dist/runtime/topology.d.ts +1 -1
  63. package/dist/runtime/traits/index.js +16 -17
  64. package/dist/runtime/types.d.ts +15 -1
  65. package/dist/runtime/validate/index.js +4 -4
  66. package/dist/shared/{81ebdx9p.js → 0j6qe91y.js} +4 -4
  67. package/dist/shared/{qq3899ht.js → 12z7aznj.js} +243 -14
  68. package/dist/shared/{gzaveazc.js → 1d9q9cv2.js} +29 -2
  69. package/dist/shared/{w79hsckn.js → 1mxm04fy.js} +5 -5
  70. package/dist/shared/{7k9grvf6.js → 2szgv6yj.js} +5 -5
  71. package/dist/shared/{gs74dbj5.js → 2zdxkt7b.js} +933 -111
  72. package/dist/shared/{m6p12kbr.js → 3456242y.js} +23 -23
  73. package/dist/shared/3bb7wsqt.js +99 -0
  74. package/dist/shared/{y6njgyda.js → 3t6kw83m.js} +2 -2
  75. package/dist/shared/{xhfsfkgk.js → 4vbf83xx.js} +9 -2
  76. package/dist/shared/{pt94kz7f.js → 5kyb3tqx.js} +1 -1
  77. package/dist/shared/61bmrmhr.js +35 -0
  78. package/dist/shared/{x933k9a3.js → 62b603gr.js} +10 -10
  79. package/dist/shared/{jgqj11w4.js → 6f0z3hzh.js} +4 -0
  80. package/dist/shared/{tsvww22q.js → 6nae0spm.js} +156 -95
  81. package/dist/shared/{mtaszv5a.js → 71n0jmhc.js} +102 -4
  82. package/dist/shared/{5e3tf63j.js → 760ppr76.js} +14 -6
  83. package/dist/shared/7ba5h6pb.js +17 -0
  84. package/dist/shared/{sfz1ksn6.js → 8jesqsrk.js} +2 -2
  85. package/dist/shared/{5v3xf80k.js → 8n688yjg.js} +1 -1
  86. package/dist/shared/{rs73fca9.js → 8qzkd9f3.js} +6 -6
  87. package/dist/shared/{ax7qv55k.js → 90sg0w5a.js} +202 -25
  88. package/dist/shared/{zm0ptry0.js → 99qp9vtg.js} +1 -1
  89. package/dist/shared/{xnxsed62.js → 9dr2qnx7.js} +14 -14
  90. package/dist/shared/{1w508jbr.js → 9dtgn1z2.js} +21 -17
  91. package/dist/shared/{h15jx9bc.js → 9dvh1dr6.js} +1 -1
  92. package/dist/shared/{q5c6ngn3.js → a62rb2da.js} +1 -1
  93. package/dist/shared/{te5as7z7.js → aksb89mn.js} +3 -3
  94. package/dist/shared/{bz84tkzm.js → bphrgwz9.js} +2 -2
  95. package/dist/shared/bsek72fg.js +225 -0
  96. package/dist/shared/{7mnme5tn.js → c3mvnq7a.js} +1 -1
  97. package/dist/shared/{p6qc5gcp.js → ccsmhv9r.js} +25 -7
  98. package/dist/shared/{pbp1ss85.js → cexb7z6b.js} +2 -2
  99. package/dist/shared/{ab9gmbwy.js → cf8zdz5p.js} +5 -5
  100. package/dist/shared/{jvd0mqag.js → ckz05z15.js} +5 -5
  101. package/dist/shared/{dk8v79p3.js → d5nmj7vv.js} +1 -1
  102. package/dist/shared/{14vx95q4.js → dd5pmnky.js} +7 -7
  103. package/dist/shared/{vb6qfyve.js → ds229x4r.js} +2 -2
  104. package/dist/shared/{1qa0qajz.js → e275q7sd.js} +1 -1
  105. package/dist/shared/{zp9fmend.js → ebhz8vh6.js} +4 -4
  106. package/dist/shared/{hyh73qk6.js → ecn8mfdq.js} +5 -5
  107. package/dist/shared/{je4axnm8.js → fdsz3w80.js} +2 -2
  108. package/dist/shared/{9s0ypp87.js → fk9vef30.js} +85 -74
  109. package/dist/shared/{g6evj1j3.js → g89fmz5s.js} +1 -1
  110. package/dist/shared/{aytqh5y2.js → ghqzdp94.js} +2 -2
  111. package/dist/shared/h9811jjm.js +17 -0
  112. package/dist/shared/{dkxgzaqd.js → hqpxf4a4.js} +1 -1
  113. package/dist/shared/{8skf0cam.js → jpybhf98.js} +1 -1
  114. package/dist/shared/{67txcrcw.js → kgwp4jdd.js} +4 -4
  115. package/dist/shared/kzgrnckd.js +29 -0
  116. package/dist/shared/mazpbcdj.js +233 -0
  117. package/dist/shared/{kb2sdhfb.js → mfvx6kjx.js} +17 -1
  118. package/dist/shared/{svncdeh5.js → mgaq2sd3.js} +11 -8
  119. package/dist/shared/{5mtf6cj3.js → mvmm3xpb.js} +5 -2
  120. package/dist/shared/{b5pftnn2.js → myk8fswx.js} +66 -21
  121. package/dist/shared/{2shdbn8q.js → n306gqkd.js} +9 -9
  122. package/dist/shared/{n816peh1.js → n454kcnw.js} +1 -1
  123. package/dist/shared/n5whk3mt.js +665 -0
  124. package/dist/shared/n7kg8mmx.js +61 -0
  125. package/dist/shared/{vqrjpcqn.js → nmnsgj7x.js} +1 -1
  126. package/dist/shared/{tbrvgw6z.js → npdftanb.js} +19 -26
  127. package/dist/shared/{ek2wt2dz.js → pg4ezgyf.js} +11 -30
  128. package/dist/shared/{5fe3kbsh.js → pgsak1yw.js} +1 -1
  129. package/dist/shared/{bm22fvzr.js → qfw14tna.js} +2 -2
  130. package/dist/shared/{wa07eas9.js → qvwwp1mp.js} +62 -41
  131. package/dist/shared/rbxt795q.js +1766 -0
  132. package/dist/shared/{xjk7hnn5.js → s7he5e5d.js} +2 -2
  133. package/dist/shared/{w72kgg4q.js → ss2kv2rs.js} +6 -6
  134. package/dist/shared/stfa9xcw.js +450 -0
  135. package/dist/shared/{3v7t1yrv.js → th3be82n.js} +2 -2
  136. package/dist/shared/{79dt02vc.js → tr64w5p0.js} +1 -1
  137. package/dist/shared/{cn957f8d.js → tw1nvvfj.js} +3 -3
  138. package/dist/shared/{ed0b8k4s.js → typ3vy7t.js} +1 -1
  139. package/dist/shared/{9628a9xt.js → vk7rag46.js} +1 -1
  140. package/dist/shared/{ze4hxsmy.js → w9ejzaqf.js} +1 -1
  141. package/dist/shared/{vczs8gd4.js → wvtjg5m3.js} +2 -2
  142. package/dist/shared/{9y3mmmh1.js → xfhe73sb.js} +1 -1
  143. package/dist/shared/xqacq37x.js +61 -0
  144. package/dist/shared/{1est4ezg.js → xtsydxjg.js} +3 -3
  145. package/dist/shared/xv8qj97g.js +96 -0
  146. package/dist/shared/{bg3rym3g.js → xwhdcrm4.js} +2 -2
  147. package/dist/shared/{kr0jzbw2.js → yvabyg9k.js} +7 -7
  148. package/dist/shared/yybhre6n.js +136 -0
  149. package/dist/shared/zdqf8k5d.js +8 -0
  150. package/dist/shared/{jznfax0y.js → zq1he88x.js} +4 -4
  151. package/dist/tooling/cli.js +108 -23
  152. package/dist/tooling/driver-run.js +4654 -0
  153. package/dist/tooling/driver-test.d.ts +36 -0
  154. package/dist/tooling/github/comment.d.ts +50 -0
  155. package/dist/tooling/github/post-comment.d.ts +17 -0
  156. package/dist/tooling/migrate/commands.d.ts +3 -2
  157. package/dist/tooling/migrate/config.d.ts +13 -0
  158. package/dist/tooling/migrate/export.d.ts +165 -0
  159. package/dist/tooling/migrate/ext-surface.d.ts +80 -0
  160. package/dist/tooling/migrate/ext.d.ts +31 -4
  161. package/dist/tooling/migrate/import-drizzle.d.ts +40 -0
  162. package/dist/tooling/migrate/index.d.ts +3 -0
  163. package/dist/tooling/migrate/index.js +55 -10
  164. package/dist/tooling/migrate/lint-rules.d.ts +18 -1
  165. package/dist/tooling/migrate/lint.d.ts +14 -1
  166. package/dist/tooling/migrate/place.d.ts +18 -0
  167. package/dist/tooling/migrate/plan-json.d.ts +65 -0
  168. package/dist/tooling/migrate/project.d.ts +13 -1
  169. package/dist/tooling/migrate/pull-emit.d.ts +98 -0
  170. package/dist/tooling/migrate/pull-routines.d.ts +126 -0
  171. package/dist/tooling/migrate/pull-verify.d.ts +26 -0
  172. package/dist/tooling/migrate/pull.d.ts +62 -0
  173. package/dist/tooling/migrate/reprint.d.ts +32 -0
  174. package/dist/tooling/migrate/sandbox.d.ts +8 -2
  175. package/dist/tooling/migrate/schema-api.d.ts +43 -0
  176. package/dist/tooling/migrate/sql-lex.d.ts +18 -0
  177. package/dist/tooling/shared/04svnxxj.js +344 -0
  178. package/dist/tooling/shared/0j0jtse1.js +13 -0
  179. package/dist/tooling/shared/0r05zhyq.js +618 -0
  180. package/dist/tooling/shared/1800eymw.js +49 -0
  181. package/dist/tooling/shared/35zemrmf.js +12 -0
  182. package/dist/tooling/shared/3rmaz3pq.js +140 -0
  183. package/dist/tooling/shared/3yn79q5m.js +12 -0
  184. package/dist/tooling/shared/54tqkxja.js +51 -0
  185. package/dist/tooling/shared/57btdkgm.js +209 -0
  186. package/dist/tooling/shared/5bmksndh.js +118 -0
  187. package/dist/tooling/shared/5p8km5jb.js +38 -0
  188. package/dist/tooling/shared/5x3pf400.js +185 -0
  189. package/dist/tooling/shared/5zrzffnk.js +6 -0
  190. package/dist/tooling/shared/62fxpc1p.js +332 -0
  191. package/dist/tooling/shared/68tf18v4.js +20 -0
  192. package/dist/tooling/shared/6vk28rjp.js +98 -0
  193. package/dist/tooling/shared/76pv6qff.js +9 -0
  194. package/dist/tooling/shared/7aews1zg.js +311 -0
  195. package/dist/tooling/shared/7b3xp9gc.js +195 -0
  196. package/dist/tooling/shared/7kms6fvj.js +20 -0
  197. package/dist/tooling/shared/7pxncnzs.js +40 -0
  198. package/dist/tooling/shared/829fjxyw.js +198 -0
  199. package/dist/tooling/shared/8m5znpdt.js +66 -0
  200. package/dist/tooling/shared/8yr7ges1.js +211 -0
  201. package/dist/tooling/shared/96ps4g1z.js +32 -0
  202. package/dist/tooling/shared/9qr9rkbh.js +143 -0
  203. package/dist/tooling/shared/9sfbtwsk.js +49 -0
  204. package/dist/tooling/shared/9wsnsvbf.js +10 -0
  205. package/dist/tooling/shared/a2xe04bm.js +18 -0
  206. package/dist/tooling/shared/a4pzghha.js +20 -0
  207. package/dist/tooling/shared/am04v8na.js +233 -0
  208. package/dist/tooling/shared/asgr07mp.js +194 -0
  209. package/dist/tooling/shared/c0xevn1h.js +312 -0
  210. package/dist/tooling/shared/dhatassp.js +134 -0
  211. package/dist/tooling/shared/ezqn88nj.js +1696 -0
  212. package/dist/tooling/shared/f77nnfqy.js +231 -0
  213. package/dist/tooling/shared/fh07z1dt.js +15 -0
  214. package/dist/tooling/shared/gqwevek4.js +81 -0
  215. package/dist/tooling/shared/hfq3n3bd.js +4116 -0
  216. package/dist/tooling/shared/htnaj77v.js +17 -0
  217. package/dist/tooling/shared/j664qjrt.js +64 -0
  218. package/dist/tooling/shared/jpgmjsx3.js +38 -0
  219. package/dist/tooling/shared/jr2nmr81.js +497 -0
  220. package/dist/tooling/shared/jsjjz875.js +139 -0
  221. package/dist/tooling/shared/kbqnrs1x.js +1425 -0
  222. package/dist/tooling/shared/kz715295.js +94 -0
  223. package/dist/tooling/shared/m3xt8b1h.js +22 -0
  224. package/dist/tooling/shared/m4hhb8aa.js +9 -0
  225. package/dist/tooling/shared/mw5npye0.js +16 -0
  226. package/dist/tooling/shared/n4jqhpqz.js +27 -0
  227. package/dist/tooling/shared/nxq95eb6.js +42 -0
  228. package/dist/tooling/shared/qartgazh.js +70 -0
  229. package/dist/tooling/shared/rcywj2w8.js +190 -0
  230. package/dist/tooling/shared/s6qha57m.js +16 -0
  231. package/dist/tooling/shared/shjs0379.js +150 -0
  232. package/dist/tooling/shared/sjwkt1pg.js +16 -0
  233. package/dist/tooling/shared/stebqyyd.js +401 -0
  234. package/dist/tooling/shared/t4xveemq.js +250 -0
  235. package/dist/tooling/shared/t6zjeake.js +261 -0
  236. package/dist/tooling/shared/tcwpjvxe.js +9 -0
  237. package/dist/tooling/shared/tpssp7ac.js +160 -0
  238. package/dist/tooling/shared/v6j1ep6w.js +42 -0
  239. package/dist/tooling/shared/vwr2y833.js +270 -0
  240. package/dist/tooling/shared/w17sj05d.js +7 -0
  241. package/dist/tooling/shared/w5d89rb4.js +131 -0
  242. package/dist/tooling/shared/wbwq1jsa.js +157 -0
  243. package/dist/tooling/shared/wezxf3h8.js +17 -0
  244. package/dist/tooling/shared/yb4kd19s.js +12 -0
  245. package/dist/tooling/shared/ybdew6j7.js +294 -0
  246. package/dist/tooling/shared/yfedr1zb.js +6 -0
  247. package/dist/tooling/shared/yhp829bx.js +177 -0
  248. package/dist/tooling/shared/ytk1tjs0.js +604 -0
  249. package/dist/tooling/shared/zs9aymcp.js +210 -0
  250. package/dist/tooling/shared/zx3v9phv.js +8 -0
  251. package/dist/tooling/testing/index.js +3 -3
  252. package/package.json +50 -9
  253. package/dist/shared/ktz42yez.js +0 -113
  254. package/dist/shared/ttsqgp4p.js +0 -71
  255. package/dist/shared/y34a8714.js +0 -150
  256. package/dist/shared/zy8r7qvz.js +0 -116
  257. package/dist/shared/{ywr8hmey.js → 606g0nbf.js} +3 -3
  258. package/dist/shared/{rpycarcr.js → gcefca6p.js} +4 -4
@@ -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,12 +5,20 @@ import {
9
5
  serializeCatalog2,
10
6
  catalogHash2,
11
7
  startupCatalog2,
8
+ startupHash2,
12
9
  catalogFileText2,
13
10
  parseCatalog2
14
- } from "./qq3899ht.js";
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 "./14vx95q4.js";
21
+ } from "./dd5pmnky.js";
18
22
  import {
19
23
  sqlStatements,
20
24
  transactionControl,
@@ -35,14 +39,14 @@ import {
35
39
  assertTargetAlias,
36
40
  listTargets,
37
41
  selectTarget
38
- } from "./tsvww22q.js";
42
+ } from "./6nae0spm.js";
39
43
  import {
40
44
  contributedFields
41
45
  } from "./n0dgs0pb.js";
42
46
  import {
43
47
  assertValidation,
44
48
  writeWouldValidate
45
- } from "./ze4hxsmy.js";
49
+ } from "./w9ejzaqf.js";
46
50
  import {
47
51
  tableOwner,
48
52
  stampOwnership
@@ -51,17 +55,17 @@ import {
51
55
  POSTGRES_FLOOR,
52
56
  uuidv7NeedsPostgres
53
57
  } from "./ga9j92qj.js";
54
- import {
55
- catalog,
56
- isDomain
57
- } from "./ywspezzx.js";
58
58
  import {
59
59
  snakeCase,
60
60
  emittedTypeName
61
- } from "./rpycarcr.js";
61
+ } from "./gcefca6p.js";
62
62
  import {
63
63
  ColumnBuilder2
64
- } from "./ywr8hmey.js";
64
+ } from "./606g0nbf.js";
65
+ import {
66
+ catalog,
67
+ isDomain
68
+ } from "./ywspezzx.js";
65
69
  import {
66
70
  staticNamespace2
67
71
  } from "./358zsv4r.js";
@@ -78,6 +82,374 @@ function projectPath(cwd, ...parts) {
78
82
  return resolve(cwd, ...parts);
79
83
  }
80
84
 
85
+ // src/tooling/migrate/export.ts
86
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
87
+ import { isAbsolute, join } from "node:path";
88
+ var EXPORT_FORMAT = 1;
89
+ var LOGICAL_FORMAT = 1;
90
+ function snapshotFromCatalog(source, namespace, database) {
91
+ return {
92
+ format: EXPORT_FORMAT,
93
+ logical: { namespace, name: namespace, format: LOGICAL_FORMAT },
94
+ catalogHash: startupHash2(source),
95
+ ...database !== undefined ? { database } : {},
96
+ tables: exportTables(source)
97
+ };
98
+ }
99
+ function serializeSnapshot(snapshot) {
100
+ return `${JSON.stringify(snapshot, null, 2)}
101
+ `;
102
+ }
103
+ function parseSnapshot(path, text) {
104
+ let parsed;
105
+ try {
106
+ parsed = JSON.parse(text);
107
+ } catch {
108
+ throw snapshotError(path, "stale", `Catalog snapshot ${path} is not JSON.`);
109
+ }
110
+ if (!isSnapshot(parsed)) {
111
+ throw snapshotError(path, "stale", `Catalog snapshot ${path} is stale: the file format is not ${String(EXPORT_FORMAT)}.`);
112
+ }
113
+ return parsed;
114
+ }
115
+ function databaseName(url) {
116
+ try {
117
+ const name = decodeURIComponent(new URL(url).pathname.replace(/^\//, ""));
118
+ return name.length > 0 ? name : undefined;
119
+ } catch {
120
+ return;
121
+ }
122
+ }
123
+ function catalogNamespace(config) {
124
+ return config.namespace ?? "public";
125
+ }
126
+ function loadSnapshots(cwd, config) {
127
+ const paths = config.external ?? [];
128
+ return paths.map((path) => {
129
+ const file = resolveSnapshot(cwd, path);
130
+ if (!existsSync(file)) {
131
+ throw snapshotError(path, "missing", `Catalog snapshot ${path} is missing.`);
132
+ }
133
+ return { path, snapshot: parseSnapshot(path, readFileSync(file, "utf8")) };
134
+ });
135
+ }
136
+ function externalNamespaceMap(snapshots) {
137
+ const names = new Map;
138
+ for (const loaded of snapshots) {
139
+ for (const table of loaded.snapshot.tables)
140
+ names.set(table.name, table.namespace);
141
+ }
142
+ return names;
143
+ }
144
+ function couplingFindings(source) {
145
+ const findings = [];
146
+ for (const object of source.objects) {
147
+ if (object.kind !== "constraint" || object.definition.constraintKind !== "foreignKey")
148
+ continue;
149
+ const references = object.definition.references;
150
+ if (references === undefined)
151
+ continue;
152
+ const from = object.identity.parent.namespace;
153
+ const to = references.parent.namespace;
154
+ if (from.form !== "static" || to.form !== "static" || from.name === to.name)
155
+ continue;
156
+ findings.push({
157
+ severity: "warning",
158
+ code: "coupling",
159
+ place: `${from.name}.${object.identity.parent.name}.${object.definition.columns.join(",")}`,
160
+ reason: `references ${to.name}.${references.parent.name}.${references.columns.join(",")}`,
161
+ fix: "Cross-catalog foreign keys are allowed inside one database."
162
+ });
163
+ }
164
+ return findings;
165
+ }
166
+ function assertSnapshots(cwd, config, source) {
167
+ if ((config.external ?? []).length === 0)
168
+ return;
169
+ const snapshots = loadSnapshots(cwd, config);
170
+ for (const table of externalTables(source)) {
171
+ const found = findTable(snapshots, table.namespace, table.name);
172
+ if (found === undefined) {
173
+ throw snapshotError(table.name, "missing", `External table ${table.namespace}.${table.name} is missing from the catalog snapshots.`);
174
+ }
175
+ const differ = columnDifference(table.columns, found.table.columns);
176
+ if (differ !== undefined) {
177
+ throw snapshotError(found.path, "stale", `Catalog snapshot ${found.path} is stale: ${table.namespace}.${table.name}.${differ} does not match the export.`);
178
+ }
179
+ }
180
+ }
181
+ async function assertLiveExternal(url, source, snapshots) {
182
+ if (snapshots.length === 0 && externalTables(source).length === 0)
183
+ return;
184
+ const postgres = (await import("postgres")).default;
185
+ const sql = postgres(url, { max: 1, connect_timeout: 5, idle_timeout: 1, onnotice: () => {} });
186
+ try {
187
+ const current = await sql`select current_database() as name`;
188
+ const database = current[0]?.name;
189
+ if (database !== undefined)
190
+ assertSameDatabase(source, snapshots, database);
191
+ for (const table of externalTables(source)) {
192
+ const found = findTable(snapshots, table.namespace, table.name);
193
+ if (found === undefined)
194
+ continue;
195
+ await assertLiveTable(sql, found.path, found.snapshot, table);
196
+ }
197
+ } finally {
198
+ await sql.end({ timeout: 5 });
199
+ }
200
+ }
201
+ function renderExport(cwd, config, source, args) {
202
+ const flags = exportFlags(args);
203
+ const database = exportDatabase(config);
204
+ const snapshot = snapshotFromCatalog(source, catalogNamespace(config), database);
205
+ const text = serializeSnapshot(snapshot);
206
+ if (flags.out !== undefined) {
207
+ const path = isAbsolute(flags.out) ? flags.out : join(cwd, flags.out);
208
+ writeFileSync(path, text);
209
+ }
210
+ if (flags.json || flags.out === undefined)
211
+ return text;
212
+ return `${flags.out}
213
+ `;
214
+ }
215
+ function exportTables(source) {
216
+ const tables = [];
217
+ for (const object of source.objects) {
218
+ if (object.kind !== "table" || object.owner === "external" || object.owner === "ignored") {
219
+ continue;
220
+ }
221
+ if (object.identity.namespace.form !== "static")
222
+ continue;
223
+ const columns = [];
224
+ for (const column of source.objects) {
225
+ if (!isColumnOf(column, object.identity.namespace.name, object.identity.name))
226
+ continue;
227
+ columns.push({
228
+ name: column.identity.name,
229
+ type: column.definition.dataType,
230
+ nullable: column.definition.nullable
231
+ });
232
+ }
233
+ tables.push({
234
+ namespace: object.identity.namespace.name,
235
+ name: object.identity.name,
236
+ columns
237
+ });
238
+ }
239
+ return tables;
240
+ }
241
+ function isColumnOf(object, namespace, table) {
242
+ return object.kind === "column" && object.identity.parent.name === table && object.identity.parent.namespace.form === "static" && object.identity.parent.namespace.name === namespace;
243
+ }
244
+ function externalTables(source) {
245
+ const tables = [];
246
+ for (const object of source.objects) {
247
+ if (object.kind !== "table" || object.owner !== "external")
248
+ continue;
249
+ if (object.identity.namespace.form !== "static")
250
+ continue;
251
+ const columns = [];
252
+ for (const column of source.objects) {
253
+ if (!isColumnOf(column, object.identity.namespace.name, object.identity.name))
254
+ continue;
255
+ columns.push({
256
+ name: column.identity.name,
257
+ type: column.definition.dataType,
258
+ nullable: column.definition.nullable
259
+ });
260
+ }
261
+ tables.push({ namespace: object.identity.namespace.name, name: object.identity.name, columns });
262
+ }
263
+ return tables;
264
+ }
265
+ function findTable(snapshots, namespace, name) {
266
+ for (const loaded of snapshots) {
267
+ for (const table of loaded.snapshot.tables) {
268
+ if (table.name === name && (table.namespace === namespace || namespace === "public")) {
269
+ return { path: loaded.path, snapshot: loaded.snapshot, table };
270
+ }
271
+ }
272
+ }
273
+ for (const loaded of snapshots) {
274
+ for (const table of loaded.snapshot.tables) {
275
+ if (table.name === name)
276
+ return { path: loaded.path, snapshot: loaded.snapshot, table };
277
+ }
278
+ }
279
+ return;
280
+ }
281
+ function columnDifference(left, right) {
282
+ const byName = new Map(right.map((column) => [column.name, column]));
283
+ for (const column of left) {
284
+ const other = byName.get(column.name);
285
+ if (other === undefined)
286
+ return column.name;
287
+ if (other.type !== column.type || other.nullable !== column.nullable)
288
+ return column.name;
289
+ }
290
+ for (const column of right) {
291
+ if (!left.some((item) => item.name === column.name))
292
+ return column.name;
293
+ }
294
+ return;
295
+ }
296
+ function assertSameDatabase(source, snapshots, database) {
297
+ if (!hasCrossCatalogForeignKey(source))
298
+ return;
299
+ for (const loaded of snapshots) {
300
+ const name = loaded.snapshot.database;
301
+ if (name !== undefined && name !== database) {
302
+ throw new OkmError2("OKM1842", `A foreign key references ${loaded.snapshot.logical.namespace} in database ${name}. This database is ${database}.`, {
303
+ kind: "invalid",
304
+ fieldReason: "database",
305
+ fix: {
306
+ summary: "Keep the foreign key inside one database. Cross-catalog keys are allowed only there."
307
+ }
308
+ });
309
+ }
310
+ }
311
+ }
312
+ function hasCrossCatalogForeignKey(source) {
313
+ for (const object of source.objects) {
314
+ if (object.kind !== "constraint" || object.definition.constraintKind !== "foreignKey")
315
+ continue;
316
+ const references = object.definition.references;
317
+ if (references === undefined)
318
+ continue;
319
+ const from = object.identity.parent.namespace;
320
+ const to = references.parent.namespace;
321
+ if (from.form === "static" && to.form === "static" && from.name !== to.name)
322
+ return true;
323
+ }
324
+ return false;
325
+ }
326
+ async function assertLiveTable(sql, path, snapshot, table) {
327
+ const hash = await liveHash(sql, table.namespace);
328
+ if (hash !== undefined && hash !== snapshot.catalogHash) {
329
+ throw snapshotError(path, "stale", `Catalog snapshot ${path} is stale: the catalog hash changed.`);
330
+ }
331
+ const live = await liveColumns(sql, table.namespace, table.name);
332
+ if (live === undefined) {
333
+ throw snapshotError(path, "missing", `External table ${table.namespace}.${table.name} is missing on the database.`);
334
+ }
335
+ const differ = columnDifference(snapshotTableColumns(snapshot, table), live);
336
+ if (differ !== undefined) {
337
+ throw snapshotError(path, "stale", `Catalog snapshot ${path} is stale: ${table.namespace}.${table.name}.${differ} is missing on the database.`);
338
+ }
339
+ }
340
+ function snapshotTableColumns(snapshot, table) {
341
+ for (const item of snapshot.tables) {
342
+ if (item.name === table.name)
343
+ return item.columns;
344
+ }
345
+ return table.columns;
346
+ }
347
+ async function liveHash(sql, namespace) {
348
+ const reg = `${quoteIdent2(namespace)}.${quoteIdent2("okm_meta")}`;
349
+ const present = await sql.unsafe(`select to_regclass('${reg}')::text as reg`);
350
+ if (present[0]?.reg == null)
351
+ return;
352
+ const rows = await sql.unsafe(`select catalog_hash from ${reg} where id = 'head'`);
353
+ return rows[0]?.catalog_hash;
354
+ }
355
+ async function liveColumns(sql, namespace, table) {
356
+ const rows = await sql`select a.attname as name, format_type(a.atttypid, a.atttypmod) as type, a.attnotnull as not_null
357
+ from pg_attribute a
358
+ join pg_class c on c.oid = a.attrelid
359
+ join pg_namespace n on n.oid = c.relnamespace
360
+ where n.nspname = ${namespace} and c.relname = ${table}
361
+ and a.attnum > 0 and not a.attisdropped
362
+ order by a.attnum`;
363
+ if (rows.length === 0) {
364
+ const exists = await sql`
365
+ select to_regclass(${`${quoteIdent2(namespace)}.${quoteIdent2(table)}`})::text as reg
366
+ `;
367
+ if (exists[0]?.reg == null)
368
+ return;
369
+ }
370
+ return rows.map((row) => ({ name: row.name, type: row.type, nullable: !row.not_null }));
371
+ }
372
+ function resolveSnapshot(cwd, path) {
373
+ return isAbsolute(path) ? path : projectPath(cwd, path);
374
+ }
375
+ function exportDatabase(config) {
376
+ const target = config.database;
377
+ if (typeof target === "string")
378
+ return databaseName(target);
379
+ if (target?.url !== undefined)
380
+ return databaseName(target.url);
381
+ return;
382
+ }
383
+ function exportFlags(args) {
384
+ let json = false;
385
+ let out;
386
+ for (let index = 0;index < args.length; index += 1) {
387
+ const arg = args[index] ?? "";
388
+ if (arg === "--json") {
389
+ json = true;
390
+ continue;
391
+ }
392
+ if (arg === "--out") {
393
+ const value = args[index + 1];
394
+ if (value === undefined || value.startsWith("--")) {
395
+ throw new OkmError2("invalid", "--out needs a file.", {
396
+ fix: { summary: "Pass --out <file>." }
397
+ });
398
+ }
399
+ out = value;
400
+ index += 1;
401
+ continue;
402
+ }
403
+ if (arg.startsWith("--out=")) {
404
+ out = arg.slice("--out=".length);
405
+ continue;
406
+ }
407
+ throw new OkmError2("invalid", `Unknown flag ${arg}.`, {
408
+ fix: { summary: "okm catalog export accepts --json and --out <file>." }
409
+ });
410
+ }
411
+ return out === undefined ? { json } : { json, out };
412
+ }
413
+ function snapshotError(path, reason, message) {
414
+ return new OkmError2("OKM1841", message, {
415
+ kind: reason === "missing" ? "not_found" : "schema_drift",
416
+ fieldReason: reason,
417
+ columns: [path],
418
+ fix: {
419
+ 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."
420
+ }
421
+ });
422
+ }
423
+ function isSnapshot(value) {
424
+ if (!isRecord(value) || value.format !== EXPORT_FORMAT)
425
+ return false;
426
+ if (typeof value.catalogHash !== "string")
427
+ return false;
428
+ if (!isLogical(value.logical))
429
+ return false;
430
+ if (value.database !== undefined && typeof value.database !== "string")
431
+ return false;
432
+ if (!Array.isArray(value.tables))
433
+ return false;
434
+ return value.tables.every(isTable);
435
+ }
436
+ function isLogical(value) {
437
+ if (!isRecord(value))
438
+ return false;
439
+ return typeof value.namespace === "string" && typeof value.name === "string" && value.format === LOGICAL_FORMAT;
440
+ }
441
+ function isTable(value) {
442
+ if (!isRecord(value) || typeof value.namespace !== "string" || typeof value.name !== "string") {
443
+ return false;
444
+ }
445
+ if (!Array.isArray(value.columns))
446
+ return false;
447
+ return value.columns.every((column) => isRecord(column) && typeof column.name === "string" && typeof column.type === "string" && typeof column.nullable === "boolean");
448
+ }
449
+ function isRecord(value) {
450
+ return typeof value === "object" && value !== null;
451
+ }
452
+
81
453
  // src/tooling/errors/registry.ts
82
454
  var ERROR_DOCS = [
83
455
  {
@@ -236,6 +608,12 @@ var ERROR_DOCS = [
236
608
  summary: "An operator does not apply to the column type.",
237
609
  fix: "Use an operator the error lists for that column type. Search needs a column built for search, not a plain text column."
238
610
  },
611
+ {
612
+ code: "OKM1125",
613
+ title: "Bad SQL fragment",
614
+ 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.",
615
+ 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()."
616
+ },
239
617
  {
240
618
  code: "OKM1130",
241
619
  title: "Cursor order",
@@ -449,7 +827,7 @@ var ERROR_DOCS = [
449
827
  {
450
828
  code: "OKM1538",
451
829
  title: "Type rewrite",
452
- summary: "A statement changes a column type in a way that rewrites the table.",
830
+ 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.",
453
831
  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."
454
832
  },
455
833
  {
@@ -530,12 +908,24 @@ var ERROR_DOCS = [
530
908
  summary: "A step contains COMMIT, ROLLBACK, or other transaction control, including inside a DO body. A function or procedure body is not this.",
531
909
  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."
532
910
  },
911
+ {
912
+ code: "OKM1552",
913
+ title: "Reprinted declaration",
914
+ summary: "A domain check or a LANGUAGE sql function body is stored differently from the text in the migration catalog.",
915
+ 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."
916
+ },
533
917
  {
534
918
  code: "OKM1601",
535
919
  title: "Stale typed SQL",
536
920
  summary: "A typed SQL signature no longer matches the database.",
537
921
  fix: "Regenerate the typed SQL and commit the new signature."
538
922
  },
923
+ {
924
+ code: "OKM1602",
925
+ title: "Bad typed SQL",
926
+ summary: "A raw statement is missing its class, is not one select or one write, or the result class has no column initializers.",
927
+ fix: 'Pass "read" or "write", a fragment`...`, and a class with an initializer on every column. Identifiers are ident(name).'
928
+ },
539
929
  {
540
930
  code: "OKM1701",
541
931
  title: "Tenant context missing",
@@ -545,8 +935,8 @@ var ERROR_DOCS = [
545
935
  {
546
936
  code: "OKM1702",
547
937
  title: "Unverifiable tenant SQL",
548
- summary: "Raw SQL touches a tenant table and the tenant predicate cannot be proved.",
549
- fix: "Use the query builder, or mark the fragment trusted with a reason if it is reviewed."
938
+ summary: "Raw SQL on a tenant table cannot prove the tenant predicate. A raw write is this. A raw read that does not select the primary key with ident is this.",
939
+ fix: 'Use update or delete, or select the primary key with ident(name). unscoped("reason") is the reviewed hatch.'
550
940
  },
551
941
  {
552
942
  code: "OKM1704",
@@ -739,6 +1129,18 @@ var ERROR_DOCS = [
739
1129
  title: "Pooler session",
740
1130
  summary: "Two statements on the same connection saw different backend pids. A transaction-mode pooler is in the way.",
741
1131
  fix: "Use a direct connection, or pass --allow-pooler."
1132
+ },
1133
+ {
1134
+ code: "OKM1855",
1135
+ title: "Pull would overwrite",
1136
+ summary: "okm pull or okm import drizzle found a file it would replace. It writes nothing.",
1137
+ fix: "Pass --out <directory> to write somewhere else, or move the file."
1138
+ },
1139
+ {
1140
+ code: "OKM1856",
1141
+ title: "Drizzle is not installed",
1142
+ summary: "okm import drizzle could not resolve drizzle-orm from the project.",
1143
+ fix: "Install drizzle-orm in the project that owns the schema."
742
1144
  }
743
1145
  ];
744
1146
  var byCode;
@@ -755,25 +1157,25 @@ function indexDocs() {
755
1157
  }
756
1158
 
757
1159
  // src/tooling/migrate/files.ts
758
- import { existsSync, readdirSync, readFileSync } from "node:fs";
759
- import { join } from "node:path";
1160
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
1161
+ import { join as join2 } from "node:path";
760
1162
  function loadMigrations(directory) {
761
- if (!existsSync(directory))
1163
+ if (!existsSync2(directory))
762
1164
  return [];
763
1165
  const names = readdirSync(directory).filter((file) => file.endsWith(".sql")).sort();
764
1166
  const migrations = [];
765
1167
  for (const file of names) {
766
1168
  const id = file.slice(0, -".sql".length);
767
- const catalogPath = join(directory, `${id}.catalog.json`);
768
- if (!existsSync(catalogPath)) {
1169
+ const catalogPath = join2(directory, `${id}.catalog.json`);
1170
+ if (!existsSync2(catalogPath)) {
769
1171
  throw new OkmError2("invalid", `Migration ${id} is missing its catalog snapshot.`, {
770
1172
  fix: {
771
1173
  summary: "Generate the migration again so the SQL and the catalog are written together."
772
1174
  }
773
1175
  });
774
1176
  }
775
- const plan = readMigrationPlan(join(directory, file));
776
- const stored = parseCatalog2(readFileSync(catalogPath, "utf8"));
1177
+ const plan = readMigrationPlan(join2(directory, file));
1178
+ const stored = parseCatalog2(readFileSync2(catalogPath, "utf8"));
777
1179
  const startup = startupCatalog2(stored);
778
1180
  const hash = catalogHash2(startup);
779
1181
  migrations.push(startup === stored ? { id, catalogHash: hash, steps: plan.steps } : { id, catalogHash: hash, legacyHash: catalogHash2(stored), steps: plan.steps });
@@ -781,7 +1183,7 @@ function loadMigrations(directory) {
781
1183
  return migrations;
782
1184
  }
783
1185
  function readMigrationPlan(path) {
784
- const text = prepareMigrationText(readFileSync(path, "utf8"), path);
1186
+ const text = prepareMigrationText(readFileSync2(path, "utf8"), path);
785
1187
  const plan = parsePlan(text);
786
1188
  assertMigrationSteps(text, plan.steps.length, path);
787
1189
  return plan;
@@ -794,13 +1196,13 @@ function loadHeadSnapshot(directory) {
794
1196
  return {
795
1197
  id: last.id,
796
1198
  catalogHash: last.catalogHash,
797
- catalog: parseCatalog2(readFileSync(join(directory, `${last.id}.catalog.json`), "utf8"))
1199
+ catalog: parseCatalog2(readFileSync2(join2(directory, `${last.id}.catalog.json`), "utf8"))
798
1200
  };
799
1201
  }
800
1202
 
801
1203
  // src/tooling/migrate/lint.ts
802
- import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "node:fs";
803
- import { join as join2 } from "node:path";
1204
+ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "node:fs";
1205
+ import { join as join3 } from "node:path";
804
1206
 
805
1207
  // src/tooling/migrate/lint-rules.ts
806
1208
  var INTEGER_WIDTH = {
@@ -887,7 +1289,7 @@ function rule(code, category, severity, doc, check) {
887
1289
  function hit(matched, reason) {
888
1290
  return matched ? [reason] : [];
889
1291
  }
890
- function normalized(sql) {
1292
+ function normalizedSql(sql) {
891
1293
  const collapsed = sql.trim().replace(/\s+/g, " ");
892
1294
  let out = "";
893
1295
  let quoted = false;
@@ -902,7 +1304,7 @@ function normalized(sql) {
902
1304
  return out;
903
1305
  }
904
1306
  function weakensRls(input) {
905
- const text = normalized(input.step.sql);
1307
+ const text = normalizedSql(input.step.sql);
906
1308
  if (/\bdisable row level security\b/.test(text))
907
1309
  return ["disables row level security"];
908
1310
  if (/\bno force row level security\b/.test(text))
@@ -1019,7 +1421,7 @@ function parenthesized(text, open) {
1019
1421
  }
1020
1422
  function policyExpression(input, table, name) {
1021
1423
  const expression = expressionOf(input.after, table, name) ?? expressionOf(input.before, table, name);
1022
- return expression === undefined ? undefined : normalized(expression);
1424
+ return expression === undefined ? undefined : normalizedSql(expression);
1023
1425
  }
1024
1426
  function expressionOf(source, table, name) {
1025
1427
  for (const object of source.objects) {
@@ -1042,7 +1444,7 @@ function hasPolicy(source, table) {
1042
1444
  return source.objects.some((object) => object.kind === "policy" && object.identity.parent.name === table);
1043
1445
  }
1044
1446
  function dataStatement(step) {
1045
- const text = normalized(step.sql);
1447
+ const text = normalizedSql(step.sql);
1046
1448
  if (/^(?:do|call)\b/.test(text))
1047
1449
  return ["the body is not analysed"];
1048
1450
  if (step.backfill !== undefined)
@@ -1056,16 +1458,16 @@ function dataStatement(step) {
1056
1458
  return [];
1057
1459
  }
1058
1460
  function matches(step, kind, pattern) {
1059
- return pattern.test(normalized(step.sql)) || step.kind === kind;
1461
+ return pattern.test(normalizedSql(step.sql)) || step.kind === kind;
1060
1462
  }
1061
1463
  function renamesTable(step) {
1062
- const text = normalized(step.sql);
1464
+ const text = normalizedSql(step.sql);
1063
1465
  if (/^alter table\s+\S+\s+rename to\b/.test(text))
1064
1466
  return true;
1065
1467
  return step.kind === "rename-table" && !/^alter table\s+\S+\s+rename (column|constraint)\b/.test(text);
1066
1468
  }
1067
1469
  function droppedType(input) {
1068
- const text = normalized(input.step.sql);
1470
+ const text = normalizedSql(input.step.sql);
1069
1471
  const dropping = /^drop type\b/.test(text);
1070
1472
  if (!dropping && input.step.kind !== "drop-enum" && input.step.kind !== "drop-domain") {
1071
1473
  return;
@@ -1081,7 +1483,7 @@ function droppedType(input) {
1081
1483
  return "enum";
1082
1484
  }
1083
1485
  function requiredColumn(input) {
1084
- const text = normalized(input.step.sql);
1486
+ const text = normalizedSql(input.step.sql);
1085
1487
  const adding = /^alter table\s+\S+\s+add column\b/.test(text) || input.step.kind === "add-column-required";
1086
1488
  if (!adding)
1087
1489
  return false;
@@ -1097,7 +1499,7 @@ function requiredColumn(input) {
1097
1499
  function uniqueConstraint(input) {
1098
1500
  if (!tableExists(input))
1099
1501
  return false;
1100
- const text = normalized(input.step.sql);
1502
+ const text = normalizedSql(input.step.sql);
1101
1503
  if (/\busing index\b/.test(text))
1102
1504
  return false;
1103
1505
  if (/^alter table\s+\S+\s+add constraint\b/.test(text) && /\b(unique|primary key)\b/.test(text)) {
@@ -1108,16 +1510,16 @@ function uniqueConstraint(input) {
1108
1510
  const name = createdIndexName(text);
1109
1511
  if (name === undefined)
1110
1512
  return false;
1111
- return input.later.some((step) => usesIndex(normalized(step.sql), name));
1513
+ return input.later.some((step) => usesIndex(normalizedSql(step.sql), name));
1112
1514
  }
1113
1515
  function uniqueIndex(input) {
1114
- const text = normalized(input.step.sql);
1516
+ const text = normalizedSql(input.step.sql);
1115
1517
  if (!/^create unique index\b/.test(text))
1116
1518
  return false;
1117
1519
  if (!tableExists(input))
1118
1520
  return false;
1119
1521
  const name = createdIndexName(text);
1120
- if (name !== undefined && input.later.some((step) => usesIndex(normalized(step.sql), name))) {
1522
+ if (name !== undefined && input.later.some((step) => usesIndex(normalizedSql(step.sql), name))) {
1121
1523
  return false;
1122
1524
  }
1123
1525
  return true;
@@ -1130,7 +1532,7 @@ function usesIndex(text, name) {
1130
1532
  return text.includes(`using index "${name}"`) || text.includes(`using index ${name}`);
1131
1533
  }
1132
1534
  function unsafeSetNotNull(input) {
1133
- const text = normalized(input.step.sql);
1535
+ const text = normalizedSql(input.step.sql);
1134
1536
  const match = /^alter table\s+(\S+)\s+alter column\s+(\S+)\s+set not null\b/.exec(text);
1135
1537
  if (match === null && input.step.kind !== "set-not-null")
1136
1538
  return false;
@@ -1146,21 +1548,21 @@ function unsafeSetNotNull(input) {
1146
1548
  function validatedNotNullCheck(earlier, table, column) {
1147
1549
  const needle = `check (${column} is not null) not valid`;
1148
1550
  for (const step of earlier) {
1149
- const text = normalized(step.sql);
1551
+ const text = normalizedSql(step.sql);
1150
1552
  if (!text.startsWith(`alter table ${table} `) || !text.includes(needle))
1151
1553
  continue;
1152
1554
  const added = /^alter table\s+\S+\s+add constraint\s+(\S+)\s+check\b/.exec(text);
1153
1555
  const name = added?.[1];
1154
1556
  if (name === undefined)
1155
1557
  continue;
1156
- const validated = earlier.some((item) => normalized(item.sql).startsWith(`alter table ${table} validate constraint ${name}`));
1558
+ const validated = earlier.some((item) => normalizedSql(item.sql).startsWith(`alter table ${table} validate constraint ${name}`));
1157
1559
  if (validated)
1158
1560
  return true;
1159
1561
  }
1160
1562
  return false;
1161
1563
  }
1162
1564
  function blockingIndex(input) {
1163
- const text = normalized(input.step.sql);
1565
+ const text = normalizedSql(input.step.sql);
1164
1566
  if (!/^create(?: unique)? index\b/.test(text) && input.step.kind !== "create-index")
1165
1567
  return false;
1166
1568
  if (/\bconcurrently\b/.test(text))
@@ -1168,10 +1570,10 @@ function blockingIndex(input) {
1168
1570
  return tableExists(input);
1169
1571
  }
1170
1572
  function checkWithoutNotValid(input) {
1171
- return addedConstraint(input, "check") && !/\bnot valid\b/.test(normalized(input.step.sql));
1573
+ return addedConstraint(input, "check") && !/\bnot valid\b/.test(normalizedSql(input.step.sql));
1172
1574
  }
1173
1575
  function foreignKeyWithoutNotValid(input) {
1174
- return addedConstraint(input, "foreign key") && !/\bnot valid\b/.test(normalized(input.step.sql));
1576
+ return addedConstraint(input, "foreign key") && !/\bnot valid\b/.test(normalizedSql(input.step.sql));
1175
1577
  }
1176
1578
  function validatingCheck(input) {
1177
1579
  if (widenValidate(input))
@@ -1181,7 +1583,7 @@ function validatingCheck(input) {
1181
1583
  return checkWithoutNotValid(input);
1182
1584
  }
1183
1585
  function widenValidate(input) {
1184
- const text = normalized(input.step.sql);
1586
+ const text = normalizedSql(input.step.sql);
1185
1587
  if (!/^alter table\s+\S+\s+validate constraint\b/.test(text))
1186
1588
  return false;
1187
1589
  if (input.step.kind === "widen-check")
@@ -1192,7 +1594,7 @@ function widenValidate(input) {
1192
1594
  return input.earlier.some((step) => {
1193
1595
  if (step.kind !== "widen-check")
1194
1596
  return false;
1195
- const prior = normalized(step.sql);
1597
+ const prior = normalizedSql(step.sql);
1196
1598
  return prior.includes(`add constraint ${name} `);
1197
1599
  });
1198
1600
  }
@@ -1204,13 +1606,13 @@ function validatingForeignKey(input) {
1204
1606
  function addedConstraint(input, kind) {
1205
1607
  if (!tableExists(input))
1206
1608
  return false;
1207
- const text = normalized(input.step.sql);
1609
+ const text = normalizedSql(input.step.sql);
1208
1610
  if (!/^alter table\s+\S+\s+add constraint\b/.test(text))
1209
1611
  return false;
1210
1612
  return new RegExp(`\\b${kind}\\b`).test(text);
1211
1613
  }
1212
1614
  function validates(input, kind) {
1213
- const text = normalized(input.step.sql);
1615
+ const text = normalizedSql(input.step.sql);
1214
1616
  const table = /^alter table\s+\S+\s+validate constraint\s+(\S+)/.exec(text);
1215
1617
  if (table !== null) {
1216
1618
  const token = table[1];
@@ -1230,7 +1632,7 @@ function validates(input, kind) {
1230
1632
  }
1231
1633
  var TYPE_CHANGE = /^alter table\s+(\S+)\s+alter column\s+(\S+)\s+(?:set data type|type)\b/;
1232
1634
  function typeChange(input, mode) {
1233
- const text = normalized(input.step.sql);
1635
+ const text = normalizedSql(input.step.sql);
1234
1636
  const changing = TYPE_CHANGE.test(text) || input.step.kind === "set-column-type";
1235
1637
  if (!changing)
1236
1638
  return [];
@@ -1246,18 +1648,19 @@ function typeChange(input, mode) {
1246
1648
  const shrink = lengthShrink(before, after);
1247
1649
  if (mode === "shrink")
1248
1650
  return shrink ? ["shortens a character length"] : [];
1249
- if (shrink)
1250
- return [];
1651
+ if (shrink) {
1652
+ return mode === "rewrite" && tableRewrites(before, after, input) ? ["rewrites the table"] : [];
1653
+ }
1251
1654
  if (mode === "narrow")
1252
1655
  return narrows(before, after) ? ["narrows a type"] : [];
1253
- if (lengthGrow(before, after))
1656
+ if (lengthGrow(before, after) && mode !== "rewrite")
1254
1657
  return [];
1255
1658
  if (mode === "incompatible")
1256
1659
  return ["changes a column type"];
1257
- return rewrites(before, after) ? ["rewrites the table"] : [];
1660
+ return tableRewrites(before, after, input) ? ["rewrites the table"] : [];
1258
1661
  }
1259
1662
  function columnTypes(input) {
1260
- const text = normalized(input.step.sql);
1663
+ const text = normalizedSql(input.step.sql);
1261
1664
  const match = TYPE_CHANGE.exec(text);
1262
1665
  const table = lastName(match?.[1]);
1263
1666
  const column = lastName(match?.[2]);
@@ -1282,13 +1685,115 @@ function lengthGrow(before, after) {
1282
1685
  const right = charLength(after);
1283
1686
  return left !== undefined && right !== undefined && left.family === right.family && right.length > left.length;
1284
1687
  }
1285
- function rewrites(before, after) {
1286
- if (lengthShrink(before, after) || lengthGrow(before, after))
1688
+ function tableRewrites(before, after, input) {
1689
+ if (usingRewrites(input))
1690
+ return true;
1691
+ if (varyingRelaxation(before, after))
1692
+ return false;
1693
+ if (isVarying(before) && after === "text")
1694
+ return false;
1695
+ if (before === "text" && unconstrainedVarying(after))
1696
+ return false;
1697
+ if ((before === "text" || isVarying(before)) && unconstrainedCharacter(after))
1698
+ return false;
1699
+ if (numericRelaxation(before, after))
1700
+ return false;
1701
+ if (binaryPair(before, after))
1287
1702
  return false;
1288
- if (isCharacter(before) && after === "text")
1703
+ if (domainSkipsRewrite(before, after, input))
1289
1704
  return false;
1290
1705
  return true;
1291
1706
  }
1707
+ function statementMayReprint(sql) {
1708
+ const text = normalizedSql(sql);
1709
+ if (/^(?:create|alter) domain\b/.test(text))
1710
+ return true;
1711
+ return /^create(?: or replace)? function\b/.test(text) && /\blanguage sql\b/.test(text);
1712
+ }
1713
+ function usingRewrites(input) {
1714
+ const text = normalizedSql(input.step.sql);
1715
+ const at = text.search(/\busing\b/);
1716
+ if (at < 0)
1717
+ return false;
1718
+ const column = lastName(TYPE_CHANGE.exec(text)?.[2]);
1719
+ if (column === undefined)
1720
+ return true;
1721
+ const expression = text.slice(at + "using".length).trim().replace(/;$/, "");
1722
+ return !plainCast(expression, column);
1723
+ }
1724
+ function plainCast(expression, column) {
1725
+ const bare = column.toLowerCase();
1726
+ const quoted = `"${column}"`;
1727
+ return expression.startsWith(`${bare}::`) || expression.startsWith(`${quoted}::`) || expression.startsWith(`cast(${bare} as `) || expression.startsWith(`cast(${quoted} as `);
1728
+ }
1729
+ function isVarying(type) {
1730
+ return type === "character varying" || type === "varchar" || type.startsWith("character varying(") || type.startsWith("varchar(");
1731
+ }
1732
+ function unconstrainedVarying(type) {
1733
+ return type === "character varying" || type === "varchar";
1734
+ }
1735
+ function unconstrainedCharacter(type) {
1736
+ return type === "bpchar" || type === "character";
1737
+ }
1738
+ function varyingRelaxation(before, after) {
1739
+ const left = charLength(before);
1740
+ if (left?.family === "varying" && unconstrainedVarying(after))
1741
+ return true;
1742
+ const right = charLength(after);
1743
+ return left?.family === "varying" && right?.family === "varying" && right.length > left.length;
1744
+ }
1745
+ function numericRelaxation(before, after) {
1746
+ const left = numericBounds(before);
1747
+ const right = numericBounds(after);
1748
+ if (left === undefined || right === undefined)
1749
+ return false;
1750
+ if (right.precision === undefined)
1751
+ return left.precision !== undefined;
1752
+ if (left.precision === undefined || left.scale === undefined || right.scale === undefined) {
1753
+ return false;
1754
+ }
1755
+ return right.scale === left.scale && right.precision > left.precision;
1756
+ }
1757
+ function numericBounds(type) {
1758
+ const match = /^numeric(?:\((\d+)(?:,(\d+))?\))?$/.exec(type);
1759
+ if (match === null)
1760
+ return;
1761
+ if (match[1] === undefined)
1762
+ return { precision: undefined, scale: undefined };
1763
+ return {
1764
+ precision: Number(match[1]),
1765
+ scale: match[2] === undefined ? 0 : Number(match[2])
1766
+ };
1767
+ }
1768
+ function binaryPair(before, after) {
1769
+ if (before === "integer" && after === "oid")
1770
+ return true;
1771
+ if (before === "oid" && after === "integer")
1772
+ return true;
1773
+ if (before === "cidr" && after === "inet")
1774
+ return true;
1775
+ return before === "xml" && after === "text";
1776
+ }
1777
+ function domainSkipsRewrite(before, after, input) {
1778
+ const next = domainNamed(input, after);
1779
+ if (next !== undefined && next.check.trim().length === 0 && canonical(next.base) === before) {
1780
+ return true;
1781
+ }
1782
+ const previous = domainNamed(input, before);
1783
+ return previous !== undefined && canonical(previous.base) === after;
1784
+ }
1785
+ function domainNamed(input, name) {
1786
+ for (const source of [input.after, input.before]) {
1787
+ for (const object of source.objects) {
1788
+ if (object.kind !== "type" || object.identity.name !== name)
1789
+ continue;
1790
+ if (!isDomain(object.definition))
1791
+ continue;
1792
+ return object.definition;
1793
+ }
1794
+ }
1795
+ return;
1796
+ }
1292
1797
  function narrows(before, after) {
1293
1798
  const leftInt = INTEGER_WIDTH[before];
1294
1799
  const rightInt = INTEGER_WIDTH[after];
@@ -1313,9 +1818,6 @@ function charLength(type) {
1313
1818
  return { family: "fixed", length: Number(fixed[1]) };
1314
1819
  return;
1315
1820
  }
1316
- function isCharacter(type) {
1317
- return type === "text" || charLength(type) !== undefined;
1318
- }
1319
1821
  function numericSpec(type) {
1320
1822
  const match = /^numeric(?:\((\d+)(?:,(\d+))?\))?$/.exec(type);
1321
1823
  if (match === null)
@@ -1332,7 +1834,7 @@ function tableExists(input) {
1332
1834
  return name !== undefined && input.existingTables.has(name);
1333
1835
  }
1334
1836
  function statementTable(step) {
1335
- const text = normalized(step.sql);
1837
+ const text = normalizedSql(step.sql);
1336
1838
  const altered = /^(?:alter|drop) table\s+(\S+)/.exec(text);
1337
1839
  if (altered?.[1] !== undefined)
1338
1840
  return lastName(altered[1]);
@@ -1368,6 +1870,134 @@ function isSerial(column) {
1368
1870
  return /nextval\s*\(/i.test(column.definition.defaultExpression ?? "");
1369
1871
  }
1370
1872
 
1873
+ // src/tooling/migrate/reprint.ts
1874
+ var REPRINT_CODE = "OKM1552";
1875
+ function reprintHits(declared, stored, steps) {
1876
+ const hits = [];
1877
+ const silenced = new Set;
1878
+ for (const difference of differences(declared, stored)) {
1879
+ const index = stepIndex(steps, difference.kind, difference.name);
1880
+ if (index === undefined)
1881
+ continue;
1882
+ const step = steps[index];
1883
+ const allow = step?.allows?.find((item) => item.code === REPRINT_CODE);
1884
+ if (allow !== undefined && allow.reason.trim().length > 0) {
1885
+ silenced.add(index);
1886
+ continue;
1887
+ }
1888
+ hits.push({
1889
+ severity: "warning",
1890
+ code: REPRINT_CODE,
1891
+ place: `step ${String(index + 1)}`,
1892
+ reason: difference.reason
1893
+ });
1894
+ }
1895
+ steps.forEach((step, index) => {
1896
+ for (const allow of step.allows ?? []) {
1897
+ if (allow.code !== REPRINT_CODE)
1898
+ continue;
1899
+ const place = `step ${String(index + 1)}`;
1900
+ if (allow.reason.trim().length === 0) {
1901
+ hits.push({
1902
+ severity: "error",
1903
+ code: "OKM1510",
1904
+ place,
1905
+ reason: "override needs a reason"
1906
+ });
1907
+ continue;
1908
+ }
1909
+ if (silenced.has(index))
1910
+ continue;
1911
+ if (hits.some((item) => item.place === place && item.code === REPRINT_CODE))
1912
+ continue;
1913
+ if (!statementMayReprint(step.sql))
1914
+ continue;
1915
+ hits.push({
1916
+ severity: "error",
1917
+ code: "OKM1510",
1918
+ place,
1919
+ reason: `override ${REPRINT_CODE} does not match this statement`
1920
+ });
1921
+ }
1922
+ });
1923
+ return hits;
1924
+ }
1925
+ function differences(declared, stored) {
1926
+ const found = [];
1927
+ const storedDomains = domainChecks(stored);
1928
+ for (const [name, check] of domainChecks(declared)) {
1929
+ const live = storedDomains.get(name);
1930
+ if (live === undefined || live === check)
1931
+ continue;
1932
+ found.push({
1933
+ kind: "domain",
1934
+ name,
1935
+ reason: `domain ${name} check reprints differently`
1936
+ });
1937
+ }
1938
+ const storedFunctions = sqlBodies(stored);
1939
+ for (const [key, body] of sqlBodies(declared)) {
1940
+ const live = storedFunctions.get(key);
1941
+ if (live === undefined || live.body === body.body)
1942
+ continue;
1943
+ found.push({
1944
+ kind: "function",
1945
+ name: body.name,
1946
+ reason: `function ${body.name} body reprints differently`
1947
+ });
1948
+ }
1949
+ return found;
1950
+ }
1951
+ function domainChecks(source) {
1952
+ const checks = new Map;
1953
+ for (const object of source.objects) {
1954
+ if (object.kind !== "type" || !isDomain(object.definition))
1955
+ continue;
1956
+ checks.set(object.identity.name, object.definition.check.trim());
1957
+ }
1958
+ return checks;
1959
+ }
1960
+ function sqlBodies(source) {
1961
+ const bodies = new Map;
1962
+ for (const object of source.objects) {
1963
+ if (object.kind !== "function" || object.definition.language !== "sql")
1964
+ continue;
1965
+ bodies.set(functionKey(object), {
1966
+ name: object.identity.name,
1967
+ body: object.definition.body.trim()
1968
+ });
1969
+ }
1970
+ return bodies;
1971
+ }
1972
+ function functionKey(object) {
1973
+ const args = object.identity.argTypes.map((typeName) => canonicalTypeName(typeName)).join(",");
1974
+ return `${object.identity.name}(${args})`;
1975
+ }
1976
+ function stepIndex(steps, kind, name) {
1977
+ let found;
1978
+ steps.forEach((step, index) => {
1979
+ if (mentions(normalizedSql(step.sql), kind, name))
1980
+ found = index;
1981
+ });
1982
+ return found;
1983
+ }
1984
+ function mentions(text, kind, name) {
1985
+ if (kind === "domain" && !/^(?:create|alter) domain\b/.test(text))
1986
+ return false;
1987
+ if (kind === "function") {
1988
+ if (!/^create(?: or replace)? function\b/.test(text) || !/\blanguage sql\b/.test(text)) {
1989
+ return false;
1990
+ }
1991
+ }
1992
+ const quoted = `"${name}"`;
1993
+ if (text.includes(quoted))
1994
+ return true;
1995
+ return new RegExp(`\\b${escapeRegExp(name.toLowerCase())}\\b`).test(text);
1996
+ }
1997
+ function escapeRegExp(value) {
1998
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1999
+ }
2000
+
1371
2001
  // src/tooling/migrate/lint.ts
1372
2002
  function lintPlan(plan, before, after) {
1373
2003
  const existingTables = tableNames(before);
@@ -1377,6 +2007,9 @@ function lintPlan(plan, before, after) {
1377
2007
  });
1378
2008
  return findings;
1379
2009
  }
2010
+ function lintReprints(declared, stored, steps) {
2011
+ return reprintHits(declared, stored, steps).map((hit) => finding(hit.severity, hit.code, hit.place, hit.reason));
2012
+ }
1380
2013
  function lintCatalog(source) {
1381
2014
  const findings = [];
1382
2015
  for (const object of source.objects) {
@@ -1392,15 +2025,15 @@ function lintCatalog(source) {
1392
2025
  return findings;
1393
2026
  }
1394
2027
  function lintMigrationDirectory(directory, pending) {
1395
- if (!existsSync2(directory))
2028
+ if (!existsSync3(directory))
1396
2029
  return [];
1397
2030
  const names = readdirSync2(directory).filter((file) => file.endsWith(".sql")).sort();
1398
2031
  let before = catalog([]);
1399
2032
  const findings = [];
1400
2033
  for (const file of names) {
1401
2034
  const id = file.slice(0, -".sql".length);
1402
- const plan = readMigrationPlan(join2(directory, file));
1403
- const after = parseCatalog2(readFileSync2(join2(directory, `${id}.catalog.json`), "utf8"));
2035
+ const plan = readMigrationPlan(join3(directory, file));
2036
+ const after = parseCatalog2(readFileSync3(join3(directory, `${id}.catalog.json`), "utf8"));
1404
2037
  if (pending === undefined || pending.has(id)) {
1405
2038
  for (const item of lintPlan(plan, before, after)) {
1406
2039
  findings.push({ ...item, place: `${id} ${item.place}` });
@@ -1450,6 +2083,8 @@ function lintStep(step, number, before, after, existingTables, earlier, later) {
1450
2083
  triggered.add(hit.code);
1451
2084
  }
1452
2085
  }
2086
+ if (statementMayReprint(statement.sql))
2087
+ triggered.add(REPRINT_CODE);
1453
2088
  const place = multi ? `step ${String(number)} statement ${String(index + 1)}` : `step ${String(number)}`;
1454
2089
  perStatement.push({ place, hits });
1455
2090
  });
@@ -1519,8 +2154,16 @@ function columnPlace(column) {
1519
2154
  }
1520
2155
 
1521
2156
  // src/tooling/migrate/project.ts
1522
- import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync, writeFileSync } from "node:fs";
1523
- import { basename, dirname, join as join3 } from "node:path";
2157
+ import {
2158
+ existsSync as existsSync4,
2159
+ mkdirSync,
2160
+ readdirSync as readdirSync3,
2161
+ readFileSync as readFileSync4,
2162
+ statSync,
2163
+ unlinkSync,
2164
+ writeFileSync as writeFileSync2
2165
+ } from "node:fs";
2166
+ import { basename, dirname, join as join4 } from "node:path";
1524
2167
  import { pathToFileURL } from "node:url";
1525
2168
 
1526
2169
  // src/dialects/pg/declarations.ts
@@ -2131,6 +2774,131 @@ function columnNames(target, built, table) {
2131
2774
  return built.flatMap((object) => object.kind === "column" && object.identity.parent.name === table ? [object.identity.name] : []);
2132
2775
  }
2133
2776
 
2777
+ // src/tooling/migrate/place.ts
2778
+ function placeCatalog(source, namespace, externalNamespaces) {
2779
+ if (namespace === "public" && externalNamespaces.size === 0)
2780
+ return source;
2781
+ const homes = tableHomes(source, namespace, externalNamespaces);
2782
+ let changed = false;
2783
+ const objects = source.objects.map((object) => {
2784
+ const next = relocateObject(object, homes, namespace);
2785
+ if (next !== object)
2786
+ changed = true;
2787
+ return next;
2788
+ });
2789
+ if (!changed)
2790
+ return source;
2791
+ return catalog(objects);
2792
+ }
2793
+ function tableHomes(source, namespace, externalNamespaces) {
2794
+ const homes = new Map;
2795
+ for (const object of source.objects) {
2796
+ if (object.kind !== "table" && object.kind !== "view" && object.kind !== "materializedView") {
2797
+ continue;
2798
+ }
2799
+ const home = object.owner === "external" ? externalNamespaces.get(object.identity.name) ?? namespace : namespace;
2800
+ homes.set(object.identity.name, home);
2801
+ }
2802
+ return homes;
2803
+ }
2804
+ function relocateObject(object, homes, namespace) {
2805
+ const dependencies = relocateEdges(object.dependencies, homes, namespace);
2806
+ if (object.kind === "extension") {
2807
+ if (object.definition.schema === "public" && namespace !== "public") {
2808
+ return { ...object, definition: { ...object.definition, schema: namespace }, dependencies };
2809
+ }
2810
+ return dependencies === object.dependencies ? object : { ...object, dependencies };
2811
+ }
2812
+ if (object.kind === "role") {
2813
+ return dependencies === object.dependencies ? object : { ...object, dependencies };
2814
+ }
2815
+ const identity = relocateIdentity(object.identity, homes, namespace);
2816
+ if (object.kind === "constraint") {
2817
+ const references = relocateReferences(object, homes);
2818
+ if (identity === object.identity && references === object.definition.references && dependencies === object.dependencies) {
2819
+ return object;
2820
+ }
2821
+ if (references === undefined || references === object.definition.references) {
2822
+ return { ...object, identity, dependencies };
2823
+ }
2824
+ return {
2825
+ ...object,
2826
+ identity,
2827
+ definition: { ...object.definition, references },
2828
+ dependencies
2829
+ };
2830
+ }
2831
+ if (identity === object.identity && dependencies === object.dependencies)
2832
+ return object;
2833
+ return { ...object, identity, dependencies };
2834
+ }
2835
+ function relocateReferences(object, homes) {
2836
+ const references = object.definition.references;
2837
+ if (references === undefined)
2838
+ return;
2839
+ const home = homes.get(references.parent.name);
2840
+ if (home === undefined)
2841
+ return references;
2842
+ const parent = moveRef(references.parent, home);
2843
+ if (parent === references.parent)
2844
+ return references;
2845
+ return { ...references, parent };
2846
+ }
2847
+ function relocateEdges(edges, homes, namespace) {
2848
+ let changed = false;
2849
+ const next = edges.map((edge) => {
2850
+ const target = relocateIdentity(edge.target, homes, namespace);
2851
+ if (target === edge.target)
2852
+ return edge;
2853
+ changed = true;
2854
+ return { target };
2855
+ });
2856
+ return changed ? next : edges;
2857
+ }
2858
+ function relocateIdentity(identity, homes, namespace) {
2859
+ if (identity.kind === "role" || identity.kind === "extension")
2860
+ return identity;
2861
+ if (identity.kind === "column" || identity.kind === "index" || identity.kind === "constraint" || identity.kind === "trigger" || identity.kind === "policy") {
2862
+ const home = homes.get(identity.parent.name) ?? namespace;
2863
+ const parent = moveRef(identity.parent, home);
2864
+ if (parent === identity.parent)
2865
+ return identity;
2866
+ return { ...identity, parent };
2867
+ }
2868
+ if (identity.kind === "grant") {
2869
+ const home = homes.get(identity.object.name) ?? namespace;
2870
+ const moved = moveNamespace(identity.object.namespace, home);
2871
+ if (moved === identity.object.namespace)
2872
+ return identity;
2873
+ return { ...identity, object: { ...identity.object, namespace: moved } };
2874
+ }
2875
+ if (identity.kind === "defaultPrivilege") {
2876
+ const moved = moveNamespace(identity.namespace, namespace);
2877
+ if (moved === identity.namespace)
2878
+ return identity;
2879
+ return { ...identity, namespace: moved };
2880
+ }
2881
+ if (identity.kind === "table" || identity.kind === "view" || identity.kind === "materializedView" || identity.kind === "sequence" || identity.kind === "type" || identity.kind === "function") {
2882
+ const home = homes.get(identity.name) ?? namespace;
2883
+ const moved = moveNamespace(identity.namespace, home);
2884
+ if (moved === identity.namespace)
2885
+ return identity;
2886
+ return { ...identity, namespace: moved };
2887
+ }
2888
+ return identity;
2889
+ }
2890
+ function moveRef(ref, namespace) {
2891
+ const moved = moveNamespace(ref.namespace, namespace);
2892
+ if (moved === ref.namespace)
2893
+ return ref;
2894
+ return { ...ref, namespace: moved };
2895
+ }
2896
+ function moveNamespace(namespace, name) {
2897
+ if (namespace.form !== "static" || namespace.name === name)
2898
+ return namespace;
2899
+ return staticNamespace2(name);
2900
+ }
2901
+
2134
2902
  // src/tooling/migrate/estimate.ts
2135
2903
  var LARGE_TABLE_ROWS = 1e6;
2136
2904
  var ESTIMATE_SQL = `
@@ -2324,11 +3092,35 @@ function trimZero(text) {
2324
3092
  return text.endsWith(".0") ? text.slice(0, -2) : text;
2325
3093
  }
2326
3094
 
3095
+ // src/tooling/migrate/plan-json.ts
3096
+ function planDocument(plan, findings, lockText) {
3097
+ return {
3098
+ name: plan.name,
3099
+ class: plan.class,
3100
+ steps: plan.steps.map((step) => ({
3101
+ class: step.class,
3102
+ lock: lockText === undefined ? step.lock : lockText(step),
3103
+ sql: step.sql
3104
+ })),
3105
+ findings: findings.map((finding) => ({
3106
+ severity: finding.severity,
3107
+ code: finding.code,
3108
+ place: finding.place,
3109
+ reason: finding.reason,
3110
+ fix: finding.fix
3111
+ }))
3112
+ };
3113
+ }
3114
+ function formatPlanJson(plan, findings, lockText) {
3115
+ return `${JSON.stringify(planDocument(plan, findings, lockText))}
3116
+ `;
3117
+ }
3118
+
2327
3119
  // src/tooling/migrate/project.ts
2328
3120
  async function buildProject(cwd) {
2329
3121
  const config = await loadConfig(cwd);
2330
3122
  const built = await loadSchema(cwd, config.schema);
2331
- return writeArtifact(cwd, config, withRoles(built, config));
3123
+ return writeArtifact(cwd, config, withRoles(built, config, cwd));
2332
3124
  }
2333
3125
  async function generateProject(cwd, name, flags) {
2334
3126
  const prepared = await prepare(cwd, name, flags);
@@ -2340,17 +3132,20 @@ async function generateProject(cwd, name, flags) {
2340
3132
  const directory = projectPath(cwd, prepared.config.migrations ?? "migrations");
2341
3133
  mkdirSync(directory, { recursive: true });
2342
3134
  const base = `${nextNumber(directory)}_${safeName(name)}`;
2343
- const sqlPath = join3(directory, `${base}.sql`);
2344
- writeFileSync(sqlPath, formatPlan(prepared.plan));
2345
- writeFileSync(join3(directory, `${base}.catalog.json`), serializeCatalog2(prepared.built.catalog));
3135
+ const sqlPath = join4(directory, `${base}.sql`);
3136
+ writeFileSync2(sqlPath, formatPlan(prepared.plan));
3137
+ writeFileSync2(join4(directory, `${base}.catalog.json`), serializeCatalog2(prepared.built.catalog));
2346
3138
  return { path: sqlPath, findings: lintPrepared(prepared) };
2347
3139
  }
2348
3140
  async function planProject(cwd, name, flags, invoke) {
2349
3141
  const prepared = await prepare(cwd, name, flags);
3142
+ const findings = lintPrepared(prepared);
3143
+ const lockText = await planLockText(prepared.config, prepared.plan, invoke);
2350
3144
  return {
2351
3145
  plan: prepared.plan,
2352
- findings: lintPrepared(prepared),
2353
- text: await displayPlan(prepared.config, prepared.plan, invoke)
3146
+ findings,
3147
+ text: formatPlan(prepared.plan, lockText),
3148
+ json: formatPlanJson(prepared.plan, findings, lockText)
2354
3149
  };
2355
3150
  }
2356
3151
  async function checkProject(cwd, flags) {
@@ -2366,8 +3161,8 @@ async function checkProject(cwd, flags) {
2366
3161
  assertTargetAlias(targets);
2367
3162
  if (opened.config.tables !== undefined) {
2368
3163
  const directory = projectPath(cwd, opened.config.tables);
2369
- const files = existsSync3(directory) ? readdirSync3(directory) : [];
2370
- const source = readFileSync3(projectPath(cwd, opened.config.schema), "utf8");
3164
+ const files = existsSync4(directory) ? readdirSync3(directory) : [];
3165
+ const source = readFileSync4(projectPath(cwd, opened.config.schema), "utf8");
2371
3166
  const missing = unlistedTableFiles(source, files);
2372
3167
  if (missing.length > 0) {
2373
3168
  throw new OkmError2("OKM1024", `Table file not in schema: ${missing.join(", ")}.`, {
@@ -2375,7 +3170,8 @@ async function checkProject(cwd, flags) {
2375
3170
  });
2376
3171
  }
2377
3172
  }
2378
- const findings = lintOpened(opened);
3173
+ assertSnapshots(cwd, opened.config, opened.built.catalog);
3174
+ const findings = [...lintOpened(opened), ...couplingFindings(opened.built.catalog)];
2379
3175
  if (hasError(findings))
2380
3176
  throw lintRefusal(findings);
2381
3177
  const named = flags?.target;
@@ -2386,12 +3182,14 @@ async function checkProject(cwd, flags) {
2386
3182
  const target = selectTarget(opened.config, named);
2387
3183
  assertTargetPolicy(target, "check", flags?.allowProtected ?? false);
2388
3184
  assertTargetPolicy(target, "drift", flags?.allowProtected ?? false);
2389
- const { assertAuthorDrift } = await import("./jvd0mqag.js");
3185
+ const { assertAuthorDrift } = await import("./ckz05z15.js");
2390
3186
  const allowPooler = flags?.allowPooler === true || opened.config.allowPooler === true;
3187
+ await assertLiveExternal(target.url, opened.built.catalog, loadSnapshots(cwd, opened.config));
2391
3188
  await assertAuthorDrift(target.url, opened.built.catalog, opened.config.roles, allowPooler);
2392
3189
  return findings;
2393
3190
  }
2394
3191
  function unlistedTableFiles(schemaSource, fileNames) {
3192
+ const folded = schemaSource.toLowerCase();
2395
3193
  const missing = [];
2396
3194
  for (const file of fileNames) {
2397
3195
  if (!file.endsWith(".ts"))
@@ -2399,7 +3197,7 @@ function unlistedTableFiles(schemaSource, fileNames) {
2399
3197
  const stem = basename(file, ".ts");
2400
3198
  if (stem.length === 0 || stem.startsWith("."))
2401
3199
  continue;
2402
- if (!schemaSource.includes(stem))
3200
+ if (!folded.includes(stem.toLowerCase()))
2403
3201
  missing.push(file);
2404
3202
  }
2405
3203
  return missing;
@@ -2416,6 +3214,7 @@ async function prepare(cwd, name, flags) {
2416
3214
  renames: opened.renames,
2417
3215
  replacements: flags.map((flag) => parseReplace(flag)),
2418
3216
  name,
3217
+ ...placedSchema(opened.config),
2419
3218
  ...batchSizeField(opened.config.backfill?.batchSize)
2420
3219
  });
2421
3220
  return {
@@ -2429,26 +3228,25 @@ async function prepare(cwd, name, flags) {
2429
3228
  function lintPrepared(prepared) {
2430
3229
  return lintPlan(prepared.plan, prepared.before, prepared.after);
2431
3230
  }
2432
- async function displayPlan(config, plan, invoke) {
2433
- const offline = formatPlan(plan);
3231
+ async function planLockText(config, plan, invoke) {
2434
3232
  const targets = listTargets(config);
2435
3233
  if (targets.length === 0)
2436
- return offline;
3234
+ return;
2437
3235
  assertTargetAlias(targets);
2438
3236
  const named = invoke?.target;
2439
3237
  if (named === undefined && targets.length !== 1)
2440
- return offline;
3238
+ return;
2441
3239
  const target = selectTarget(config, named);
2442
3240
  assertTargetPolicy(target, "plan", invoke?.allowProtected ?? false);
2443
3241
  let estimates;
2444
3242
  try {
2445
3243
  const names = plan.steps.flatMap((step) => step.tables ?? []);
2446
- estimates = await readRowEstimates(target.url, "public", names);
3244
+ estimates = await readRowEstimates(target.url, catalogNamespace(config), names);
2447
3245
  } catch {
2448
- return offline;
3246
+ return;
2449
3247
  }
2450
3248
  const aliases = renameAliases(plan.steps);
2451
- return formatPlan(plan, (step) => annotateLock(step, estimates, aliases));
3249
+ return (step) => annotateLock(step, estimates, aliases);
2452
3250
  }
2453
3251
  function lintOpened(opened) {
2454
3252
  const plan = planMigration({
@@ -2456,6 +3254,7 @@ function lintOpened(opened) {
2456
3254
  after: opened.built.catalog,
2457
3255
  renames: opened.renames,
2458
3256
  name: "check",
3257
+ ...placedSchema(opened.config),
2459
3258
  ...batchSizeField(opened.config.backfill?.batchSize)
2460
3259
  });
2461
3260
  return [
@@ -2465,7 +3264,7 @@ function lintOpened(opened) {
2465
3264
  }
2466
3265
  async function openProject(cwd) {
2467
3266
  const config = await loadConfig(cwd);
2468
- const built = withRoles(await loadSchema(cwd, config.schema), config);
3267
+ const built = await loadBuiltSchema(cwd, config);
2469
3268
  const declarations = schemaDeclarations(built);
2470
3269
  const previous = readPrevious(projectPath(cwd, config.migrations ?? "migrations"));
2471
3270
  const stale = staleRenames(previous?.catalog, declarations.renames);
@@ -2481,15 +3280,22 @@ async function openProject(cwd) {
2481
3280
  renames: declarations.renames
2482
3281
  };
2483
3282
  }
2484
- function withRoles(built, config) {
3283
+ function withRoles(built, config, cwd) {
2485
3284
  const marks = ownershipMarks(built);
2486
3285
  const stamped = marks.size === 0 ? built.catalog : stampOwnership(built.catalog, marks);
3286
+ const placed = placeCatalog(stamped, catalogNamespace(config), externalNamespaceMap(loadSnapshots(cwd, config)));
2487
3287
  const roles = config.roles;
2488
- const catalog = roles === undefined ? stamped : attachRoles(stamped, roles, exposureFor(built, roles));
3288
+ const catalog = roles === undefined ? placed : attachRoles(placed, roles, exposureFor(built, roles));
2489
3289
  if (catalog === built.catalog)
2490
3290
  return built;
2491
3291
  return { ...built, catalog };
2492
3292
  }
3293
+ function placedSchema(config) {
3294
+ const namespace = catalogNamespace(config);
3295
+ if (namespace === "public")
3296
+ return;
3297
+ return { schema: namespace };
3298
+ }
2493
3299
  function ownershipMarks(built) {
2494
3300
  const marks = new Map;
2495
3301
  const models = built.model;
@@ -2526,9 +3332,9 @@ var GIT_ATTRIBUTES = [
2526
3332
  `);
2527
3333
  function writeGitAttributes(cwd) {
2528
3334
  const path = projectPath(cwd, ".gitattributes");
2529
- if (existsSync3(path))
3335
+ if (existsSync4(path))
2530
3336
  return;
2531
- writeFileSync(path, GIT_ATTRIBUTES);
3337
+ writeFileSync2(path, GIT_ATTRIBUTES);
2532
3338
  }
2533
3339
  function typesText(cwd, spec, built) {
2534
3340
  return `${emitRowTypes(built, jsonColumnTypes(readSchemaSource(cwd, spec)))}${referenceAugmentation(built)}`;
@@ -2537,7 +3343,7 @@ function readSchemaSource(cwd, spec) {
2537
3343
  const parts = [];
2538
3344
  const seen = new Set;
2539
3345
  const visit = (file) => {
2540
- if (seen.has(file) || !existsSync3(file))
3346
+ if (seen.has(file) || !existsSync4(file))
2541
3347
  return;
2542
3348
  let stat;
2543
3349
  try {
@@ -2548,16 +3354,16 @@ function readSchemaSource(cwd, spec) {
2548
3354
  if (!stat.isFile())
2549
3355
  return;
2550
3356
  seen.add(file);
2551
- const text = readFileSync3(file, "utf8");
3357
+ const text = readFileSync4(file, "utf8");
2552
3358
  parts.push(text);
2553
3359
  for (const match of text.matchAll(/from\s+["'](\.[^"']+)["']/g)) {
2554
3360
  const rel = match[1];
2555
3361
  if (rel === undefined)
2556
3362
  continue;
2557
- const base = join3(dirname(file), rel);
2558
- const candidates = [base, `${base}.ts`, `${base}.tsx`, `${base}.mts`, join3(base, "index.ts")];
3363
+ const base = join4(dirname(file), rel);
3364
+ const candidates = [base, `${base}.ts`, `${base}.tsx`, `${base}.mts`, join4(base, "index.ts")];
2559
3365
  for (const candidate of candidates) {
2560
- if (existsSync3(candidate)) {
3366
+ if (existsSync4(candidate)) {
2561
3367
  visit(candidate);
2562
3368
  break;
2563
3369
  }
@@ -2573,19 +3379,35 @@ function writeArtifact(cwd, config, built) {
2573
3379
  mkdirSync(directory, { recursive: true });
2574
3380
  const startup = startupCatalog2(built.catalog);
2575
3381
  const text = catalogFileText2(serializeCatalog2(startup));
2576
- writeFileSync(join3(directory, "catalog.json"), text);
2577
- writeFileSync(join3(directory, "catalog.hash"), `${sha2562(text)}
3382
+ writeFileSync2(join4(directory, "catalog.json"), text);
3383
+ writeFileSync2(join4(directory, "catalog.hash"), `${sha2562(text)}
2578
3384
  `);
2579
- writeFileSync(join3(directory, "types.d.ts"), typesText(cwd, config.schema, built));
3385
+ writeFileSync2(join4(directory, "types.d.ts"), typesText(cwd, config.schema, built));
3386
+ writeNamespace(directory, catalogNamespace(config));
2580
3387
  return directory;
2581
3388
  }
3389
+ function writeNamespace(directory, namespace) {
3390
+ const path = join4(directory, "namespace");
3391
+ if (namespace === "public") {
3392
+ if (existsSync4(path))
3393
+ unlinkSync(path);
3394
+ return;
3395
+ }
3396
+ writeFileSync2(path, `${namespace}
3397
+ `);
3398
+ }
2582
3399
  async function loadBuiltSchema(cwd, config) {
2583
- return withRoles(await loadSchema(cwd, config.schema), config);
3400
+ return withRoles(await loadSchema(cwd, config.schema), config, cwd);
3401
+ }
3402
+ async function exportProject(cwd, args) {
3403
+ const config = await loadConfig(cwd);
3404
+ const built = await loadBuiltSchema(cwd, config);
3405
+ return renderExport(cwd, config, built.catalog, args);
2584
3406
  }
2585
3407
  async function loadConfig(cwd) {
2586
3408
  const path = projectPath(cwd, "okmodel.config.ts");
2587
3409
  const imported = await import(`${pathToFileURL(path).href}?okm=${Date.now()}`);
2588
- const record = isRecord(imported) ? imported : {};
3410
+ const record = isRecord2(imported) ? imported : {};
2589
3411
  const config = record.default ?? record.config;
2590
3412
  if (!isConfig(config)) {
2591
3413
  throw new OkmError2("invalid", "okmodel.config.ts must default-export defineConfig(...).");
@@ -2595,7 +3417,7 @@ async function loadConfig(cwd) {
2595
3417
  async function loadSchema(cwd, spec) {
2596
3418
  const path = projectPath(cwd, spec);
2597
3419
  const imported = await import(`${pathToFileURL(path).href}?okm=${Date.now()}`);
2598
- const record = isRecord(imported) ? imported : {};
3420
+ const record = isRecord2(imported) ? imported : {};
2599
3421
  for (const value of Object.values(record)) {
2600
3422
  if (!isBuilt(value))
2601
3423
  continue;
@@ -2605,13 +3427,13 @@ async function loadSchema(cwd, spec) {
2605
3427
  throw new OkmError2("invalid", `${spec} must export a schema().`);
2606
3428
  }
2607
3429
  function readPrevious(directory) {
2608
- if (!existsSync3(directory))
3430
+ if (!existsSync4(directory))
2609
3431
  return;
2610
3432
  const catalogs = readdirSync3(directory).filter((file) => file.endsWith(".catalog.json")).sort();
2611
3433
  const latest = catalogs.at(-1);
2612
3434
  if (latest === undefined)
2613
3435
  return;
2614
- const text = readFileSync3(join3(directory, latest), "utf8");
3436
+ const text = readFileSync4(join4(directory, latest), "utf8");
2615
3437
  return { catalog: parseCatalog2(text) };
2616
3438
  }
2617
3439
  function referenceAugmentation(source) {
@@ -2636,7 +3458,7 @@ function referenceAugmentation(source) {
2636
3458
  }
2637
3459
  function nextNumber(directory) {
2638
3460
  let max = 0;
2639
- if (existsSync3(directory)) {
3461
+ if (existsSync4(directory)) {
2640
3462
  for (const file of readdirSync3(directory)) {
2641
3463
  const match = /^(\d+)_/.exec(file);
2642
3464
  const value = match?.[1];
@@ -2653,14 +3475,14 @@ function safeName(name) {
2653
3475
  return name;
2654
3476
  }
2655
3477
  function isBuilt(value) {
2656
- if (!isRecord(value) || !Array.isArray(value.tables))
3478
+ if (!isRecord2(value) || !Array.isArray(value.tables))
2657
3479
  return false;
2658
3480
  return "catalog" in value;
2659
3481
  }
2660
3482
  function isConfig(value) {
2661
- return isRecord(value) && typeof value.schema === "string";
3483
+ return isRecord2(value) && typeof value.schema === "string";
2662
3484
  }
2663
- function isRecord(value) {
3485
+ function isRecord2(value) {
2664
3486
  return typeof value === "object" && value !== null;
2665
3487
  }
2666
3488
  var SKIP_SCAN = new Set(["node_modules", "dist", "coverage"]);
@@ -2685,7 +3507,7 @@ function directoryImportsValidate(dir) {
2685
3507
  for (const entry of entries) {
2686
3508
  if (entry.name.startsWith(".") || SKIP_SCAN.has(entry.name))
2687
3509
  continue;
2688
- const path = join3(dir, entry.name);
3510
+ const path = join4(dir, entry.name);
2689
3511
  if (entry.isDirectory()) {
2690
3512
  if (directoryImportsValidate(path))
2691
3513
  return true;
@@ -2693,7 +3515,7 @@ function directoryImportsValidate(dir) {
2693
3515
  }
2694
3516
  if (!/\.(?:ts|tsx|mts|cts)$/.test(entry.name))
2695
3517
  continue;
2696
- if (runtimeImportsValidate(readFileSync3(path, "utf8")))
3518
+ if (runtimeImportsValidate(readFileSync4(path, "utf8")))
2697
3519
  return true;
2698
3520
  }
2699
3521
  return false;
@@ -2715,4 +3537,4 @@ function runtimeImportsValidate(source) {
2715
3537
  return false;
2716
3538
  }
2717
3539
 
2718
- export { projectPath, errorDoc, loadMigrations, readMigrationPlan, loadHeadSnapshot, lintMigrationDirectory, formatFindings, hasError, lintRefusal, buildProject, generateProject, planProject, checkProject, projectHead, openProject, loadBuiltSchema, loadConfig, loadSchema };
3540
+ 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 };