@starci/hfs 4.11.22 → 4.12.2

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 (1088) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +31 -31
  3. package/ci/cli.d.mts +15 -0
  4. package/ci/cli.mjs +71 -52
  5. package/ci/github.d.mts +43 -0
  6. package/ci/github.mjs +33 -28
  7. package/ci/local.d.mts +12 -0
  8. package/ci/local.mjs +145 -108
  9. package/ci/secret.d.mts +15 -0
  10. package/ci/secret.mjs +31 -21
  11. package/ci/vars.d.mts +17 -0
  12. package/ci/vars.mjs +47 -33
  13. package/ci/verify.d.mts +18 -0
  14. package/ci/verify.mjs +52 -40
  15. package/codemod/button-intent.d.mts +63 -0
  16. package/codemod/button-intent.mjs +338 -285
  17. package/coverage/cli.d.mts +28 -0
  18. package/coverage/cli.mjs +65 -51
  19. package/db/backup-receipt.d.mts +28 -0
  20. package/db/backup-receipt.mjs +78 -58
  21. package/db/drift.d.mts +86 -0
  22. package/db/drift.mjs +213 -180
  23. package/db/dump.d.mts +52 -0
  24. package/db/dump.mjs +63 -56
  25. package/db/entity-list.d.mts +4 -0
  26. package/db/entity-list.mjs +0 -2
  27. package/db/image.d.mts +8 -0
  28. package/db/image.mjs +1 -2
  29. package/db/migrate.d.mts +104 -0
  30. package/db/migrate.mjs +207 -167
  31. package/db/new.d.mts +11 -0
  32. package/db/new.mjs +25 -25
  33. package/db/post-cutover.d.mts +5 -0
  34. package/db/post-cutover.mjs +89 -82
  35. package/db/postgres.d.mts +71 -0
  36. package/db/postgres.mjs +62 -61
  37. package/emit/compiler.d.mts +3 -0
  38. package/emit/compiler.mjs +25 -27
  39. package/emit/contracts.d.mts +35 -0
  40. package/emit/contracts.mjs +46 -50
  41. package/emit/events.d.mts +9 -0
  42. package/emit/events.mjs +19 -20
  43. package/emit/form-bounds.d.mts +83 -0
  44. package/emit/form-bounds.mjs +133 -117
  45. package/emit/graphql-input-bounds.d.mts +63 -0
  46. package/emit/graphql-input-bounds.mjs +90 -79
  47. package/emit/input-bounds.d.mts +30 -0
  48. package/emit/input-bounds.mjs +29 -27
  49. package/emit/operation-schemas.d.mts +24 -0
  50. package/emit/operation-schemas.mjs +136 -116
  51. package/emit/operations-worker.d.mts +1 -0
  52. package/emit/operations-worker.mjs +9 -7
  53. package/emit/operations.d.mts +47 -0
  54. package/emit/operations.mjs +124 -133
  55. package/emit/schema-worker.d.mts +1 -0
  56. package/emit/schema-worker.mjs +57 -50
  57. package/emit/static-graph.d.mts +48 -0
  58. package/emit/static-graph.mjs +766 -640
  59. package/emit/type-schema.d.mts +29 -0
  60. package/emit/type-schema.mjs +153 -140
  61. package/lint/bound-sys.cjs +27 -33
  62. package/lint/chunk.d.mts +17 -0
  63. package/lint/chunk.mjs +41 -31
  64. package/lint/run.d.mts +120 -0
  65. package/lint/run.mjs +414 -325
  66. package/package.json +21 -4
  67. package/release/candidate-ci.d.mts +46 -0
  68. package/release/candidate-ci.mjs +76 -70
  69. package/release/gate.d.mts +120 -0
  70. package/release/gate.mjs +358 -330
  71. package/release/index.d.mts +68 -0
  72. package/release/index.mjs +95 -73
  73. package/release/lint-report.d.mts +14 -0
  74. package/release/lint-report.mjs +33 -32
  75. package/release/push.d.mts +32 -0
  76. package/release/push.mjs +92 -79
  77. package/release/records.d.mts +18 -0
  78. package/release/records.mjs +22 -21
  79. package/release/sonar-quality-gate.d.mts +18 -0
  80. package/release/sonar-quality-gate.mjs +60 -52
  81. package/release/urgent.d.mts +20 -0
  82. package/release/urgent.mjs +76 -74
  83. package/report/order.d.mts +1 -0
  84. package/report/sonar.d.mts +73 -0
  85. package/report/sonar.mjs +82 -89
  86. package/runtime/engine/admission.d.mts +209 -0
  87. package/runtime/engine/admission.mjs +225 -204
  88. package/runtime/engine/by-code-unit.d.mts +5 -0
  89. package/runtime/engine/by-code-unit.mjs +6 -6
  90. package/runtime/engine/config.d.mts +333 -0
  91. package/runtime/engine/config.mjs +486 -328
  92. package/runtime/engine/db/blob.d.mts +83 -0
  93. package/runtime/engine/db/blob.mjs +344 -268
  94. package/runtime/engine/db/event-compact.d.mts +21 -0
  95. package/runtime/engine/db/event-compact.mjs +34 -38
  96. package/runtime/engine/db/machine-connection.d.mts +68 -0
  97. package/runtime/engine/db/machine-connection.mjs +166 -119
  98. package/runtime/engine/db/machine-open.d.mts +26 -0
  99. package/runtime/engine/db/machine-open.mjs +59 -45
  100. package/runtime/engine/db/machine-schema.d.mts +34 -0
  101. package/runtime/engine/db/machine-schema.mjs +102 -83
  102. package/runtime/engine/db/machine.d.mts +1746 -0
  103. package/runtime/engine/db/machine.mjs +1040 -734
  104. package/runtime/engine/db/provider-reservations.d.mts +169 -0
  105. package/runtime/engine/db/provider-reservations.mjs +141 -119
  106. package/runtime/engine/db/provision-lock.d.mts +23 -0
  107. package/runtime/engine/db/provision-lock.mjs +14 -14
  108. package/runtime/engine/db/ref-value.d.mts +19 -0
  109. package/runtime/engine/db/ref-value.mjs +36 -35
  110. package/runtime/engine/db/sqlite-types.d.mts +28 -0
  111. package/runtime/engine/db/sqlite-types.mjs +4 -0
  112. package/runtime/engine/digest.d.mts +4 -0
  113. package/runtime/engine/digest.mjs +16 -7
  114. package/runtime/engine/invalid-config.d.mts +13 -0
  115. package/runtime/engine/invalid-config.mjs +17 -11
  116. package/runtime/engine/model-config.d.mts +91 -0
  117. package/runtime/engine/model-config.mjs +98 -76
  118. package/runtime/engine/orca-config.d.mts +26 -0
  119. package/runtime/engine/orca-config.mjs +22 -17
  120. package/runtime/engine/plain-object.d.mts +2 -0
  121. package/runtime/engine/refuse.d.mts +9 -0
  122. package/runtime/engine/refuse.mjs +3 -6
  123. package/runtime/engine/release-config.d.mts +19 -0
  124. package/runtime/engine/release-config.mjs +22 -18
  125. package/runtime/engine/removed-vocabulary.d.mts +12 -0
  126. package/runtime/engine/removed-vocabulary.mjs +21 -20
  127. package/runtime/engine/resources-config.d.mts +34 -0
  128. package/runtime/engine/resources-config.mjs +42 -35
  129. package/runtime/engine/runtime-root.d.mts +28 -0
  130. package/runtime/engine/runtime-root.mjs +11 -13
  131. package/runtime/engine/secrets.d.mts +41 -0
  132. package/runtime/engine/secrets.mjs +170 -102
  133. package/runtime/engine/sonar-config.d.mts +10 -0
  134. package/runtime/engine/sonar-config.mjs +16 -13
  135. package/runtime/engine/temp-root.d.mts +17 -0
  136. package/runtime/engine/temp-root.mjs +3 -9
  137. package/runtime/engine/yaml.d.mts +9 -0
  138. package/runtime/knowledge/hfs/canon-pins.yaml +10 -10
  139. package/runtime/knowledge/hfs/ci-standard.yaml +2 -2
  140. package/runtime/knowledge/hfs/rules.yaml +95 -6
  141. package/runtime/knowledge/hfs/slots.yaml +3 -3
  142. package/runtime/knowledge/sonar-gate.yaml +1 -1
  143. package/runtime/modules/kernel/failure-codes.yaml +43 -32
  144. package/runtime/modules/schemas/application-stacks.schema.yaml +3 -3
  145. package/runtime/packages/cli/src/catalog.generated.d.mts +13 -0
  146. package/runtime/packages/cli/src/catalog.generated.mjs +1853 -14824
  147. package/runtime/packages/cli/src/validate-args.d.mts +74 -0
  148. package/runtime/packages/cli/src/validate-args.mjs +183 -171
  149. package/runtime/packages/jest-preset/sonar-exclusions.cjs +5 -5
  150. package/runtime/scripts/api/fs/claim-file.d.mts +12 -0
  151. package/runtime/scripts/api/fs/claim-file.mjs +31 -17
  152. package/runtime/scripts/api/fs/ensure-temp-root.d.mts +4 -0
  153. package/runtime/scripts/api/fs/ensure-temp-root.mjs +15 -14
  154. package/runtime/scripts/api/fs/forbidden-root.d.mts +15 -0
  155. package/runtime/scripts/api/fs/forbidden-root.mjs +31 -23
  156. package/runtime/scripts/api/fs/is-link-like.d.mts +10 -0
  157. package/runtime/scripts/api/fs/is-link-like.mjs +25 -11
  158. package/runtime/scripts/api/fs/lib.d.mts +12 -0
  159. package/runtime/scripts/api/fs/lib.mjs +28 -8
  160. package/runtime/scripts/api/fs/make-temp-dir.d.mts +5 -0
  161. package/runtime/scripts/api/fs/make-temp-dir.mjs +3 -4
  162. package/runtime/scripts/api/fs/rename-over.d.mts +13 -0
  163. package/runtime/scripts/api/fs/rename-over.mjs +16 -10
  164. package/runtime/scripts/api/fs/safe-remove.d.mts +25 -0
  165. package/runtime/scripts/api/fs/safe-remove.mjs +129 -71
  166. package/runtime/scripts/api/git/add.d.mts +3 -0
  167. package/runtime/scripts/api/git/add.mjs +0 -1
  168. package/runtime/scripts/api/git/cat-file.d.mts +3 -0
  169. package/runtime/scripts/api/git/cat-file.mjs +0 -1
  170. package/runtime/scripts/api/git/check-ignore.d.mts +3 -0
  171. package/runtime/scripts/api/git/check-ignore.mjs +0 -1
  172. package/runtime/scripts/api/git/commit-tree.d.mts +3 -0
  173. package/runtime/scripts/api/git/commit-tree.mjs +0 -1
  174. package/runtime/scripts/api/git/config-get.d.mts +10 -0
  175. package/runtime/scripts/api/git/config-get.mjs +1 -2
  176. package/runtime/scripts/api/git/diff.d.mts +3 -0
  177. package/runtime/scripts/api/git/diff.mjs +0 -1
  178. package/runtime/scripts/api/git/init.d.mts +7 -0
  179. package/runtime/scripts/api/git/init.mjs +1 -2
  180. package/runtime/scripts/api/git/is-ancestor.d.mts +5 -0
  181. package/runtime/scripts/api/git/is-ancestor.mjs +0 -1
  182. package/runtime/scripts/api/git/lib.d.mts +67 -0
  183. package/runtime/scripts/api/git/lib.mjs +32 -32
  184. package/runtime/scripts/api/git/ls-files.d.mts +3 -0
  185. package/runtime/scripts/api/git/ls-files.mjs +0 -1
  186. package/runtime/scripts/api/git/ls-tree.d.mts +3 -0
  187. package/runtime/scripts/api/git/ls-tree.mjs +0 -1
  188. package/runtime/scripts/api/git/merge-base.d.mts +5 -0
  189. package/runtime/scripts/api/git/merge-base.mjs +2 -3
  190. package/runtime/scripts/api/git/remote.d.mts +3 -0
  191. package/runtime/scripts/api/git/remote.mjs +0 -1
  192. package/runtime/scripts/api/git/rev-parse-query.d.mts +3 -0
  193. package/runtime/scripts/api/git/rev-parse-query.mjs +0 -1
  194. package/runtime/scripts/api/git/show.d.mts +3 -0
  195. package/runtime/scripts/api/git/show.mjs +0 -1
  196. package/runtime/scripts/api/git/tag.d.mts +3 -0
  197. package/runtime/scripts/api/git/tag.mjs +0 -1
  198. package/runtime/scripts/api/git/update-ref.d.mts +7 -0
  199. package/runtime/scripts/api/git/update-ref.mjs +1 -2
  200. package/runtime/scripts/api/git/write-tree.d.mts +3 -0
  201. package/runtime/scripts/api/git/write-tree.mjs +0 -1
  202. package/runtime/scripts/api/node/lib.d.mts +7 -0
  203. package/runtime/scripts/api/node/lib.mjs +17 -7
  204. package/runtime/scripts/api/node/run-node.d.mts +3 -0
  205. package/runtime/scripts/api/node/run-node.mjs +0 -3
  206. package/runtime/scripts/api/process/resolve-real-tool.d.mts +12 -0
  207. package/runtime/scripts/api/process/resolve-real-tool.mjs +46 -38
  208. package/runtime/scripts/api/process/run-program.d.mts +3 -0
  209. package/runtime/scripts/api/process/run-program.mjs +5 -2
  210. package/runtime/scripts/api/sops/decrypt.d.mts +9 -0
  211. package/runtime/scripts/api/sops/decrypt.mjs +1 -2
  212. package/runtime/scripts/api/sops/lib.d.mts +149 -0
  213. package/runtime/scripts/api/sops/lib.mjs +417 -318
  214. package/runtime/scripts/api/sops/resolve-sops.d.mts +3 -0
  215. package/runtime/scripts/api/sops/resolve-sops.mjs +1 -2
  216. package/runtime/scripts/api/sops/seal.d.mts +19 -0
  217. package/runtime/scripts/api/sops/seal.mjs +14 -10
  218. package/runtime/scripts/api/sops/selected-age-recipient.d.mts +3 -0
  219. package/runtime/scripts/api/sops/selected-age-recipient.mjs +1 -2
  220. package/runtime/scripts/api/sops/update-keys.d.mts +16 -0
  221. package/runtime/scripts/api/sops/update-keys.mjs +11 -8
  222. package/runtime/scripts/cli/gen-catalog.d.mts +42 -0
  223. package/runtime/scripts/cli/gen-catalog.mjs +72 -0
  224. package/runtime/scripts/gates/runtime-host.d.mts +23 -0
  225. package/runtime/scripts/gates/runtime-host.mjs +48 -41
  226. package/runtime/scripts/gates/sonar-gate.d.mts +219 -0
  227. package/runtime/scripts/gates/sonar-gate.mjs +166 -153
  228. package/runtime/scripts/gates/starcistacks-ci.d.mts +35 -0
  229. package/runtime/scripts/gates/starcistacks-ci.mjs +47 -39
  230. package/runtime/scripts/gates/starcistacks-form.d.mts +80 -0
  231. package/runtime/scripts/gates/starcistacks-form.mjs +16 -14
  232. package/runtime/scripts/gates/starcistacks-runtime.d.mts +1 -0
  233. package/runtime/scripts/gates/starcistacks-value-file.d.mts +2 -0
  234. package/runtime/scripts/gates/starcistacks-value-file.mjs +14 -11
  235. package/runtime/scripts/gates/starcistacks.d.mts +141 -0
  236. package/runtime/scripts/gates/starcistacks.mjs +470 -355
  237. package/runtime/scripts/guards/git-pathspec.d.mts +48 -0
  238. package/runtime/scripts/guards/git-pathspec.mjs +215 -130
  239. package/runtime/scripts/guards/git-policy.d.mts +30 -0
  240. package/runtime/scripts/guards/git-policy.mjs +278 -235
  241. package/runtime/scripts/guards/hook-io.d.mts +22 -0
  242. package/runtime/scripts/guards/hook-io.mjs +77 -63
  243. package/runtime/scripts/guards/push-scan.d.mts +31 -0
  244. package/runtime/scripts/guards/push-scan.mjs +93 -80
  245. package/runtime/scripts/guards/release-push-gate.d.mts +22 -0
  246. package/runtime/scripts/guards/release-push-gate.mjs +40 -38
  247. package/runtime/scripts/hfs/allows.d.mts +24 -0
  248. package/runtime/scripts/hfs/allows.mjs +37 -38
  249. package/runtime/scripts/hfs/app-wiring.d.mts +72 -0
  250. package/runtime/scripts/hfs/app-wiring.mjs +197 -167
  251. package/runtime/scripts/hfs/architecture/ast-walks.d.mts +89 -0
  252. package/runtime/scripts/hfs/architecture/ast-walks.mjs +180 -163
  253. package/runtime/scripts/hfs/architecture/automatic-gates.d.mts +21 -0
  254. package/runtime/scripts/hfs/architecture/automatic-gates.mjs +80 -73
  255. package/runtime/scripts/hfs/architecture/backend-app-files.d.mts +10 -0
  256. package/runtime/scripts/hfs/architecture/backend-app-files.mjs +88 -77
  257. package/runtime/scripts/hfs/architecture/backend.d.mts +5 -0
  258. package/runtime/scripts/hfs/architecture/backend.mjs +378 -357
  259. package/runtime/scripts/hfs/architecture/background-unowned.d.mts +25 -0
  260. package/runtime/scripts/hfs/architecture/background-unowned.mjs +201 -187
  261. package/runtime/scripts/hfs/architecture/canon-clones.d.mts +8 -0
  262. package/runtime/scripts/hfs/architecture/canon-clones.mjs +260 -209
  263. package/runtime/scripts/hfs/architecture/canon-jsx-clones.d.mts +5 -0
  264. package/runtime/scripts/hfs/architecture/canon-jsx-clones.mjs +149 -116
  265. package/runtime/scripts/hfs/architecture/client-reaches-server.d.mts +28 -0
  266. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +72 -68
  267. package/runtime/scripts/hfs/architecture/clones.d.mts +54 -0
  268. package/runtime/scripts/hfs/architecture/clones.mjs +212 -217
  269. package/runtime/scripts/hfs/architecture/config-unread.d.mts +14 -0
  270. package/runtime/scripts/hfs/architecture/config-unread.mjs +7 -9
  271. package/runtime/scripts/hfs/architecture/config.d.mts +74 -0
  272. package/runtime/scripts/hfs/architecture/config.mjs +302 -268
  273. package/runtime/scripts/hfs/architecture/connection-map.d.mts +33 -0
  274. package/runtime/scripts/hfs/architecture/connection-map.mjs +258 -225
  275. package/runtime/scripts/hfs/architecture/constructor-deps.d.mts +57 -0
  276. package/runtime/scripts/hfs/architecture/constructor-deps.mjs +80 -71
  277. package/runtime/scripts/hfs/architecture/context-coupling.d.mts +21 -0
  278. package/runtime/scripts/hfs/architecture/context-coupling.mjs +82 -77
  279. package/runtime/scripts/hfs/architecture/context-map.d.mts +102 -0
  280. package/runtime/scripts/hfs/architecture/context-map.mjs +267 -234
  281. package/runtime/scripts/hfs/architecture/context-owner.d.mts +15 -0
  282. package/runtime/scripts/hfs/architecture/context-owner.mjs +73 -61
  283. package/runtime/scripts/hfs/architecture/context-platform-tables.d.mts +18 -0
  284. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -43
  285. package/runtime/scripts/hfs/architecture/context-transaction.d.mts +20 -0
  286. package/runtime/scripts/hfs/architecture/context-transaction.mjs +54 -52
  287. package/runtime/scripts/hfs/architecture/contract-fixture-guard.d.mts +37 -0
  288. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +113 -104
  289. package/runtime/scripts/hfs/architecture/contracts-readonly.d.mts +32 -0
  290. package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +288 -270
  291. package/runtime/scripts/hfs/architecture/contracts.d.mts +30 -0
  292. package/runtime/scripts/hfs/architecture/contracts.mjs +408 -393
  293. package/runtime/scripts/hfs/architecture/cross-app-duplicate.d.mts +27 -0
  294. package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +50 -51
  295. package/runtime/scripts/hfs/architecture/dead-exports-bindings.d.mts +21 -0
  296. package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +143 -119
  297. package/runtime/scripts/hfs/architecture/dead-exports.d.mts +32 -0
  298. package/runtime/scripts/hfs/architecture/dead-exports.mjs +286 -228
  299. package/runtime/scripts/hfs/architecture/default-deny.d.mts +21 -0
  300. package/runtime/scripts/hfs/architecture/default-deny.mjs +129 -110
  301. package/runtime/scripts/hfs/architecture/doc-language.d.mts +17 -0
  302. package/runtime/scripts/hfs/architecture/doc-language.mjs +24 -19
  303. package/runtime/scripts/hfs/architecture/entity-registry.d.mts +49 -0
  304. package/runtime/scripts/hfs/architecture/entity-registry.mjs +20 -22
  305. package/runtime/scripts/hfs/architecture/entrypoint.d.mts +13 -0
  306. package/runtime/scripts/hfs/architecture/entrypoint.mjs +47 -45
  307. package/runtime/scripts/hfs/architecture/error-codes.d.mts +16 -0
  308. package/runtime/scripts/hfs/architecture/error-codes.mjs +37 -35
  309. package/runtime/scripts/hfs/architecture/error-masked.d.mts +16 -0
  310. package/runtime/scripts/hfs/architecture/error-masked.mjs +55 -59
  311. package/runtime/scripts/hfs/architecture/fe-slot-allows.d.mts +38 -0
  312. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +24 -23
  313. package/runtime/scripts/hfs/architecture/feature-shape.d.mts +26 -0
  314. package/runtime/scripts/hfs/architecture/feature-shape.mjs +40 -38
  315. package/runtime/scripts/hfs/architecture/framework-pinned.d.mts +3 -0
  316. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +21 -21
  317. package/runtime/scripts/hfs/architecture/frontend-block-render.d.mts +26 -0
  318. package/runtime/scripts/hfs/architecture/frontend-block-render.mjs +106 -98
  319. package/runtime/scripts/hfs/architecture/frontend-grammar.d.mts +7 -0
  320. package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +153 -150
  321. package/runtime/scripts/hfs/architecture/frontend-routing.d.mts +23 -0
  322. package/runtime/scripts/hfs/architecture/frontend-routing.mjs +161 -149
  323. package/runtime/scripts/hfs/architecture/frontend-world-render.d.mts +72 -0
  324. package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +343 -305
  325. package/runtime/scripts/hfs/architecture/frontend-world.d.mts +94 -0
  326. package/runtime/scripts/hfs/architecture/frontend-world.mjs +340 -294
  327. package/runtime/scripts/hfs/architecture/frontend.d.mts +31 -0
  328. package/runtime/scripts/hfs/architecture/frontend.mjs +174 -184
  329. package/runtime/scripts/hfs/architecture/function-spec-providers.d.mts +27 -0
  330. package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +124 -106
  331. package/runtime/scripts/hfs/architecture/hfs-graph.d.mts +78 -0
  332. package/runtime/scripts/hfs/architecture/hfs-graph.mjs +52 -48
  333. package/runtime/scripts/hfs/architecture/hfs.d.mts +135 -0
  334. package/runtime/scripts/hfs/architecture/hfs.mjs +328 -282
  335. package/runtime/scripts/hfs/architecture/hooks-are-hooks.d.mts +24 -0
  336. package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +81 -71
  337. package/runtime/scripts/hfs/architecture/i18n-keys.d.mts +31 -0
  338. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +155 -141
  339. package/runtime/scripts/hfs/architecture/identity.d.mts +7 -0
  340. package/runtime/scripts/hfs/architecture/identity.mjs +141 -125
  341. package/runtime/scripts/hfs/architecture/index.d.mts +101 -0
  342. package/runtime/scripts/hfs/architecture/index.mjs +272 -272
  343. package/runtime/scripts/hfs/architecture/injection-token-exported.d.mts +21 -0
  344. package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +45 -43
  345. package/runtime/scripts/hfs/architecture/jwt-ast.d.mts +16 -0
  346. package/runtime/scripts/hfs/architecture/jwt-ast.mjs +195 -176
  347. package/runtime/scripts/hfs/architecture/machine-ast.d.mts +88 -0
  348. package/runtime/scripts/hfs/architecture/machine-ast.mjs +163 -155
  349. package/runtime/scripts/hfs/architecture/managed-scripts.d.mts +2 -0
  350. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +19 -20
  351. package/runtime/scripts/hfs/architecture/module-per-transport.d.mts +35 -0
  352. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +177 -172
  353. package/runtime/scripts/hfs/architecture/next-data-contract.d.mts +35 -0
  354. package/runtime/scripts/hfs/architecture/next-data-contract.mjs +100 -78
  355. package/runtime/scripts/hfs/architecture/next-data-inference.d.mts +39 -0
  356. package/runtime/scripts/hfs/architecture/next-data-inference.mjs +170 -154
  357. package/runtime/scripts/hfs/architecture/next-data-key.d.mts +50 -0
  358. package/runtime/scripts/hfs/architecture/next-data-key.mjs +311 -275
  359. package/runtime/scripts/hfs/architecture/next-data-symbols.d.mts +5 -0
  360. package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +17 -17
  361. package/runtime/scripts/hfs/architecture/next-data.d.mts +36 -0
  362. package/runtime/scripts/hfs/architecture/next-data.mjs +351 -334
  363. package/runtime/scripts/hfs/architecture/operation-contract-roots.d.mts +10 -0
  364. package/runtime/scripts/hfs/architecture/operation-contract-roots.mjs +39 -37
  365. package/runtime/scripts/hfs/architecture/owners.d.mts +5 -0
  366. package/runtime/scripts/hfs/architecture/owners.mjs +97 -98
  367. package/runtime/scripts/hfs/architecture/package-shape.d.mts +26 -0
  368. package/runtime/scripts/hfs/architecture/package-shape.mjs +56 -55
  369. package/runtime/scripts/hfs/architecture/presentation.d.mts +24 -0
  370. package/runtime/scripts/hfs/architecture/presentation.mjs +138 -130
  371. package/runtime/scripts/hfs/architecture/reachability.d.mts +25 -0
  372. package/runtime/scripts/hfs/architecture/reachability.mjs +258 -241
  373. package/runtime/scripts/hfs/architecture/register-once.d.mts +18 -0
  374. package/runtime/scripts/hfs/architecture/register-once.mjs +152 -139
  375. package/runtime/scripts/hfs/architecture/registration.d.mts +15 -0
  376. package/runtime/scripts/hfs/architecture/registration.mjs +301 -267
  377. package/runtime/scripts/hfs/architecture/required-files.d.mts +64 -0
  378. package/runtime/scripts/hfs/architecture/required-files.mjs +172 -167
  379. package/runtime/scripts/hfs/architecture/route-files-thin.d.mts +23 -0
  380. package/runtime/scripts/hfs/architecture/route-files-thin.mjs +85 -77
  381. package/runtime/scripts/hfs/architecture/schema-owner.d.mts +34 -0
  382. package/runtime/scripts/hfs/architecture/schema-owner.mjs +206 -181
  383. package/runtime/scripts/hfs/architecture/server-action.d.mts +22 -0
  384. package/runtime/scripts/hfs/architecture/server-action.mjs +48 -48
  385. package/runtime/scripts/hfs/architecture/side-boundary.d.mts +3 -0
  386. package/runtime/scripts/hfs/architecture/side-boundary.mjs +2 -5
  387. package/runtime/scripts/hfs/architecture/source-names-shape.d.mts +85 -0
  388. package/runtime/scripts/hfs/architecture/source-names-shape.mjs +261 -259
  389. package/runtime/scripts/hfs/architecture/source-names.d.mts +7 -0
  390. package/runtime/scripts/hfs/architecture/source-names.mjs +304 -278
  391. package/runtime/scripts/hfs/architecture/sql-keys.d.mts +5 -0
  392. package/runtime/scripts/hfs/architecture/sql-keys.mjs +277 -194
  393. package/runtime/scripts/hfs/architecture/sql-owner.d.mts +30 -0
  394. package/runtime/scripts/hfs/architecture/sql-owner.mjs +236 -194
  395. package/runtime/scripts/hfs/architecture/sql-returning.d.mts +22 -0
  396. package/runtime/scripts/hfs/architecture/sql-returning.mjs +46 -45
  397. package/runtime/scripts/hfs/architecture/sql-token-forms.d.mts +21 -0
  398. package/runtime/scripts/hfs/architecture/sql-token-forms.mjs +86 -74
  399. package/runtime/scripts/hfs/architecture/sql-tokens.d.mts +32 -0
  400. package/runtime/scripts/hfs/architecture/sql-tokens.mjs +456 -341
  401. package/runtime/scripts/hfs/architecture/staged-consumers.d.mts +7 -0
  402. package/runtime/scripts/hfs/architecture/staged-consumers.mjs +35 -30
  403. package/runtime/scripts/hfs/architecture/surface.d.mts +26 -0
  404. package/runtime/scripts/hfs/architecture/surface.mjs +64 -74
  405. package/runtime/scripts/hfs/architecture/symbols.d.mts +11 -0
  406. package/runtime/scripts/hfs/architecture/symbols.mjs +209 -216
  407. package/runtime/scripts/hfs/architecture/test-world-files.d.mts +45 -0
  408. package/runtime/scripts/hfs/architecture/test-world-files.mjs +165 -155
  409. package/runtime/scripts/hfs/architecture/tiers.d.mts +29 -0
  410. package/runtime/scripts/hfs/architecture/tiers.mjs +161 -154
  411. package/runtime/scripts/hfs/architecture/transport-owner.d.mts +37 -0
  412. package/runtime/scripts/hfs/architecture/transport-owner.mjs +155 -149
  413. package/runtime/scripts/hfs/architecture/type-context.d.mts +60 -0
  414. package/runtime/scripts/hfs/architecture/type-context.mjs +255 -250
  415. package/runtime/scripts/hfs/architecture/typescript.d.mts +94 -0
  416. package/runtime/scripts/hfs/architecture/typescript.mjs +358 -318
  417. package/runtime/scripts/hfs/architecture/unit-spec-providers.d.mts +34 -0
  418. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +110 -92
  419. package/runtime/scripts/hfs/architecture.d.mts +13 -0
  420. package/runtime/scripts/hfs/architecture.mjs +39 -29
  421. package/runtime/scripts/hfs/canon-pins.d.mts +26 -0
  422. package/runtime/scripts/hfs/canon-pins.mjs +5 -8
  423. package/runtime/scripts/hfs/catalog-once.d.mts +2 -0
  424. package/runtime/scripts/hfs/catalog-once.mjs +8 -9
  425. package/runtime/scripts/hfs/check-results.d.mts +29 -0
  426. package/runtime/scripts/hfs/check-results.mjs +14 -18
  427. package/runtime/scripts/hfs/check.d.mts +144 -0
  428. package/runtime/scripts/hfs/check.mjs +309 -329
  429. package/runtime/scripts/hfs/ci-declaration.d.mts +77 -0
  430. package/runtime/scripts/hfs/ci-declaration.mjs +99 -87
  431. package/runtime/scripts/hfs/coverage-from.d.mts +22 -0
  432. package/runtime/scripts/hfs/coverage-from.mjs +52 -49
  433. package/runtime/scripts/hfs/coverage-scope.d.mts +87 -0
  434. package/runtime/scripts/hfs/coverage-scope.mjs +139 -142
  435. package/runtime/scripts/hfs/db-baseline-verb.d.mts +84 -0
  436. package/runtime/scripts/hfs/db-baseline-verb.mjs +327 -266
  437. package/runtime/scripts/hfs/db-errors.d.mts +2 -0
  438. package/runtime/scripts/hfs/db-errors.mjs +2 -1
  439. package/runtime/scripts/hfs/db-plan-verb.d.mts +33 -0
  440. package/runtime/scripts/hfs/db-plan-verb.mjs +174 -128
  441. package/runtime/scripts/hfs/db-restamp-verb.d.mts +5 -0
  442. package/runtime/scripts/hfs/db-restamp-verb.mjs +94 -70
  443. package/runtime/scripts/hfs/db-squash.d.mts +117 -0
  444. package/runtime/scripts/hfs/db-squash.mjs +414 -351
  445. package/runtime/scripts/hfs/declaration-shape.d.mts +3 -0
  446. package/runtime/scripts/hfs/declaration-shape.mjs +140 -103
  447. package/runtime/scripts/hfs/declaration-slots.d.mts +18 -0
  448. package/runtime/scripts/hfs/declaration-slots.mjs +64 -61
  449. package/runtime/scripts/hfs/doc-commands.d.mts +71 -0
  450. package/runtime/scripts/hfs/doc-commands.mjs +257 -250
  451. package/runtime/scripts/hfs/edition-slots.d.mts +37 -0
  452. package/runtime/scripts/hfs/edition-slots.mjs +43 -40
  453. package/runtime/scripts/hfs/example-secrets.d.mts +18 -0
  454. package/runtime/scripts/hfs/example-secrets.mjs +173 -154
  455. package/runtime/scripts/hfs/gate-threshold.d.mts +20 -0
  456. package/runtime/scripts/hfs/gate-threshold.mjs +14 -22
  457. package/runtime/scripts/hfs/grammar-presentation.d.mts +74 -0
  458. package/runtime/scripts/hfs/grammar-presentation.mjs +109 -106
  459. package/runtime/scripts/hfs/linear-text.d.mts +6 -0
  460. package/runtime/scripts/hfs/linear-text.mjs +20 -20
  461. package/runtime/scripts/hfs/manifest-shape.d.mts +216 -0
  462. package/runtime/scripts/hfs/manifest-shape.mjs +399 -324
  463. package/runtime/scripts/hfs/param-names.d.mts +3 -0
  464. package/runtime/scripts/hfs/param-names.mjs +6 -11
  465. package/runtime/scripts/hfs/path-findings.d.mts +22 -0
  466. package/runtime/scripts/hfs/path-findings.mjs +120 -100
  467. package/runtime/scripts/hfs/pin-findings.d.mts +19 -0
  468. package/runtime/scripts/hfs/pin-findings.mjs +26 -21
  469. package/runtime/scripts/hfs/refusal-codes.d.mts +1 -0
  470. package/runtime/scripts/hfs/refusal-codes.mjs +2 -2
  471. package/runtime/scripts/hfs/repo-identity.d.mts +15 -0
  472. package/runtime/scripts/hfs/repo-identity.mjs +55 -42
  473. package/runtime/scripts/hfs/rule-catalog.d.mts +77 -0
  474. package/runtime/scripts/hfs/rule-catalog.mjs +353 -257
  475. package/runtime/scripts/hfs/rule-params-shape.d.mts +10 -0
  476. package/runtime/scripts/hfs/rule-params-shape.mjs +60 -47
  477. package/runtime/scripts/hfs/rules/a15-findings.d.mts +18 -0
  478. package/runtime/scripts/hfs/rules/a15-findings.mjs +59 -55
  479. package/runtime/scripts/hfs/rules/app-env-required.d.mts +9 -0
  480. package/runtime/scripts/hfs/rules/app-env-required.mjs +13 -13
  481. package/runtime/scripts/hfs/rules/app-root.d.mts +15 -0
  482. package/runtime/scripts/hfs/rules/app-root.mjs +20 -21
  483. package/runtime/scripts/hfs/rules/app-workflow-findings.d.mts +12 -0
  484. package/runtime/scripts/hfs/rules/app-workflow-findings.mjs +4 -4
  485. package/runtime/scripts/hfs/rules/assets.d.mts +13 -0
  486. package/runtime/scripts/hfs/rules/assets.mjs +58 -46
  487. package/runtime/scripts/hfs/rules/boot-proof.d.mts +11 -0
  488. package/runtime/scripts/hfs/rules/boot-proof.mjs +7 -8
  489. package/runtime/scripts/hfs/rules/bypass-markers.d.mts +9 -0
  490. package/runtime/scripts/hfs/rules/bypass-markers.mjs +7 -12
  491. package/runtime/scripts/hfs/rules/canon-copy-drift.d.mts +10 -0
  492. package/runtime/scripts/hfs/rules/canon-copy-drift.mjs +70 -68
  493. package/runtime/scripts/hfs/rules/ci-custody.d.mts +7 -0
  494. package/runtime/scripts/hfs/rules/ci-custody.mjs +72 -73
  495. package/runtime/scripts/hfs/rules/ci-standard.d.mts +12 -0
  496. package/runtime/scripts/hfs/rules/ci-standard.mjs +149 -142
  497. package/runtime/scripts/hfs/rules/cli.d.mts +7 -0
  498. package/runtime/scripts/hfs/rules/cli.mjs +24 -23
  499. package/runtime/scripts/hfs/rules/contract-compat.d.mts +11 -0
  500. package/runtime/scripts/hfs/rules/contract-compat.mjs +64 -53
  501. package/runtime/scripts/hfs/rules/contract.d.mts +26 -0
  502. package/runtime/scripts/hfs/rules/contract.mjs +126 -111
  503. package/runtime/scripts/hfs/rules/coverage-below.d.mts +17 -0
  504. package/runtime/scripts/hfs/rules/coverage-below.mjs +200 -167
  505. package/runtime/scripts/hfs/rules/database-access.d.mts +12 -0
  506. package/runtime/scripts/hfs/rules/database-access.mjs +231 -202
  507. package/runtime/scripts/hfs/rules/database-baseline.d.mts +22 -0
  508. package/runtime/scripts/hfs/rules/database-baseline.mjs +104 -104
  509. package/runtime/scripts/hfs/rules/database-config.d.mts +11 -0
  510. package/runtime/scripts/hfs/rules/database-config.mjs +101 -135
  511. package/runtime/scripts/hfs/rules/database-constants.d.mts +36 -0
  512. package/runtime/scripts/hfs/rules/database-constants.mjs +7 -7
  513. package/runtime/scripts/hfs/rules/database-migrations.d.mts +41 -0
  514. package/runtime/scripts/hfs/rules/database-migrations.mjs +428 -388
  515. package/runtime/scripts/hfs/rules/database-plpgsql.d.mts +5 -0
  516. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +278 -259
  517. package/runtime/scripts/hfs/rules/database-queue-sources.d.mts +4 -0
  518. package/runtime/scripts/hfs/rules/database-queue-sources.mjs +48 -43
  519. package/runtime/scripts/hfs/rules/database-queues.d.mts +15 -0
  520. package/runtime/scripts/hfs/rules/database-queues.mjs +211 -182
  521. package/runtime/scripts/hfs/rules/database-sql.d.mts +16 -0
  522. package/runtime/scripts/hfs/rules/database-sql.mjs +394 -394
  523. package/runtime/scripts/hfs/rules/database-transaction.d.mts +3 -0
  524. package/runtime/scripts/hfs/rules/database-transaction.mjs +25 -27
  525. package/runtime/scripts/hfs/rules/database.d.mts +22 -0
  526. package/runtime/scripts/hfs/rules/database.mjs +42 -58
  527. package/runtime/scripts/hfs/rules/deploy-scan.d.mts +10 -0
  528. package/runtime/scripts/hfs/rules/deploy-scan.mjs +177 -168
  529. package/runtime/scripts/hfs/rules/deploy-workflow.d.mts +3 -0
  530. package/runtime/scripts/hfs/rules/deploy-workflow.mjs +194 -171
  531. package/runtime/scripts/hfs/rules/deps.d.mts +6 -0
  532. package/runtime/scripts/hfs/rules/deps.mjs +61 -60
  533. package/runtime/scripts/hfs/rules/doc-fresh.d.mts +11 -0
  534. package/runtime/scripts/hfs/rules/doc-fresh.mjs +242 -223
  535. package/runtime/scripts/hfs/rules/docker-health.d.mts +9 -0
  536. package/runtime/scripts/hfs/rules/docker-health.mjs +222 -196
  537. package/runtime/scripts/hfs/rules/docker.d.mts +13 -0
  538. package/runtime/scripts/hfs/rules/docker.mjs +234 -208
  539. package/runtime/scripts/hfs/rules/edition.d.mts +18 -0
  540. package/runtime/scripts/hfs/rules/edition.mjs +103 -101
  541. package/runtime/scripts/hfs/rules/env-parity.d.mts +25 -0
  542. package/runtime/scripts/hfs/rules/env-parity.mjs +278 -252
  543. package/runtime/scripts/hfs/rules/env-value-shape.d.mts +21 -0
  544. package/runtime/scripts/hfs/rules/env-value-shape.mjs +82 -67
  545. package/runtime/scripts/hfs/rules/event-bus.d.mts +17 -0
  546. package/runtime/scripts/hfs/rules/event-bus.mjs +145 -133
  547. package/runtime/scripts/hfs/rules/event-tables.d.mts +7 -0
  548. package/runtime/scripts/hfs/rules/event-tables.mjs +77 -75
  549. package/runtime/scripts/hfs/rules/fe-build-args.d.mts +10 -0
  550. package/runtime/scripts/hfs/rules/fe-build-args.mjs +116 -99
  551. package/runtime/scripts/hfs/rules/fe-contract-documents.d.mts +7 -0
  552. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +121 -111
  553. package/runtime/scripts/hfs/rules/fe-no-tests.d.mts +9 -0
  554. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +37 -36
  555. package/runtime/scripts/hfs/rules/fe-stack.d.mts +23 -0
  556. package/runtime/scripts/hfs/rules/fe-stack.mjs +108 -105
  557. package/runtime/scripts/hfs/rules/feature-files.d.mts +10 -0
  558. package/runtime/scripts/hfs/rules/feature-files.mjs +14 -19
  559. package/runtime/scripts/hfs/rules/frontend-tree.d.mts +18 -0
  560. package/runtime/scripts/hfs/rules/frontend-tree.mjs +105 -91
  561. package/runtime/scripts/hfs/rules/integration-specs.d.mts +7 -0
  562. package/runtime/scripts/hfs/rules/integration-specs.mjs +190 -177
  563. package/runtime/scripts/hfs/rules/kinds.d.mts +6 -0
  564. package/runtime/scripts/hfs/rules/kinds.mjs +88 -81
  565. package/runtime/scripts/hfs/rules/lexicon.d.mts +9 -0
  566. package/runtime/scripts/hfs/rules/lexicon.mjs +43 -40
  567. package/runtime/scripts/hfs/rules/lint-suppression.d.mts +5 -0
  568. package/runtime/scripts/hfs/rules/lint-suppression.mjs +22 -20
  569. package/runtime/scripts/hfs/rules/lite-proof-lane.d.mts +9 -0
  570. package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +59 -58
  571. package/runtime/scripts/hfs/rules/lite-secrets.d.mts +12 -0
  572. package/runtime/scripts/hfs/rules/lite-secrets.mjs +60 -63
  573. package/runtime/scripts/hfs/rules/mcp-tools.d.mts +7 -0
  574. package/runtime/scripts/hfs/rules/mcp-tools.mjs +56 -41
  575. package/runtime/scripts/hfs/rules/monorepo.d.mts +17 -0
  576. package/runtime/scripts/hfs/rules/monorepo.mjs +153 -138
  577. package/runtime/scripts/hfs/rules/peer-integrations.d.mts +7 -0
  578. package/runtime/scripts/hfs/rules/peer-integrations.mjs +39 -36
  579. package/runtime/scripts/hfs/rules/pipeline.d.mts +7 -0
  580. package/runtime/scripts/hfs/rules/pipeline.mjs +53 -53
  581. package/runtime/scripts/hfs/rules/proof-commands.d.mts +14 -0
  582. package/runtime/scripts/hfs/rules/proof-commands.mjs +52 -41
  583. package/runtime/scripts/hfs/rules/read.d.mts +7 -0
  584. package/runtime/scripts/hfs/rules/read.mjs +19 -15
  585. package/runtime/scripts/hfs/rules/release-provenance.d.mts +12 -0
  586. package/runtime/scripts/hfs/rules/release-provenance.mjs +196 -159
  587. package/runtime/scripts/hfs/rules/repo-local-checks.d.mts +5 -0
  588. package/runtime/scripts/hfs/rules/repo-local-checks.mjs +19 -17
  589. package/runtime/scripts/hfs/rules/rule-types.d.mts +40 -0
  590. package/runtime/scripts/hfs/rules/rule-types.mjs +4 -0
  591. package/runtime/scripts/hfs/rules/saga-delivery.d.mts +2 -0
  592. package/runtime/scripts/hfs/rules/saga-delivery.mjs +58 -50
  593. package/runtime/scripts/hfs/rules/saga.d.mts +7 -0
  594. package/runtime/scripts/hfs/rules/saga.mjs +151 -145
  595. package/runtime/scripts/hfs/rules/script-hygiene.d.mts +11 -0
  596. package/runtime/scripts/hfs/rules/script-hygiene.mjs +112 -95
  597. package/runtime/scripts/hfs/rules/secret-custody-only.d.mts +7 -0
  598. package/runtime/scripts/hfs/rules/secret-custody-only.mjs +29 -35
  599. package/runtime/scripts/hfs/rules/secrets.d.mts +29 -0
  600. package/runtime/scripts/hfs/rules/secrets.mjs +45 -44
  601. package/runtime/scripts/hfs/rules/services.d.mts +7 -0
  602. package/runtime/scripts/hfs/rules/services.mjs +148 -148
  603. package/runtime/scripts/hfs/rules/spec-placement.d.mts +8 -0
  604. package/runtime/scripts/hfs/rules/spec-placement.mjs +12 -13
  605. package/runtime/scripts/hfs/rules/stack-tooling.d.mts +11 -0
  606. package/runtime/scripts/hfs/rules/stack-tooling.mjs +45 -52
  607. package/runtime/scripts/hfs/rules/stacks-auth-cookie-domain.d.mts +30 -0
  608. package/runtime/scripts/hfs/rules/stacks-auth-cookie-domain.mjs +149 -0
  609. package/runtime/scripts/hfs/rules/stacks-deploy.d.mts +7 -0
  610. package/runtime/scripts/hfs/rules/stacks-deploy.mjs +32 -42
  611. package/runtime/scripts/hfs/rules/stacks-ingress.d.mts +16 -0
  612. package/runtime/scripts/hfs/rules/stacks-ingress.mjs +62 -64
  613. package/runtime/scripts/hfs/rules/stacks-lite-backing.d.mts +17 -0
  614. package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +111 -116
  615. package/runtime/scripts/hfs/rules/stacks-release.d.mts +26 -0
  616. package/runtime/scripts/hfs/rules/stacks-release.mjs +335 -309
  617. package/runtime/scripts/hfs/rules/stacks-runtime.d.mts +31 -0
  618. package/runtime/scripts/hfs/rules/stacks-runtime.mjs +130 -124
  619. package/runtime/scripts/hfs/rules/stacks-swarm.d.mts +5 -0
  620. package/runtime/scripts/hfs/rules/stacks-swarm.mjs +366 -344
  621. package/runtime/scripts/hfs/rules/stacks.d.mts +7 -0
  622. package/runtime/scripts/hfs/rules/stacks.mjs +37 -38
  623. package/runtime/scripts/hfs/rules/staged-specs.d.mts +10 -0
  624. package/runtime/scripts/hfs/rules/staged-specs.mjs +14 -18
  625. package/runtime/scripts/hfs/rules/supply-chain.d.mts +10 -0
  626. package/runtime/scripts/hfs/rules/supply-chain.mjs +143 -150
  627. package/runtime/scripts/hfs/rules/test-topology.d.mts +5 -0
  628. package/runtime/scripts/hfs/rules/test-topology.mjs +23 -21
  629. package/runtime/scripts/hfs/rules/text-integrity.d.mts +9 -0
  630. package/runtime/scripts/hfs/rules/text-integrity.mjs +47 -43
  631. package/runtime/scripts/hfs/rules/unused-symbol-flow.d.mts +22 -0
  632. package/runtime/scripts/hfs/rules/unused-symbol-flow.mjs +226 -204
  633. package/runtime/scripts/hfs/rules/unused-symbol-ledger.d.mts +7 -0
  634. package/runtime/scripts/hfs/rules/unused-symbol-ledger.mjs +70 -49
  635. package/runtime/scripts/hfs/rules/unused-symbol-properties.d.mts +12 -0
  636. package/runtime/scripts/hfs/rules/unused-symbol-properties.mjs +141 -126
  637. package/runtime/scripts/hfs/rules/unused-symbol-translators.d.mts +8 -0
  638. package/runtime/scripts/hfs/rules/unused-symbol-translators.mjs +160 -140
  639. package/runtime/scripts/hfs/rules/unused-symbol-values.d.mts +9 -0
  640. package/runtime/scripts/hfs/rules/unused-symbol-values.mjs +138 -125
  641. package/runtime/scripts/hfs/rules/unused-symbol.d.mts +11 -0
  642. package/runtime/scripts/hfs/rules/unused-symbol.mjs +283 -253
  643. package/runtime/scripts/hfs/rules/workflow-contexts.d.mts +8 -0
  644. package/runtime/scripts/hfs/rules/workflow-contexts.mjs +79 -67
  645. package/runtime/scripts/hfs/runtime-rules/git-triggers.d.mts +6 -0
  646. package/runtime/scripts/hfs/runtime-rules/git-triggers.mjs +36 -35
  647. package/runtime/scripts/hfs/runtime-rules/hook-shape.d.mts +10 -0
  648. package/runtime/scripts/hfs/runtime-rules/hook-shape.mjs +45 -42
  649. package/runtime/scripts/hfs/runtime-rules/source-ast.d.mts +39 -0
  650. package/runtime/scripts/hfs/runtime-rules/source-ast.mjs +130 -116
  651. package/runtime/scripts/hfs/runtime-rules/workflow-source.d.mts +5 -0
  652. package/runtime/scripts/hfs/runtime-rules/workflow-source.mjs +8 -7
  653. package/runtime/scripts/hfs/sealed-members.d.mts +22 -0
  654. package/runtime/scripts/hfs/sealed-members.mjs +79 -75
  655. package/runtime/scripts/hfs/secret-bootstrap-crypto.d.mts +12 -0
  656. package/runtime/scripts/hfs/secret-bootstrap-crypto.mjs +151 -167
  657. package/runtime/scripts/hfs/secret-document.d.mts +16 -0
  658. package/runtime/scripts/hfs/secret-document.mjs +59 -75
  659. package/runtime/scripts/hfs/secret-edit.d.mts +14 -0
  660. package/runtime/scripts/hfs/secret-edit.mjs +115 -135
  661. package/runtime/scripts/hfs/secret-errors.d.mts +4 -0
  662. package/runtime/scripts/hfs/secret-errors.mjs +19 -16
  663. package/runtime/scripts/hfs/secret-files.d.mts +20 -0
  664. package/runtime/scripts/hfs/secret-files.mjs +103 -112
  665. package/runtime/scripts/hfs/secret-init.d.mts +108 -0
  666. package/runtime/scripts/hfs/secret-init.mjs +304 -346
  667. package/runtime/scripts/hfs/secret-output.d.mts +18 -0
  668. package/runtime/scripts/hfs/secret-output.mjs +52 -80
  669. package/runtime/scripts/hfs/secret-prompt.d.mts +39 -0
  670. package/runtime/scripts/hfs/secret-prompt.mjs +108 -93
  671. package/runtime/scripts/hfs/secret-sops.d.mts +29 -0
  672. package/runtime/scripts/hfs/secret-sops.mjs +161 -199
  673. package/runtime/scripts/hfs/secret-standard.d.mts +89 -0
  674. package/runtime/scripts/hfs/secret-standard.mjs +300 -264
  675. package/runtime/scripts/hfs/secret-store.d.mts +36 -0
  676. package/runtime/scripts/hfs/secret-store.mjs +120 -136
  677. package/runtime/scripts/hfs/secret.d.mts +40 -0
  678. package/runtime/scripts/hfs/secret.mjs +254 -222
  679. package/runtime/scripts/hfs/slot-app-view.d.mts +11 -0
  680. package/runtime/scripts/hfs/slot-app-view.mjs +69 -74
  681. package/runtime/scripts/hfs/slot-classify.d.mts +75 -0
  682. package/runtime/scripts/hfs/slot-classify.mjs +42 -38
  683. package/runtime/scripts/hfs/slot-errors.d.mts +6 -0
  684. package/runtime/scripts/hfs/slot-errors.mjs +8 -7
  685. package/runtime/scripts/hfs/slot-imports.d.mts +8 -0
  686. package/runtime/scripts/hfs/slot-imports.mjs +70 -63
  687. package/runtime/scripts/hfs/slot-manifest-shape.d.mts +9 -0
  688. package/runtime/scripts/hfs/slot-manifest-shape.mjs +74 -52
  689. package/runtime/scripts/hfs/slot-match.d.mts +50 -0
  690. package/runtime/scripts/hfs/slot-match.mjs +122 -111
  691. package/runtime/scripts/hfs/slot-path.d.mts +1 -0
  692. package/runtime/scripts/hfs/slot-path.mjs +0 -2
  693. package/runtime/scripts/hfs/slot-required.d.mts +11 -0
  694. package/runtime/scripts/hfs/slot-required.mjs +65 -59
  695. package/runtime/scripts/hfs/slot-semantic-problems.d.mts +9 -0
  696. package/runtime/scripts/hfs/slot-semantic-problems.mjs +138 -112
  697. package/runtime/scripts/hfs/slot-side-problems.d.mts +3 -0
  698. package/runtime/scripts/hfs/slot-side-problems.mjs +23 -19
  699. package/runtime/scripts/hfs/slots.d.mts +188 -0
  700. package/runtime/scripts/hfs/slots.mjs +175 -152
  701. package/runtime/scripts/hfs/sql/pg-parse.d.mts +9 -0
  702. package/runtime/scripts/hfs/sql/pg-parse.mjs +25 -25
  703. package/runtime/scripts/hfs/stack-config-kit.d.mts +22 -0
  704. package/runtime/scripts/hfs/stack-config-kit.mjs +21 -17
  705. package/runtime/scripts/hfs/stack-deploy-config.d.mts +155 -0
  706. package/runtime/scripts/hfs/stack-deploy-config.mjs +345 -273
  707. package/runtime/scripts/hfs/stack-ingress-config.d.mts +74 -0
  708. package/runtime/scripts/hfs/stack-ingress-config.mjs +211 -177
  709. package/runtime/scripts/hfs/stacks-gate.d.mts +52 -0
  710. package/runtime/scripts/hfs/stacks-gate.mjs +557 -397
  711. package/runtime/scripts/hfs/starcistacks-runtime.d.mts +7 -0
  712. package/runtime/scripts/hfs/starcistacks-runtime.mjs +51 -40
  713. package/runtime/scripts/hfs/test-world-slot.d.mts +2 -0
  714. package/runtime/scripts/hfs/test-world-slot.mjs +0 -1
  715. package/runtime/scripts/hfs/trailing-slashes.d.mts +2 -0
  716. package/runtime/scripts/hfs/trailing-slashes.mjs +4 -4
  717. package/runtime/scripts/hfs/tree-findings.d.mts +9 -0
  718. package/runtime/scripts/hfs/tree-findings.mjs +18 -17
  719. package/runtime/scripts/hfs/tree.d.mts +35 -0
  720. package/runtime/scripts/hfs/tree.mjs +56 -46
  721. package/runtime/scripts/hfs/typescript-programs.d.mts +36 -0
  722. package/runtime/scripts/hfs/typescript-programs.mjs +66 -69
  723. package/runtime/scripts/hfs/view.d.mts +45 -0
  724. package/runtime/scripts/hfs/view.mjs +38 -40
  725. package/runtime/scripts/lib/bypass-markers.d.mts +15 -0
  726. package/runtime/scripts/lib/bypass-markers.mjs +210 -197
  727. package/runtime/scripts/lib/cli-arg.d.mts +67 -0
  728. package/runtime/scripts/lib/cli-arg.mjs +59 -63
  729. package/runtime/scripts/lib/command-tokens.d.mts +11 -0
  730. package/runtime/scripts/lib/command-tokens.mjs +77 -78
  731. package/runtime/scripts/lib/compose-values.d.mts +6 -0
  732. package/runtime/scripts/lib/compose-values.mjs +7 -7
  733. package/runtime/scripts/lib/css-rules.d.mts +36 -0
  734. package/runtime/scripts/lib/css-rules.mjs +181 -171
  735. package/runtime/scripts/lib/deployment-findings.d.mts +9 -0
  736. package/runtime/scripts/lib/deployment-findings.mjs +4 -4
  737. package/runtime/scripts/lib/dockerfile.d.mts +39 -0
  738. package/runtime/scripts/lib/dockerfile.mjs +131 -114
  739. package/runtime/scripts/lib/dom-tree.d.mts +7 -0
  740. package/runtime/scripts/lib/dom-tree.mjs +3 -3
  741. package/runtime/scripts/lib/env.d.mts +12 -0
  742. package/runtime/scripts/lib/env.mjs +3 -6
  743. package/runtime/scripts/lib/escape.d.mts +6 -0
  744. package/runtime/scripts/lib/escape.mjs +2 -6
  745. package/runtime/scripts/lib/event-contract.d.mts +21 -0
  746. package/runtime/scripts/lib/event-contract.mjs +73 -78
  747. package/runtime/scripts/lib/event-items.d.mts +20 -0
  748. package/runtime/scripts/lib/event-items.mjs +27 -15
  749. package/runtime/scripts/lib/example-refs.d.mts +69 -0
  750. package/runtime/scripts/lib/example-refs.mjs +145 -129
  751. package/runtime/scripts/lib/fs-kind.d.mts +12 -0
  752. package/runtime/scripts/lib/fs-kind.mjs +20 -10
  753. package/runtime/scripts/lib/git-hooks-dir.d.mts +15 -0
  754. package/runtime/scripts/lib/git-hooks-dir.mjs +16 -16
  755. package/runtime/scripts/lib/git.d.mts +24 -0
  756. package/runtime/scripts/lib/git.mjs +15 -16
  757. package/runtime/scripts/lib/glob.d.mts +4 -0
  758. package/runtime/scripts/lib/glob.mjs +23 -14
  759. package/runtime/scripts/lib/grammar-lint.d.mts +47 -0
  760. package/runtime/scripts/lib/grammar-lint.mjs +33 -37
  761. package/runtime/scripts/lib/grammar-vendor-parity-jsx.d.mts +40 -0
  762. package/runtime/scripts/lib/grammar-vendor-parity-jsx.mjs +285 -266
  763. package/runtime/scripts/lib/grammar-vendor-parity.d.mts +61 -0
  764. package/runtime/scripts/lib/grammar-vendor-parity.mjs +209 -216
  765. package/runtime/scripts/lib/graphql-contract.d.mts +53 -0
  766. package/runtime/scripts/lib/graphql-contract.mjs +201 -198
  767. package/runtime/scripts/lib/graphql-sdl.d.mts +66 -0
  768. package/runtime/scripts/lib/graphql-sdl.mjs +230 -205
  769. package/runtime/scripts/lib/hook-limits.d.mts +11 -0
  770. package/runtime/scripts/lib/hook-limits.mjs +0 -1
  771. package/runtime/scripts/lib/i18n.d.mts +48 -0
  772. package/runtime/scripts/lib/i18n.mjs +32 -35
  773. package/runtime/scripts/lib/in-order.d.mts +12 -0
  774. package/runtime/scripts/lib/in-order.mjs +57 -38
  775. package/runtime/scripts/lib/is-main.d.mts +6 -0
  776. package/runtime/scripts/lib/is-main.mjs +11 -4
  777. package/runtime/scripts/lib/js-lex.d.mts +32 -0
  778. package/runtime/scripts/lib/js-lex.mjs +196 -162
  779. package/runtime/scripts/lib/json-clone.d.mts +11 -0
  780. package/runtime/scripts/lib/json-clone.mjs +45 -36
  781. package/runtime/scripts/lib/json-schema.d.mts +2 -0
  782. package/runtime/scripts/lib/json-schema.mjs +127 -90
  783. package/runtime/scripts/lib/json.d.mts +16 -0
  784. package/runtime/scripts/lib/json.mjs +26 -14
  785. package/runtime/scripts/lib/language.d.mts +44 -0
  786. package/runtime/scripts/lib/language.mjs +133 -129
  787. package/runtime/scripts/lib/list.d.mts +17 -0
  788. package/runtime/scripts/lib/list.mjs +9 -14
  789. package/runtime/scripts/lib/loosening-budgets.d.mts +12 -0
  790. package/runtime/scripts/lib/loosening-budgets.mjs +35 -29
  791. package/runtime/scripts/lib/loosening-config.d.mts +64 -0
  792. package/runtime/scripts/lib/loosening-config.mjs +267 -236
  793. package/runtime/scripts/lib/loosening-state.d.mts +77 -0
  794. package/runtime/scripts/lib/loosening-state.mjs +134 -128
  795. package/runtime/scripts/lib/mutation-fence.d.mts +11 -0
  796. package/runtime/scripts/lib/mutation-fence.mjs +10 -5
  797. package/runtime/scripts/lib/normalize.d.mts +12 -0
  798. package/runtime/scripts/lib/normalize.mjs +11 -11
  799. package/runtime/scripts/lib/numeric-match.d.mts +2 -0
  800. package/runtime/scripts/lib/numeric-match.mjs +2 -2
  801. package/runtime/scripts/lib/package-at.d.mts +24 -0
  802. package/runtime/scripts/lib/package-at.mjs +34 -31
  803. package/runtime/scripts/lib/path-key.d.mts +76 -0
  804. package/runtime/scripts/lib/path-key.mjs +53 -38
  805. package/runtime/scripts/lib/pid-alive.d.mts +2 -0
  806. package/runtime/scripts/lib/pid-alive.mjs +10 -3
  807. package/runtime/scripts/lib/properties.d.mts +2 -0
  808. package/runtime/scripts/lib/properties.mjs +17 -11
  809. package/runtime/scripts/lib/provision-operation-schema.d.mts +4 -0
  810. package/runtime/scripts/lib/provision-operation-schema.mjs +4 -5
  811. package/runtime/scripts/lib/read-text.d.mts +12 -0
  812. package/runtime/scripts/lib/read-text.mjs +30 -17
  813. package/runtime/scripts/lib/redact.d.mts +52 -0
  814. package/runtime/scripts/lib/redact.mjs +135 -121
  815. package/runtime/scripts/lib/regex.d.mts +2 -0
  816. package/runtime/scripts/lib/regex.mjs +1 -2
  817. package/runtime/scripts/lib/roots.d.mts +32 -0
  818. package/runtime/scripts/lib/roots.mjs +5 -15
  819. package/runtime/scripts/lib/runtime-example.d.mts +1 -0
  820. package/runtime/scripts/lib/runtime-example.mjs +10 -8
  821. package/runtime/scripts/lib/same-text.d.mts +2 -0
  822. package/runtime/scripts/lib/same-text.mjs +0 -1
  823. package/runtime/scripts/lib/sealed-member.d.mts +16 -0
  824. package/runtime/scripts/lib/sealed-member.mjs +8 -13
  825. package/runtime/scripts/lib/secret-patterns.d.mts +30 -0
  826. package/runtime/scripts/lib/secret-patterns.mjs +175 -163
  827. package/runtime/scripts/lib/shell-source.d.mts +2 -0
  828. package/runtime/scripts/lib/shell-source.mjs +0 -1
  829. package/runtime/scripts/lib/sleep-sync.d.mts +2 -0
  830. package/runtime/scripts/lib/sleep-sync.mjs +5 -6
  831. package/runtime/scripts/lib/sops-envelope.d.mts +34 -0
  832. package/runtime/scripts/lib/sops-envelope.mjs +148 -120
  833. package/runtime/scripts/lib/source-phrases.d.mts +4 -0
  834. package/runtime/scripts/lib/source-phrases.mjs +13 -13
  835. package/runtime/scripts/lib/sql-ast.d.mts +13 -0
  836. package/runtime/scripts/lib/sql-ast.mjs +19 -18
  837. package/runtime/scripts/lib/sqlite.d.mts +21 -0
  838. package/runtime/scripts/lib/sqlite.mjs +3 -9
  839. package/runtime/scripts/lib/stack-declaration.d.mts +51 -0
  840. package/runtime/scripts/lib/stack-declaration.mjs +27 -20
  841. package/runtime/scripts/lib/stack-services.d.mts +73 -0
  842. package/runtime/scripts/lib/stack-services.mjs +128 -129
  843. package/runtime/scripts/lib/starciwork-boundary.d.mts +10 -0
  844. package/runtime/scripts/lib/starciwork-boundary.mjs +98 -92
  845. package/runtime/scripts/lib/swarm-stack.d.mts +24 -0
  846. package/runtime/scripts/lib/swarm-stack.mjs +112 -100
  847. package/runtime/scripts/lib/trim-end.d.mts +2 -0
  848. package/runtime/scripts/lib/trim-end.mjs +4 -4
  849. package/runtime/scripts/lib/ts-analysis.d.mts +24 -0
  850. package/runtime/scripts/lib/ts-analysis.mjs +28 -26
  851. package/runtime/scripts/lib/ts-ast.d.mts +36 -0
  852. package/runtime/scripts/lib/ts-ast.mjs +36 -42
  853. package/runtime/scripts/lib/walk.d.mts +19 -0
  854. package/runtime/scripts/lib/walk.mjs +33 -27
  855. package/runtime/scripts/lib/work-edition.d.mts +10 -0
  856. package/runtime/scripts/lib/work-edition.mjs +29 -21
  857. package/runtime/scripts/lib/workflow-events.d.mts +2 -0
  858. package/runtime/scripts/lib/workflow-events.mjs +5 -4
  859. package/runtime/scripts/lib/worktree-exclude.d.mts +16 -0
  860. package/runtime/scripts/lib/worktree-exclude.mjs +6 -8
  861. package/runtime/scripts/lib/yaml-cached.d.mts +2 -0
  862. package/runtime/scripts/lib/yaml-cached.mjs +4 -4
  863. package/runtime/scripts/machine/artifact-hold.d.mts +26 -0
  864. package/runtime/scripts/machine/artifact-hold.mjs +22 -23
  865. package/runtime/scripts/work/asset-slot.d.mts +96 -0
  866. package/runtime/scripts/work/asset-slot.mjs +201 -175
  867. package/runtime/scripts/work/draw/draw-dna-anatomy.d.mts +30 -0
  868. package/runtime/scripts/work/draw/draw-dna-anatomy.mjs +40 -36
  869. package/runtime/scripts/work/draw/draw-dna-findings.d.mts +12 -0
  870. package/runtime/scripts/work/draw/draw-dna-findings.mjs +240 -206
  871. package/runtime/scripts/work/draw/draw-dna-selector.d.mts +7 -0
  872. package/runtime/scripts/work/draw/draw-dna-selector.mjs +28 -28
  873. package/runtime/scripts/work/draw/draw-dna.d.mts +191 -0
  874. package/runtime/scripts/work/draw/draw-dna.mjs +283 -249
  875. package/runtime/scripts/work/grammar-proposal.d.mts +83 -0
  876. package/runtime/scripts/work/grammar-proposal.mjs +215 -178
  877. package/runtime/scripts/work/work-io.d.mts +62 -0
  878. package/runtime/scripts/work/work-io.mjs +82 -63
  879. package/scaffold/add-app.d.mts +15 -0
  880. package/scaffold/add-app.mjs +111 -163
  881. package/scaffold/add-cli-lite.d.mts +8 -0
  882. package/scaffold/add-cli-lite.mjs +173 -207
  883. package/scaffold/add-compose.d.mts +44 -0
  884. package/scaffold/add-compose.mjs +62 -62
  885. package/scaffold/add-database-lite.d.mts +2 -0
  886. package/scaffold/add-database-lite.mjs +10 -10
  887. package/scaffold/add-fe-layout.d.mts +16 -0
  888. package/scaffold/add-fe-layout.mjs +23 -23
  889. package/scaffold/add-fe-overlay.d.mts +17 -0
  890. package/scaffold/add-fe-overlay.mjs +76 -78
  891. package/scaffold/add-fe-page.d.mts +41 -0
  892. package/scaffold/add-fe-page.mjs +84 -89
  893. package/scaffold/add-fe-unit.d.mts +59 -0
  894. package/scaffold/add-fe-unit.mjs +205 -197
  895. package/scaffold/add-inbox-lite.d.mts +17 -0
  896. package/scaffold/add-inbox-lite.mjs +209 -264
  897. package/scaffold/add-plan.d.mts +68 -0
  898. package/scaffold/add-plan.mjs +164 -149
  899. package/scaffold/add-queue-lite.d.mts +34 -0
  900. package/scaffold/add-queue-lite.mjs +267 -267
  901. package/scaffold/add-table.d.mts +16 -0
  902. package/scaffold/add-table.mjs +405 -442
  903. package/scaffold/add.d.mts +34 -0
  904. package/scaffold/add.mjs +260 -246
  905. package/scaffold/app.d.mts +198 -0
  906. package/scaffold/app.mjs +413 -391
  907. package/scaffold/edition-gate.d.mts +49 -0
  908. package/scaffold/edition-gate.mjs +19 -29
  909. package/scaffold/image.d.mts +4 -0
  910. package/scaffold/image.mjs +12 -12
  911. package/scaffold/lite-exports.d.mts +15 -0
  912. package/scaffold/lite-exports.mjs +262 -255
  913. package/scaffold/pattern-file-slots.d.mts +7 -0
  914. package/scaffold/pattern-file-slots.mjs +29 -24
  915. package/scaffold/service.d.mts +60 -0
  916. package/scaffold/service.mjs +297 -279
  917. package/scaffold/test-world.d.mts +31 -0
  918. package/scaffold/test-world.mjs +71 -78
  919. package/scaffold/yaml-block-edit.d.mts +23 -0
  920. package/scaffold/yaml-block-edit.mjs +95 -87
  921. package/scripts/sync-bundles.d.mts +31 -0
  922. package/src/fixtures/bypass-index.d.mts +4 -0
  923. package/src/fixtures/hfs-stack-tooling/passing.d.mts +8 -0
  924. package/src/fixtures/hfs-stack-tooling/violating.d.mts +19 -0
  925. package/src/fixtures/hfs-stacks-full/model-unavailable.d.mts +10 -0
  926. package/src/fixtures/hfs-stacks-full/passing.d.mts +19 -0
  927. package/src/fixtures/hfs-stacks-full/violating.d.mts +10 -0
  928. package/src/fixtures/loosening/adversarial.d.mts +4 -0
  929. package/src/fixtures/loosening/forms.d.mts +16 -0
  930. package/src/fixtures/loosening/swallows.d.mts +5 -0
  931. package/src/helpers/hfs-arch-fixture.d.mts +47 -0
  932. package/src/helpers/hfs-cli-fixture.d.mts +65 -0
  933. package/src/helpers/hfs-hfs-docker-rules-fixture.d.mts +5 -0
  934. package/src/helpers/hfs-secret-fixture.d.mts +19 -0
  935. package/src/helpers/pre-push-fixture.d.mts +21 -0
  936. package/src/helpers/stack-compose.d.mts +6 -0
  937. package/src/helpers/swarm-stack-fixture.d.mts +18 -0
  938. package/src/helpers/tmpdir.d.mts +2 -0
  939. package/src/main.d.mts +58 -0
  940. package/src/main.mjs +493 -348
  941. package/stack/age-install.d.mts +13 -0
  942. package/stack/age-install.mjs +28 -25
  943. package/stack/backup.d.mts +11 -0
  944. package/stack/backup.mjs +76 -69
  945. package/stack/build-args.d.mts +7 -0
  946. package/stack/build-args.mjs +22 -23
  947. package/stack/build.d.mts +8 -0
  948. package/stack/build.mjs +138 -119
  949. package/stack/cli.d.mts +55 -0
  950. package/stack/cli.mjs +187 -130
  951. package/stack/config.d.mts +197 -0
  952. package/stack/config.mjs +47 -33
  953. package/stack/context.d.mts +122 -0
  954. package/stack/context.mjs +63 -65
  955. package/stack/converge.d.mts +13 -0
  956. package/stack/converge.mjs +73 -69
  957. package/stack/deploy.d.mts +88 -0
  958. package/stack/deploy.mjs +272 -224
  959. package/stack/digest.d.mts +8 -0
  960. package/stack/digest.mjs +1 -6
  961. package/stack/docker-transport.d.mts +5 -0
  962. package/stack/docker-transport.mjs +101 -93
  963. package/stack/edge-alias.d.mts +42 -0
  964. package/stack/edge-alias.mjs +62 -60
  965. package/stack/errors.d.mts +18 -0
  966. package/stack/errors.mjs +8 -10
  967. package/stack/image-scan.d.mts +20 -0
  968. package/stack/image-scan.mjs +114 -101
  969. package/stack/ingress-create.d.mts +4 -0
  970. package/stack/ingress-create.mjs +82 -86
  971. package/stack/ingress-publish.d.mts +6 -0
  972. package/stack/ingress-publish.mjs +194 -182
  973. package/stack/ingress.d.mts +55 -0
  974. package/stack/ingress.mjs +534 -524
  975. package/stack/jobs.d.mts +12 -0
  976. package/stack/jobs.mjs +35 -34
  977. package/stack/live-restore.d.mts +35 -0
  978. package/stack/live-restore.mjs +120 -125
  979. package/stack/post-deploy.d.mts +8 -0
  980. package/stack/post-deploy.mjs +25 -25
  981. package/stack/prepare.d.mts +24 -0
  982. package/stack/prepare.mjs +213 -200
  983. package/stack/private-file.d.mts +5 -0
  984. package/stack/private-file.mjs +9 -9
  985. package/stack/provision.d.mts +25 -0
  986. package/stack/provision.mjs +64 -63
  987. package/stack/prune.d.mts +11 -0
  988. package/stack/prune.mjs +38 -27
  989. package/stack/release-store.d.mts +28 -0
  990. package/stack/release-store.mjs +68 -67
  991. package/stack/remote.d.mts +83 -0
  992. package/stack/remote.mjs +176 -162
  993. package/stack/render.d.mts +14 -0
  994. package/stack/render.mjs +132 -124
  995. package/stack/retire.d.mts +9 -0
  996. package/stack/retire.mjs +31 -34
  997. package/stack/sealed.d.mts +46 -0
  998. package/stack/sealed.mjs +78 -82
  999. package/stack/server-key.d.mts +67 -0
  1000. package/stack/server-key.mjs +217 -203
  1001. package/stack/ssh-session.d.mts +29 -0
  1002. package/stack/ssh-session.mjs +133 -123
  1003. package/stack/target-binding.d.mts +21 -0
  1004. package/stack/target-binding.mjs +68 -44
  1005. package/stack/task.d.mts +12 -0
  1006. package/stack/task.mjs +26 -31
  1007. package/stack/urgent-floor.d.mts +106 -0
  1008. package/stack/urgent-floor.mjs +211 -160
  1009. package/stack/urgent-release.d.mts +38 -0
  1010. package/stack/urgent-release.mjs +26 -22
  1011. package/stack/verify.d.mts +14 -0
  1012. package/stack/verify.mjs +75 -65
  1013. package/sync/ci-commands.d.mts +26 -0
  1014. package/sync/ci-commands.mjs +181 -182
  1015. package/sync/cli.d.mts +8 -0
  1016. package/sync/cli.mjs +9 -9
  1017. package/sync/format.d.mts +38 -0
  1018. package/sync/format.mjs +30 -24
  1019. package/sync/hygiene.d.mts +23 -0
  1020. package/sync/hygiene.mjs +113 -99
  1021. package/sync/index.d.mts +237 -0
  1022. package/sync/index.mjs +457 -436
  1023. package/sync/managed.d.mts +23 -0
  1024. package/sync/managed.mjs +104 -94
  1025. package/sync/quality-services.d.mts +35 -0
  1026. package/sync/quality-services.mjs +48 -41
  1027. package/sync/readme-badges.d.mts +13 -0
  1028. package/sync/readme-badges.mjs +45 -44
  1029. package/sync/sonar-key.d.mts +31 -0
  1030. package/sync/sonar-key.mjs +29 -28
  1031. package/sync/stack-images.d.mts +36 -0
  1032. package/sync/stack-images.mjs +85 -95
  1033. package/sync/ts-strict.d.mts +14 -0
  1034. package/sync/ts-strict.mjs +43 -34
  1035. package/templates/app/ci-workflows/github/workflows/ci.yml +1 -1
  1036. package/templates/app/ci-workflows/github/workflows/images.yml +1 -1
  1037. package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +2 -2
  1038. package/templates/app/ci-workflows/github/workflows/stack-key-restore.yml +1 -1
  1039. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +1 -1
  1040. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +3 -3
  1041. package/templates/app/ci-workflows-lite/github/workflows/images.yml +1 -1
  1042. package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +2 -2
  1043. package/templates/app/ci-workflows-lite/github/workflows/stack-key-restore.yml +1 -1
  1044. package/templates/app/codegen-lite/scripts/codegen.mjs +1 -1
  1045. package/templates/app/db-bootstrap/db/bootstrap.sql +1 -1
  1046. package/templates/app/db-image/starcistacks/images/db/Dockerfile +1 -1
  1047. package/templates/app/hooks/husky/pre-commit +2 -2
  1048. package/templates/app/package-scripts/db.json +3 -3
  1049. package/templates/app/package-scripts/package.json +1 -1
  1050. package/templates/app/quality-config/sonar-project.properties +1 -1
  1051. package/templates/app/skeleton/.starcistacks/application-stacks.yaml +2 -2
  1052. package/templates/app/skeleton/.starcistacks/production/README.md +4 -4
  1053. package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +8 -3
  1054. package/templates/app/skeleton/README.md +1 -1
  1055. package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +2 -2
  1056. package/templates/app/skeleton-lite/.starcistacks/dev/README.md +1 -1
  1057. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +6 -2
  1058. package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +3 -2
  1059. package/templates/app/skeleton-lite/.starcistacks/production/README.md +5 -5
  1060. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +7 -2
  1061. package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +3 -2
  1062. package/templates/app/skeleton-lite/README.md +2 -2
  1063. package/templates/be/skeleton/apps/cli/src/compose/platform.ts +1 -1
  1064. package/templates/be/skeleton/apps/cli/src/main.ts +6 -4
  1065. package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +12 -0
  1066. package/templates/be/skeleton/src/modules/platform/database/abstract.entity.ts +19 -0
  1067. package/templates/be/skeleton/src/modules/platform/database/database.module.ts +7 -1
  1068. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +19 -0
  1069. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +4 -3
  1070. package/templates/be/skeleton/src/modules/platform/logging/logging.module.ts +1 -1
  1071. package/templates/be/skeleton/src/modules/platform/logging/logging.options.ts +5 -2
  1072. package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +1 -1
  1073. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.spec.ts +40 -0
  1074. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.ts +1 -0
  1075. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.spec.ts +48 -1
  1076. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.ts +5 -0
  1077. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.options.ts +5 -0
  1078. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +1 -1
  1079. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/oauth-state.store.ts +7 -3
  1080. package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +7 -1
  1081. package/templates/be/table/entity.ts.tpl +4 -15
  1082. package/upgrade/check.d.mts +9 -0
  1083. package/upgrade/check.mjs +8 -8
  1084. package/upgrade/file-state.d.mts +6 -0
  1085. package/upgrade/file-state.mjs +32 -40
  1086. package/upgrade/index.d.mts +13 -0
  1087. package/upgrade/index.mjs +122 -151
  1088. package/vendor-types.d.mts +8 -0
@@ -1,4 +1,4 @@
1
- // app db baseline --squash: the whole active chain becomes ONE baseline read from a database the chain is applied to (--url-env), for a
1
+ // app db baseline --squash: the whole active chain becomes ONE baseline read from a database the chain is applied to (--db-url-env), for a
2
2
  // cutover whose production starts on an empty database. Nothing is rebuilt from migration SQL: the source's schema is dumped with its
3
3
  // privileges, the objects of the extensions the target image lacks (--drop-extension) and every function or view whose body reaches their
4
4
  // schemas are left out by name, the role facts a per-database dump loses go to a marked section of db/bootstrap.sql, and the reference rows
@@ -12,7 +12,6 @@ import { randomBytes } from 'node:crypto';
12
12
  import { DB_DOCKERFILE, DB_IMAGE_CONTEXT } from '../../../db/image.mjs';
13
13
  import { DbVerbError } from './db-errors.mjs';
14
14
  import { BOOTSTRAP_FILE, BOOTSTRAP_SQUASH_BEGIN, BOOTSTRAP_SQUASH_END, SEED_FILE, bootstrapSection } from './rules/database-constants.mjs';
15
-
16
15
  /** The extensions a lite app's db image does not ship, dropped unless --drop-extension names others (pgmq stays: it is the lite event bus). */
17
16
  export const DEFAULT_DROPPED_EXTENSIONS = Object.freeze(['pg_net', 'pg_cron']);
18
17
  export const SEED_HEADER = '-- Generated by starci app db baseline --squash';
@@ -25,69 +24,68 @@ const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);
25
24
  const REPLAY_DB = 'starci_squash_replay';
26
25
  const READY_TIMEOUT_MS = 120_000;
27
26
  const DIFF_LIMIT = 40;
28
-
29
27
  const ident = (name) => `"${String(name).replaceAll('"', '""')}"`;
30
28
  const literal = (value) => `'${String(value).replaceAll("'", "''")}'`;
31
29
  const unquote = (name) => (/^".*"$/s.test(name) ? name.slice(1, -1).replaceAll('""', '"') : name);
32
30
  const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
33
31
  const sleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
34
32
  const sorted = (items) => [...items].sort((left, right) => (left < right ? -1 : Number(left > right)));
35
-
36
33
  /** A comma list flag as distinct names, or `fallback` when the flag is absent. */
37
34
  export const flagList = (value, fallback) => (value === undefined ? [...fallback] : [...new Set(String(value).split(',').map((item) => item.trim()).filter(Boolean))]);
38
-
39
35
  /* ---- docker: every client tool runs in a container of the db image, so the host needs Docker and no PostgreSQL client */
40
-
41
36
  function docker(args, { input, env = {}, secrets = [] } = {}) {
42
- const result = spawnSync('docker', args, { input, env: { ...process.env, ...env }, encoding: 'utf8', maxBuffer: 1 << 30, timeout: 600_000, windowsHide: true });
43
- const scrub = (text) => secrets.filter(Boolean).reduce((out, secret) => out.replaceAll(secret, '[value]'), String(text ?? ''));
44
- return { ok: !result.error && result.status === 0, stdout: String(result.stdout ?? ''), stderr: scrub(result.stderr || result.error?.message) };
37
+ const result = spawnSync('docker', args, { input, env: { ...process.env, ...env }, encoding: 'utf8', maxBuffer: 1 << 30, timeout: 600_000, windowsHide: true });
38
+ const scrub = (text) => secrets.filter(Boolean).reduce((out, secret) => out.replaceAll(secret, '[value]'), String(text ?? ''));
39
+ return { ok: !result.error && result.status === 0, stdout: String(result.stdout ?? ''), stderr: scrub(result.stderr || result.error?.message) };
45
40
  }
46
-
47
41
  const firstLines = (text, count = 4) => String(text).trim().split('\n').filter(Boolean).slice(0, count).join(' | ');
48
-
49
42
  /** A throwaway container of `image` that can reach the host as host.docker.internal; refused without Docker. */
50
43
  function startScratch(image) {
51
- if (!docker(['version', '--format', '{{.Server.Version}}']).ok) throw new DbVerbError('baseline --squash needs Docker for its scratch replay; start it');
52
- const password = randomBytes(18).toString('hex');
53
- const name = `starci-db-squash-${randomBytes(6).toString('hex')}`;
54
- const scratch = { name, password, image, stop: () => docker(['rm', '-f', name]) };
55
- const started = docker(['run', '-d', '--rm', '--name', name, '--label', 'starci.db-squash=1', '--add-host', 'host.docker.internal:host-gateway', '-e', 'POSTGRES_PASSWORD', image], { env: { POSTGRES_PASSWORD: password } });
56
- if (!started.ok) { scratch.stop(); throw new DbVerbError(`docker could not start ${image}: ${firstLines(started.stderr, 1)}`); }
57
- const since = Date.now();
58
- // TCP, not the socket: the image's init server listens on the socket only, and restarts before it serves TCP.
59
- while (!docker(['exec', '-e', 'PGPASSWORD', name, 'psql', '-X', '-h', '127.0.0.1', '-U', 'postgres', '-Atqc', 'select 1'], { env: { PGPASSWORD: password } }).ok) {
60
- if (Date.now() - since > READY_TIMEOUT_MS) { scratch.stop(); throw new DbVerbError(`the scratch database of ${image} did not start`); }
61
- sleep(500);
62
- }
63
- return scratch;
44
+ if (!docker(['version', '--format', '{{.Server.Version}}']).ok)
45
+ throw new DbVerbError('baseline --squash needs Docker for its scratch replay; start it');
46
+ const password = randomBytes(18).toString('hex');
47
+ const name = `starci-db-squash-${randomBytes(6).toString('hex')}`;
48
+ const scratch = { name, password, image, stop: () => docker(['rm', '-f', name]) };
49
+ const started = docker(['run', '-d', '--rm', '--name', name, '--label', 'starci.db-squash=1', '--add-host', 'host.docker.internal:host-gateway', '-e', 'POSTGRES_PASSWORD', image], { env: { POSTGRES_PASSWORD: password } });
50
+ if (!started.ok) {
51
+ scratch.stop();
52
+ throw new DbVerbError(`docker could not start ${image}: ${firstLines(started.stderr, 1)}`);
53
+ }
54
+ const since = Date.now();
55
+ // TCP, not the socket: the image's init server listens on the socket only, and restarts before it serves TCP.
56
+ while (!docker(['exec', '-e', 'PGPASSWORD', name, 'psql', '-X', '-h', '127.0.0.1', '-U', 'postgres', '-Atqc', 'select 1'], { env: { PGPASSWORD: password } }).ok) {
57
+ if (Date.now() - since > READY_TIMEOUT_MS) {
58
+ scratch.stop();
59
+ throw new DbVerbError(`the scratch database of ${image} did not start`);
60
+ }
61
+ sleep(500);
62
+ }
63
+ return scratch;
64
64
  }
65
-
66
65
  /** The app's own db image, built from its managed Dockerfile (DB_DOCKERFILE); its id names it. */
67
66
  function buildImage(root) {
68
- if (!docker(['version', '--format', '{{.Server.Version}}']).ok) throw new DbVerbError('baseline --squash needs Docker for its scratch replay; start it');
69
- const build = docker(['build', '--quiet', '--file', path.join(root, DB_DOCKERFILE), path.join(root, DB_IMAGE_CONTEXT)]);
70
- const image = /sha256:[a-f0-9]{64}/.exec(build.stdout)?.[0];
71
- if (!build.ok || !image) throw new DbVerbError(`docker could not build ${DB_DOCKERFILE}: ${firstLines(build.stderr, 2)}`);
72
- return image;
67
+ if (!docker(['version', '--format', '{{.Server.Version}}']).ok)
68
+ throw new DbVerbError('baseline --squash needs Docker for its scratch replay; start it');
69
+ const build = docker(['build', '--quiet', '--file', path.join(root, DB_DOCKERFILE), path.join(root, DB_IMAGE_CONTEXT)]);
70
+ const image = /sha256:[a-f0-9]{64}/.exec(build.stdout)?.[0];
71
+ if (!build.ok || !image)
72
+ throw new DbVerbError(`docker could not build ${DB_DOCKERFILE}: ${firstLines(build.stderr, 2)}`);
73
+ return image;
73
74
  }
74
-
75
75
  /** Where a target's client tools run: `docker exec` in the scratch container, or a one-shot container of its image on the host's network. */
76
76
  const inScratch = (scratch) => ({ head: ['exec', '-i'], ref: scratch.name });
77
77
  const onHostNetwork = (scratch) => ({ head: ['run', '--rm', '-i', '--network', 'host', '--label', 'starci.db-squash=1'], ref: scratch.image });
78
-
79
78
  const scratchTarget = (scratch, database) => ({ url: `postgresql://postgres@127.0.0.1:5432/${database}`, password: scratch.password, label: `the scratch ${database}`, client: inScratch(scratch) });
80
-
81
79
  function exec(target, tool, args, { input, query = false } = {}) {
82
- const env = { PGPASSWORD: target.password, ...(query ? { PGOPTIONS: QUERY_OPTIONS } : {}) };
83
- const result = docker([...target.client.head, '-e', 'PGPASSWORD', ...(query ? ['-e', 'PGOPTIONS'] : []), target.client.ref, tool, ...args], { input, env, secrets: [target.password, target.url] });
84
- if (!result.ok) throw new DbVerbError(`${tool} against ${target.label} failed: ${firstLines(result.stderr)}`);
85
- return result.stdout;
80
+ const env = { PGPASSWORD: target.password, ...(query ? { PGOPTIONS: QUERY_OPTIONS } : {}) };
81
+ const result = docker([...target.client.head, '-e', 'PGPASSWORD', ...(query ? ['-e', 'PGOPTIONS'] : []), target.client.ref, tool, ...args], { input, env, secrets: [target.password, target.url] });
82
+ if (!result.ok)
83
+ throw new DbVerbError(`${tool} against ${target.label} failed: ${firstLines(result.stderr)}`);
84
+ return result.stdout;
86
85
  }
87
-
88
86
  const psqlArgs = (target) => ['-X', '-q', '-v', 'ON_ERROR_STOP=1', '-d', target.url];
87
+ // reason: the rows psql returns as JSON; each call site names the shape it expects.
89
88
  const queryJson = (target, sql) => JSON.parse(exec(target, 'psql', [...psqlArgs(target), '-A', '-t', '-c', sql], { query: true }));
90
-
91
89
  /**
92
90
  * The source as a container reaches it. A remote host is reached from the scratch container. A loopback host is the host's own: Docker
93
91
  * Desktop reaches it from the scratch container as host.docker.internal, a Linux engine only from a container on the host's network (its
@@ -95,29 +93,33 @@ const queryJson = (target, sql) => JSON.parse(exec(target, 'psql', [...psqlArgs(
95
93
  * when none does, the verb refuses with each route's failure.
96
94
  */
97
95
  export function sourceTarget(scratch, connection, label) {
98
- const password = connection.env.PGPASSWORD ?? '';
99
- const url = new URL(connection.url);
100
- if (!LOCAL_HOSTS.has(url.hostname)) return { url: url.toString(), password, label, client: inScratch(scratch) };
101
- const gateway = new URL(connection.url);
102
- gateway.hostname = 'host.docker.internal';
103
- const routes = [{ url: gateway.toString(), password, label, client: inScratch(scratch) }, { url: url.toString(), password, label, client: onHostNetwork(scratch) }];
104
- const failures = [];
105
- for (const route of routes) {
106
- try { exec(route, 'psql', [...psqlArgs(route), '-At', '-c', 'select 1']); return route; } catch (error) { failures.push(error.message); }
107
- }
108
- throw new DbVerbError(`no container of ${scratch.image} reaches ${label}:\n${failures.map((line) => ` ${line}`).join('\n')}`);
96
+ const password = connection.env.PGPASSWORD ?? '';
97
+ const url = new URL(connection.url);
98
+ if (!LOCAL_HOSTS.has(url.hostname))
99
+ return { url: url.toString(), password, label, client: inScratch(scratch) };
100
+ const gateway = new URL(connection.url);
101
+ gateway.hostname = 'host.docker.internal';
102
+ const routes = [{ url: gateway.toString(), password, label, client: inScratch(scratch) }, { url: url.toString(), password, label, client: onHostNetwork(scratch) }];
103
+ const failures = [];
104
+ for (const route of routes) {
105
+ try {
106
+ exec(route, 'psql', [...psqlArgs(route), '-At', '-c', 'select 1']);
107
+ return route;
108
+ }
109
+ catch (error) {
110
+ failures.push(error.message);
111
+ }
112
+ }
113
+ throw new DbVerbError(`no container of ${scratch.image} reaches ${label}:\n${failures.map((line) => ` ${line}`).join('\n')}`);
109
114
  }
110
-
111
115
  /** The runPostgres port of db-baseline-verb.mjs, served by the target's container: the history precondition needs no host psql. */
112
116
  export function targetRunner(target, commandOf) {
113
- return (root, op) => {
114
- const [tool, args] = commandOf(op, target.url);
115
- return { status: 0, stdout: exec(target, tool, args) };
116
- };
117
+ return (_root, op) => {
118
+ const [tool, args] = commandOf(op, target.url);
119
+ return { status: 0, stdout: exec(target, tool, args) };
120
+ };
117
121
  }
118
-
119
122
  /* ---- the source facts: one read-only catalog query */
120
-
121
123
  const notMember = (classid, column) => `not exists (select 1 from pg_catalog.pg_depend d where d.classid = '${classid}'::pg_catalog.regclass and d.objid = ${column} and d.deptype = 'e')`;
122
124
  const userSchema = (column) => `${column} !~ '^pg_' and ${column} <> 'information_schema'`;
123
125
  const FACTS_SQL = `select pg_catalog.json_build_object(
@@ -142,236 +144,268 @@ const FACTS_SQL = `select pg_catalog.json_build_object(
142
144
  'views', (select coalesce(json_agg(json_build_object('schema', n.nspname, 'name', c.relname, 'kind', c.relkind, 'text', pg_catalog.pg_get_viewdef(c.oid)) order by n.nspname, c.relname), '[]')
143
145
  from pg_catalog.pg_class c join pg_catalog.pg_namespace n on n.oid = c.relnamespace
144
146
  where c.relkind in ('v', 'm') and ${userSchema('n.nspname')} and ${notMember('pg_catalog.pg_class', 'c.oid')}))`;
145
-
146
147
  const QUEUES_SQL = `select coalesce(json_agg(json_build_object('name', m.queue_name, 'partitioned', m.is_partitioned, 'unlogged', m.is_unlogged) order by m.queue_name), '[]') from pgmq.meta m`;
147
-
148
148
  /** Whether a queue name matches one --drop-pgmq-queue entry: a name, or a pattern where `*` matches any run of characters. */
149
149
  export const queueDropped = (name, patterns) => patterns.some((pattern) => new RegExp(`^${pattern.split('*').map(escape).join('.*')}$`).test(name));
150
-
151
150
  /* ---- the dump as entries */
152
-
153
151
  const ENTRY = /^-- (?:Data for )?Name: (.*); Type: (.*); Schema: (.*); Owner: (.*)$/;
154
152
  const LOOSE_SET = /^SET default_(?:tablespace|table_access_method) = .*;$/;
155
-
156
153
  /** A plain pg_dump as { preamble, entries: [{ name, type, schema, body }] }; psql's \restrict guard and the version comments are dropped. */
157
154
  export function dumpEntries(text) {
158
- const lines = String(text).replace(/\r\n/g, '\n').split('\n').filter((line) => !/^\\(?:un)?restrict\b/.test(line));
159
- const starts = lines.flatMap((line, index) => (ENTRY.test(line) && lines[index - 1] === '--' && lines[index + 1] === '--' ? [index] : []));
160
- const preamble = lines.slice(0, starts.length ? starts[0] - 1 : lines.length).filter((line) => /^(?:SET|SELECT) /.test(line));
161
- const entries = starts.map((start, index) => {
162
- const [, name, type, schema] = ENTRY.exec(lines[start]);
163
- const end = index + 1 < starts.length ? starts[index + 1] - 1 : lines.length;
164
- const body = lines.slice(start + 2, end).filter((line) => {
165
- if (!LOOSE_SET.test(line)) return true;
166
- if (!preamble.includes(line)) preamble.push(line);
167
- return false;
168
- }).join('\n').replace(/\n--\n-- PostgreSQL database dump complete\n--\s*$/, '').trim();
169
- return { name, type, schema, body };
170
- });
171
- return { preamble, entries };
155
+ const lines = String(text).replace(/\r\n/g, '\n').split('\n').filter((line) => !/^\\(?:un)?restrict\b/.test(line));
156
+ const starts = lines.flatMap((line, index) => (ENTRY.test(line) && lines[index - 1] === '--' && lines[index + 1] === '--' ? [index] : []));
157
+ const preamble = lines.slice(0, starts.length ? starts[0] - 1 : lines.length).filter((line) => /^(?:SET|SELECT) /.test(line));
158
+ const entries = starts.map((start, index) => {
159
+ const [, name, type, schema] = ENTRY.exec(lines[start]);
160
+ const end = index + 1 < starts.length ? starts[index + 1] - 1 : lines.length;
161
+ const body = lines.slice(start + 2, end).filter((line) => {
162
+ if (!LOOSE_SET.test(line))
163
+ return true;
164
+ if (!preamble.includes(line))
165
+ preamble.push(line);
166
+ return false;
167
+ }).join('\n').replace(/\n--\n-- PostgreSQL database dump complete\n--\s*$/, '').trim();
168
+ return { name, type, schema, body };
169
+ });
170
+ return { preamble, entries };
172
171
  }
173
-
174
172
  const entryKey = (entry) => `${entry.type}|${entry.schema}|${entry.name}`;
175
173
  const renderEntry = (entry) => `--\n-- Name: ${entry.name}; Type: ${entry.type}; Schema: ${entry.schema}; Owner: -\n--\n\n${entry.body}\n\n\n`;
176
-
177
174
  const ROLE_NAMES = /\b(?:TO|FROM|FOR ROLE|SESSION AUTHORIZATION)\s+((?:"(?:[^"]|"")+"|[\w$]+)(?:\s*,\s*(?:"(?:[^"]|"")+"|[\w$]+))*)/gi;
178
175
  /** The roles a statement grants to, revokes from, acts for or switches to. */
179
176
  const rolesOf = (text) => [...String(text).matchAll(ROLE_NAMES)].flatMap((match) => match[1].split(/\s*,\s*/).map(unquote));
180
-
181
177
  /* ---- the projection: what a squash of one database writes */
182
-
183
178
  /** The names a bootstrap's own SQL creates: its schemas, roles and publications. */
184
179
  export function bootstrapDeclares(head) {
185
- const created = (kind) => new Set([...head.matchAll(new RegExp(`create\\s+${kind}\\s+(?:if\\s+not\\s+exists\\s+)?("(?:[^"]|"")+"|[\\w$]+)`, 'gi'))].map((match) => unquote(match[1])));
186
- return { schemas: created('schema'), roles: created('role'), publications: created('publication') };
180
+ const created = (kind) => new Set([...head.matchAll(new RegExp(`create\\s+${kind}\\s+(?:if\\s+not\\s+exists\\s+)?("(?:[^"]|"")+"|[\\w$]+)`, 'gi'))].map((match) => unquote(match[1])));
181
+ return { schemas: created('schema'), roles: created('role'), publications: created('publication') };
187
182
  }
188
-
189
183
  function ownerOf(entry) {
190
- if (entry.schema !== '-') return { schema: entry.schema };
191
- const target = /^(SCHEMA|EXTENSION) (.+)$/.exec(entry.name);
192
- if (entry.type === 'SCHEMA') return { schema: unquote(entry.name) };
193
- if (entry.type === 'EXTENSION') return { extension: unquote(entry.name) };
194
- if (['ACL', 'COMMENT'].includes(entry.type) && target) return target[1] === 'SCHEMA' ? { schema: unquote(target[2]), schemaLevel: true } : { extension: unquote(target[2]) };
195
- if (entry.type === 'DEFAULT ACL') return { global: true };
196
- if (entry.type === 'PUBLICATION') return { publication: unquote(entry.name) };
197
- if (entry.type === 'EVENT TRIGGER') return { eventTrigger: unquote(/EXECUTE FUNCTION\s+("(?:[^"]|"")+"|[\w$]+)\./i.exec(entry.body)?.[1] ?? '') };
198
- return { database: true };
184
+ if (entry.schema !== '-')
185
+ return { schema: entry.schema };
186
+ const target = /^(SCHEMA|EXTENSION) (.+)$/.exec(entry.name);
187
+ if (entry.type === 'SCHEMA')
188
+ return { schema: unquote(entry.name) };
189
+ if (entry.type === 'EXTENSION')
190
+ return { extension: unquote(entry.name) };
191
+ if (['ACL', 'COMMENT'].includes(entry.type) && target)
192
+ return target[1] === 'SCHEMA' ? { schema: unquote(target[2]), schemaLevel: true } : { extension: unquote(target[2]) };
193
+ if (entry.type === 'DEFAULT ACL')
194
+ return { global: true };
195
+ if (entry.type === 'PUBLICATION')
196
+ return { publication: unquote(entry.name) };
197
+ if (entry.type === 'EVENT TRIGGER')
198
+ return { eventTrigger: unquote(/EXECUTE FUNCTION\s+("(?:[^"]|"")+"|[\w$]+)\./i.exec(entry.body)?.[1] ?? '') };
199
+ return { database: true };
199
200
  }
200
-
201
201
  function routineKeys(routine) {
202
- const kind = { p: 'PROCEDURE', a: 'AGGREGATE' }[routine.kind] ?? 'FUNCTION';
203
- const signature = `${routine.name}(${routine.args})`;
204
- return [`${kind}|${routine.schema}|${routine.name}(${routine.types})`, `ACL|${routine.schema}|${kind} ${signature}`, `COMMENT|${routine.schema}|${kind} ${signature}`];
202
+ const kind = { p: 'PROCEDURE', a: 'AGGREGATE' }[routine.kind] ?? 'FUNCTION';
203
+ const signature = `${routine.name}(${routine.args})`;
204
+ return [`${kind}|${routine.schema}|${routine.name}(${routine.types})`, `ACL|${routine.schema}|${kind} ${signature}`, `COMMENT|${routine.schema}|${kind} ${signature}`];
205
205
  }
206
-
207
206
  function viewKeys(view) {
208
- const kind = view.kind === 'm' ? 'MATERIALIZED VIEW' : 'VIEW';
209
- return [`${kind}|${view.schema}|${view.name}`, `ACL|${view.schema}|TABLE ${view.name}`, `COMMENT|${view.schema}|${kind} ${view.name}`, `MATERIALIZED VIEW DATA|${view.schema}|${view.name}`];
207
+ const kind = view.kind === 'm' ? 'MATERIALIZED VIEW' : 'VIEW';
208
+ return [`${kind}|${view.schema}|${view.name}`, `ACL|${view.schema}|TABLE ${view.name}`, `COMMENT|${view.schema}|${kind} ${view.name}`, `MATERIALIZED VIEW DATA|${view.schema}|${view.name}`];
210
209
  }
211
-
212
210
  /** Which schemas are the app's, which belong to a dropped extension, and which objects reach the dropped ones. */
213
211
  function squashScope(facts, config) {
214
- const kept = facts.extensions.filter((extension) => !config.dropped.includes(extension.name));
215
- const keptHomes = new Set(kept.map((extension) => extension.schema));
216
- const droppedSchemas = new Set();
217
- for (const extension of facts.extensions.filter((item) => config.dropped.includes(item.name))) {
218
- for (const member of extension.members) droppedSchemas.add(member);
219
- if (!['public', 'pg_catalog'].includes(extension.schema) && !keptHomes.has(extension.schema) && !config.bootstrap.schemas.has(extension.schema)) droppedSchemas.add(extension.schema);
220
- }
221
- const appSchemas = new Set(facts.schemas.filter((schema) => !config.managedSchema(schema) && !droppedSchemas.has(schema) && !config.bootstrap.schemas.has(schema) && !keptHomes.has(schema)));
222
- // A kept extension's own schema (pgmq) is created by the baseline before its extension; grants on it follow in the baseline's tail.
223
- const extensionHomes = new Set([...keptHomes].filter((schema) => !['public', 'pg_catalog'].includes(schema) && !config.bootstrap.schemas.has(schema)));
224
- const reaches = droppedSchemas.size ? new RegExp(`(?<![\\w$."])"?(?:${sorted(droppedSchemas).map(escape).join('|')})"?\\s*\\.`, 'i') : null;
225
- const excluded = [
226
- ...facts.routines.filter((item) => appSchemas.has(item.schema) && reaches?.test(item.text)).map((item) => ({ label: `${item.schema}.${item.name}(${item.args})`, schema: item.schema, name: item.name, keys: routineKeys(item), call: true })),
227
- ...facts.views.filter((item) => appSchemas.has(item.schema) && reaches?.test(item.text)).map((item) => ({ label: `${item.schema}.${item.name}`, schema: item.schema, name: item.name, keys: viewKeys(item), call: false })),
228
- ];
229
- return { kept, appSchemas, extensionHomes, droppedSchemas, reaches, excluded };
212
+ const kept = facts.extensions.filter((extension) => !config.dropped.includes(extension.name));
213
+ const keptHomes = new Set(kept.map((extension) => extension.schema));
214
+ const droppedSchemas = new Set();
215
+ for (const extension of facts.extensions.filter((item) => config.dropped.includes(item.name))) {
216
+ for (const member of extension.members)
217
+ droppedSchemas.add(member);
218
+ if (!['public', 'pg_catalog'].includes(extension.schema) && !keptHomes.has(extension.schema) && !config.bootstrap.schemas.has(extension.schema))
219
+ droppedSchemas.add(extension.schema);
220
+ }
221
+ const appSchemas = new Set(facts.schemas.filter((schema) => !config.managedSchema(schema) && !droppedSchemas.has(schema) && !config.bootstrap.schemas.has(schema) && !keptHomes.has(schema)));
222
+ // A kept extension's own schema (pgmq) is created by the baseline before its extension; grants on it follow in the baseline's tail.
223
+ const extensionHomes = new Set([...keptHomes].filter((schema) => !['public', 'pg_catalog'].includes(schema) && !config.bootstrap.schemas.has(schema)));
224
+ const reaches = droppedSchemas.size ? new RegExp(`(?<![\\w$."])"?(?:${sorted(droppedSchemas).map(escape).join('|')})"?\\s*\\.`, 'i') : null;
225
+ const excluded = [
226
+ ...facts.routines.filter((item) => appSchemas.has(item.schema) && reaches?.test(item.text)).map((item) => ({ label: `${item.schema}.${item.name}(${item.args})`, schema: item.schema, name: item.name, keys: routineKeys(item), call: true })),
227
+ ...facts.views.filter((item) => appSchemas.has(item.schema) && reaches?.test(item.text)).map((item) => ({ label: `${item.schema}.${item.name}`, schema: item.schema, name: item.name, keys: viewKeys(item), call: false })),
228
+ ];
229
+ return { kept, appSchemas, extensionHomes, droppedSchemas, reaches, excluded, droppedQueues: new Set() };
230
230
  }
231
-
232
231
  /** Kept entries naming a dropped schema or an excluded object: each pair is a refusal line. */
233
232
  function danglingReferences(entries, scope) {
234
- const lines = [];
235
- for (const entry of entries) {
236
- const where = `${entry.type} ${entry.schema === '-' ? '' : `${entry.schema}.`}${entry.name}`;
237
- const schema = scope.reaches?.exec(entry.body);
238
- if (schema) lines.push(`${where} references the dropped schema ${schema[0].replace(/[\s.]+$/, '')}`);
239
- for (const object of scope.excluded) {
240
- const qualified = new RegExp(`(?<![\\w$])"?${escape(object.schema)}"?\\."?${escape(object.name)}"?(?![\\w$])`, 'i');
241
- const bare = object.call && /^(?:FUNCTION|PROCEDURE|VIEW|MATERIALIZED VIEW|TRIGGER|POLICY)$/.test(entry.type) ? new RegExp(`(?<![\\w$."])${escape(object.name)}\\s*\\(`, 'i') : null;
242
- if (qualified.test(entry.body) || bare?.test(entry.body)) lines.push(`${where} references the excluded ${object.label}`);
233
+ const lines = [];
234
+ for (const entry of entries) {
235
+ const where = `${entry.type} ${entry.schema === '-' ? '' : `${entry.schema}.`}${entry.name}`;
236
+ const schema = scope.reaches?.exec(entry.body);
237
+ if (schema)
238
+ lines.push(`${where} references the dropped schema ${schema[0].replace(/[\s.]+$/, '')}`);
239
+ for (const object of scope.excluded) {
240
+ const qualified = new RegExp(`(?<![\\w$])"?${escape(object.schema)}"?\\."?${escape(object.name)}"?(?![\\w$])`, 'i');
241
+ const bare = object.call && /^(?:FUNCTION|PROCEDURE|VIEW|MATERIALIZED VIEW|TRIGGER|POLICY)$/.test(entry.type) ? new RegExp(`(?<![\\w$."])${escape(object.name)}\\s*\\(`, 'i') : null;
242
+ if (qualified.test(entry.body) || bare?.test(entry.body))
243
+ lines.push(`${where} references the excluded ${object.label}`);
244
+ }
245
+ for (const match of entry.body.matchAll(/\bpgmq\s*\.\s*\w+\s*\(\s*'((?:[^']|'')+)'/gi)) {
246
+ const queue = match[1].replaceAll("''", "'");
247
+ if (scope.droppedQueues.has(queue))
248
+ lines.push(`${where} uses the dropped pgmq queue ${queue}`);
249
+ }
243
250
  }
244
- for (const match of entry.body.matchAll(/\bpgmq\s*\.\s*\w+\s*\(\s*'((?:[^']|'')+)'/gi)) {
245
- const queue = match[1].replaceAll("''", "'");
246
- if (scope.droppedQueues.has(queue)) lines.push(`${where} uses the dropped pgmq queue ${queue}`);
247
- }
248
- }
249
- return lines;
251
+ return lines;
250
252
  }
251
-
252
253
  function roleSql(role, existing) {
253
- const attributes = [role.login ? 'login' : 'nologin', role.inherit ? 'inherit' : 'noinherit', role.createdb ? 'createdb' : 'nocreatedb',
254
- role.createrole ? 'createrole' : 'nocreaterole', role.bypassrls ? 'bypassrls' : 'nobypassrls', `connection limit ${role.connlimit}`].join(' ');
255
- return [` if not exists (select 1 from pg_catalog.pg_roles where rolname = ${literal(role.name)}) then create role ${ident(role.name)}; end if;`,
256
- existing ? null : `alter role ${ident(role.name)} with ${attributes};`];
254
+ const attributes = [role.login ? 'login' : 'nologin', role.inherit ? 'inherit' : 'noinherit', role.createdb ? 'createdb' : 'nocreatedb',
255
+ role.createrole ? 'createrole' : 'nocreaterole', role.bypassrls ? 'bypassrls' : 'nobypassrls', `connection limit ${role.connlimit}`].join(' ');
256
+ return [` if not exists (select 1 from pg_catalog.pg_roles where rolname = ${literal(role.name)}) then create role ${ident(role.name)}; end if;`,
257
+ existing ? null : `alter role ${ident(role.name)} with ${attributes};`];
257
258
  }
258
-
259
259
  function settingSql(role, entry, refusals) {
260
- const at = entry.indexOf('=');
261
- const [key, value] = [entry.slice(0, at), entry.slice(at + 1)];
262
- if (!ROLE_SETTINGS.has(key)) { refusals.push(`role ${role} carries the setting ${key}, which a squash does not carry (allowed: ${sorted(ROLE_SETTINGS).join(', ')})`); return null; }
263
- if (key !== 'search_path') return `alter role ${ident(role)} set ${key} = ${literal(value)};`;
264
- if (!/^[\w$", ]+$/.test(value)) { refusals.push(`role ${role} has a search_path the squash cannot restate: ${value}`); return null; }
265
- return `alter role ${ident(role)} set search_path = ${value};`;
260
+ const at = entry.indexOf('=');
261
+ const [key, value] = [entry.slice(0, at), entry.slice(at + 1)];
262
+ if (!ROLE_SETTINGS.has(key)) {
263
+ refusals.push(`role ${role} carries the setting ${key}, which a squash does not carry (allowed: ${sorted(ROLE_SETTINGS).join(', ')})`);
264
+ return null;
265
+ }
266
+ if (key !== 'search_path')
267
+ return `alter role ${ident(role)} set ${key} = ${literal(value)};`;
268
+ if (!/^[\w$", ]+$/.test(value)) {
269
+ refusals.push(`role ${role} has a search_path the squash cannot restate: ${value}`);
270
+ return null;
271
+ }
272
+ return `alter role ${ident(role)} set search_path = ${value};`;
266
273
  }
267
-
268
274
  /** The bootstrap section: carried roles, their memberships and settings, schema-level grants on bootstrap schemas, database-wide default privileges. */
269
275
  function sectionText({ facts, carried, bootstrapRoles, schemaGrants, defaultGrants, refusals }) {
270
- const roles = facts.roles.filter((role) => carried.has(role.name));
271
- const create = roles.map((role) => roleSql(role, false));
272
- const known = new Set([...carried, ...bootstrapRoles]);
273
- const members = facts.members.filter((item) => known.has(item.role) && known.has(item.member)).map((item) => `grant ${ident(item.role)} to ${ident(item.member)};`);
274
- const settings = facts.settings.filter((item) => known.has(item.role)).flatMap((item) => item.config.map((entry) => settingSql(item.role, entry, refusals))).filter(Boolean);
275
- const body = [
276
- '-- The source\'s app roles (passwords are set out of band), their memberships and role-level settings, the grants on schemas this bootstrap',
277
- '-- creates, and the database-wide default privileges: a per-database dump keeps none of them.',
278
- ...(create.length ? ['do $starci_squash$', 'begin', ...create.map(([line]) => line), 'end', '$starci_squash$;', ...create.map(([, line]) => line)] : []),
279
- ...members, ...settings, ...schemaGrants, ...defaultGrants,
280
- ];
281
- return `${BOOTSTRAP_SQUASH_BEGIN}\n${body.join('\n')}\n${BOOTSTRAP_SQUASH_END}\n`;
276
+ const roles = facts.roles.filter((role) => carried.has(role.name));
277
+ const create = roles.map((role) => roleSql(role, false));
278
+ const known = new Set([...carried, ...bootstrapRoles]);
279
+ const members = facts.members.filter((item) => known.has(item.role) && known.has(item.member)).map((item) => `grant ${ident(item.role)} to ${ident(item.member)};`);
280
+ const settings = facts.settings.filter((item) => known.has(item.role)).flatMap((item) => item.config.map((entry) => settingSql(item.role, entry, refusals))).filter(Boolean);
281
+ const body = [
282
+ '-- The source\'s app roles (passwords are set out of band), their memberships and role-level settings, the grants on schemas this bootstrap',
283
+ '-- creates, and the database-wide default privileges: a per-database dump keeps none of them.',
284
+ ...(create.length ? ['do $starci_squash$', 'begin', ...create.map(([line]) => line), 'end', '$starci_squash$;', ...create.map(([, line]) => line)] : []),
285
+ ...members, ...settings, ...schemaGrants, ...defaultGrants,
286
+ ];
287
+ return `${BOOTSTRAP_SQUASH_BEGIN}\n${body.join('\n')}\n${BOOTSTRAP_SQUASH_END}\n`;
282
288
  }
283
-
284
289
  /** Partition one dump: baseline entries, statements for the bootstrap section and the baseline tail, and what was left out and why. */
285
- function partition(dump, facts, scope, config) {
286
- const excludedKeys = new Map(scope.excluded.flatMap((object) => object.keys.map((key) => [key, object])));
287
- const report = { extensions: [], objects: new Set(), eventTriggers: [], platformSchemas: new Map(), platformStatements: 0, ownerGrants: 0, lostGrants: [] };
288
- const kept = [];
289
- const bootstrapAcl = [];
290
- const extensionAcl = [];
291
- const globalAcl = [];
292
- for (const entry of dump.entries) {
293
- const owner = ownerOf(entry);
294
- if (owner.extension !== undefined) {
295
- if (config.dropped.includes(owner.extension) || owner.extension === 'plpgsql') { if (entry.type === 'EXTENSION') report.extensions.push(owner.extension); }
296
- else kept.push(entry);
297
- } else if (owner.global) globalAcl.push(entry);
298
- else if (owner.publication !== undefined) {
299
- // An empty publication (Supabase's supabase_realtime) is platform state: lite realtime is LISTEN/NOTIFY plus Redis.
300
- const publishes = /\bFOR\s+(?:ALL\s+)?TABLES?\b/i.test(entry.body) || dump.entries.some((other) => other.type.startsWith('PUBLICATION TABLE') && other.name.startsWith(`${entry.name} `));
301
- if (publishes && !config.bootstrap.publications.has(owner.publication)) kept.push(entry);
302
- else if (!publishes) report.objects.add(`publication ${owner.publication} (publishes no table)`);
290
+ function partition(dump, scope, config) {
291
+ const excludedKeys = new Map(scope.excluded.flatMap((object) => object.keys.map((key) => [key, object])));
292
+ const report = { extensions: [], objects: new Set(), eventTriggers: [], platformSchemas: new Map(), platformStatements: 0, ownerGrants: 0, lostGrants: [], droppedQueues: [], queues: [] };
293
+ const kept = [];
294
+ const bootstrapAcl = [];
295
+ const extensionAcl = [];
296
+ const globalAcl = [];
297
+ for (const entry of dump.entries) {
298
+ const owner = ownerOf(entry);
299
+ if (owner.extension !== undefined) {
300
+ if (config.dropped.includes(owner.extension) || owner.extension === 'plpgsql') {
301
+ if (entry.type === 'EXTENSION')
302
+ report.extensions.push(owner.extension);
303
+ }
304
+ else
305
+ kept.push(entry);
306
+ }
307
+ else if (owner.global)
308
+ globalAcl.push(entry);
309
+ else if (owner.publication !== undefined) {
310
+ // An empty publication (Supabase's supabase_realtime) is platform state: lite realtime is LISTEN/NOTIFY plus Redis.
311
+ const publishes = /\bFOR\s+(?:ALL\s+)?TABLES?\b/i.test(entry.body) || dump.entries.some((other) => other.type.startsWith('PUBLICATION TABLE') && other.name.startsWith(`${entry.name} `));
312
+ if (publishes && !config.bootstrap.publications.has(owner.publication))
313
+ kept.push(entry);
314
+ else if (!publishes)
315
+ report.objects.add(`publication ${owner.publication} (publishes no table)`);
316
+ }
317
+ else if (owner.eventTrigger !== undefined) {
318
+ if (scope.appSchemas.has(owner.eventTrigger))
319
+ kept.push(entry);
320
+ else
321
+ report.eventTriggers.push(unquote(entry.name));
322
+ }
323
+ else if (owner.database || scope.appSchemas.has(owner.schema)) {
324
+ const object = excludedKeys.get(entryKey(entry));
325
+ if (object)
326
+ report.objects.add(`${object.label} (reaches ${sorted(scope.droppedSchemas).join(', ')})`);
327
+ else
328
+ kept.push(entry);
329
+ }
330
+ else if (scope.extensionHomes.has(owner.schema)) {
331
+ if (entry.type === 'SCHEMA')
332
+ kept.push(entry);
333
+ else if (entry.type === 'ACL')
334
+ extensionAcl.push({ entry, schemaLevel: false });
335
+ }
336
+ else if (config.bootstrap.schemas.has(owner.schema) && entry.type === 'ACL')
337
+ bootstrapAcl.push({ entry, schemaLevel: owner.schemaLevel === true });
338
+ else {
339
+ report.platformSchemas.set(owner.schema, (report.platformSchemas.get(owner.schema) ?? 0) + 1);
340
+ // reason: lostGrants holds { line, schema } here and is narrowed to lines in project().
341
+ if (entry.type === 'ACL')
342
+ report.lostGrants.push(...entry.body.split('\n').filter((line) => /^(?:GRANT|REVOKE)\b/.test(line)).map((line) => ({ line, schema: owner.schema })));
343
+ }
303
344
  }
304
- else if (owner.eventTrigger !== undefined) { if (scope.appSchemas.has(owner.eventTrigger)) kept.push(entry); else report.eventTriggers.push(unquote(entry.name)); }
305
- else if (owner.database || scope.appSchemas.has(owner.schema)) {
306
- const object = excludedKeys.get(entryKey(entry));
307
- if (object) report.objects.add(`${object.label} (reaches ${sorted(scope.droppedSchemas).join(', ')})`);
308
- else kept.push(entry);
309
- } else if (scope.extensionHomes.has(owner.schema)) {
310
- if (entry.type === 'SCHEMA') kept.push(entry);
311
- else if (entry.type === 'ACL') extensionAcl.push({ entry, schemaLevel: false });
312
- } else if (config.bootstrap.schemas.has(owner.schema) && entry.type === 'ACL') bootstrapAcl.push({ entry, schemaLevel: owner.schemaLevel === true });
313
- else {
314
- report.platformSchemas.set(owner.schema, (report.platformSchemas.get(owner.schema) ?? 0) + 1);
315
- if (entry.type === 'ACL') report.lostGrants.push(...entry.body.split('\n').filter((line) => /^(?:GRANT|REVOKE)\b/.test(line)).map((line) => ({ line, schema: owner.schema })));
316
- }
317
- }
318
- return { kept, bootstrapAcl, extensionAcl, globalAcl, report };
345
+ return { kept, bootstrapAcl, extensionAcl, globalAcl, report };
319
346
  }
320
-
321
347
  /** One database projected to the files a squash writes; the same function reads the source and the replay. */
322
348
  function project(target, config) {
323
- const facts = queryJson(target, FACTS_SQL);
324
- const scope = squashScope(facts, config);
325
- const queues = facts.extensions.some((extension) => extension.name === 'pgmq') && !config.dropped.includes('pgmq') ? queryJson(target, QUEUES_SQL) : [];
326
- scope.droppedQueues = new Set(queues.filter((queue) => queueDropped(queue.name, config.droppedQueues)).map((queue) => queue.name));
327
- const keptQueues = queues.filter((queue) => !scope.droppedQueues.has(queue.name));
328
- const dump = dumpEntries(exec(target, 'pg_dump', ['--schema-only', '--no-owner', '-d', target.url]));
329
- const parts = partition(dump, facts, scope, config);
330
- const refusals = danglingReferences(parts.kept, scope);
331
- const roleNames = new Set(facts.roles.map((role) => role.name));
332
- const platform = new Set(facts.roles.filter((role) => (role.super && role.name !== facts.user) || PLATFORM_ROLE.test(role.name)).map((role) => role.name));
333
- const base = new Set([...config.bootstrap.roles, facts.user, 'postgres', 'public', 'PUBLIC']);
334
- const statementLines = (body) => body.split('\n');
335
- const naming = (line) => rolesOf(line).filter((role) => roleNames.has(role) && !base.has(role) && !platform.has(role));
336
- // A statement naming a platform role is the platform's own state and is left out; the rest stays as dumped.
337
- const keepLine = (line) => { const platformRole = rolesOf(line).some((role) => platform.has(role)); if (platformRole) parts.report.platformStatements += 1; return !platformRole; };
338
- // Every replayed object is owned by the migrating role, so a grant to it, spelled out on a source object a platform role owns, is implied.
339
- const owners = new Set([facts.user, 'postgres']);
340
- const ownerGrant = (entry, line) => entry.type === 'ACL' && /^GRANT\b/.test(line) && rolesOf(line).every((role) => owners.has(role));
341
- const entries = parts.kept.flatMap((entry) => {
342
- if (!['ACL', 'DEFAULT ACL'].includes(entry.type)) return [entry];
343
- const body = statementLines(entry.body).filter((line) => {
344
- if (!keepLine(line)) return false;
345
- if (!ownerGrant(entry, line)) return true;
346
- parts.report.ownerGrants += 1;
347
- return false;
348
- }).join('\n').trim();
349
- return body ? [{ ...entry, body }] : [];
350
- });
351
- const carried = new Set(entries.flatMap((entry) => naming(entry.body)));
352
- const aclLines = (items) => items.flatMap((item) => statementLines(item.entry?.body ?? item.body).filter((line) => /^(?:GRANT|REVOKE|ALTER DEFAULT PRIVILEGES)\b/.test(line) && keepLine(line)).map((line) => ({ line, schemaLevel: item.schemaLevel })));
353
- const queueTable = new RegExp(`\\bpgmq\\.[aq]_(?:${[...scope.droppedQueues].map(escape).join('|') || '(?!)'})\\b`);
354
- const outside = [...aclLines(parts.bootstrapAcl), ...aclLines(parts.extensionAcl)].filter((item) => naming(item.line).length && !queueTable.test(item.line));
355
- for (const item of outside) for (const role of naming(item.line)) carried.add(role);
356
- const defaultGrants = aclLines(parts.globalAcl).map((item) => item.line);
357
- for (const line of defaultGrants) for (const role of naming(line)) carried.add(role);
358
- parts.report.lostGrants = parts.report.lostGrants.filter((lost) => naming(lost.line).length).map((lost) => lost.line);
359
- for (const role of facts.roles.filter((item) => carried.has(item.name) && (item.super || item.replication))) refusals.push(`role ${role.name} is a superuser or replication role; a squash carries no such role`);
360
- const section = sectionText({ facts, carried, bootstrapRoles: config.bootstrap.roles, schemaGrants: outside.filter((item) => item.schemaLevel).map((item) => item.line), defaultGrants, refusals });
361
- for (const queue of keptQueues.filter((item) => item.partitioned)) refusals.push(`pgmq queue ${queue.name} is partitioned; a squash recreates plain and unlogged queues only`);
362
- const creates = keptQueues.filter((queue) => !queue.partitioned).map((queue) => `SELECT pgmq.${queue.unlogged ? 'create_unlogged' : 'create'}(${literal(queue.name)});`);
363
- const tail = [...creates, ...outside.filter((item) => !item.schemaLevel).map((item) => item.line)];
364
- parts.report.droppedQueues = sorted(scope.droppedQueues);
365
- parts.report.queues = keptQueues.map((queue) => queue.name);
366
- const seed = config.seedTables.length ? seedText(target, config.seedTables, scope, refusals) : null;
367
- return { facts, scope, dump, entries, tail, section, seed, carried, report: parts.report, refusals };
349
+ const facts = queryJson(target, FACTS_SQL);
350
+ const scope = squashScope(facts, config);
351
+ const queues = facts.extensions.some((extension) => extension.name === 'pgmq') && !config.dropped.includes('pgmq') ? queryJson(target, QUEUES_SQL) : [];
352
+ scope.droppedQueues = new Set(queues.filter((queue) => queueDropped(queue.name, config.droppedQueues)).map((queue) => queue.name));
353
+ const keptQueues = queues.filter((queue) => !scope.droppedQueues.has(queue.name));
354
+ const dump = dumpEntries(exec(target, 'pg_dump', ['--schema-only', '--no-owner', '-d', target.url]));
355
+ const parts = partition(dump, scope, config);
356
+ const refusals = danglingReferences(parts.kept, scope);
357
+ const roleNames = new Set(facts.roles.map((role) => role.name));
358
+ const platform = new Set(facts.roles.filter((role) => (role.super && role.name !== facts.user) || PLATFORM_ROLE.test(role.name)).map((role) => role.name));
359
+ const base = new Set([...config.bootstrap.roles, facts.user, 'postgres', 'public', 'PUBLIC']);
360
+ const statementLines = (body) => body.split('\n');
361
+ const naming = (line) => rolesOf(line).filter((role) => roleNames.has(role) && !base.has(role) && !platform.has(role));
362
+ // A statement naming a platform role is the platform's own state and is left out; the rest stays as dumped.
363
+ const keepLine = (line) => { const platformRole = rolesOf(line).some((role) => platform.has(role)); if (platformRole)
364
+ parts.report.platformStatements += 1; return !platformRole; };
365
+ // Every replayed object is owned by the migrating role, so a grant to it, spelled out on a source object a platform role owns, is implied.
366
+ const owners = new Set([facts.user, 'postgres']);
367
+ const ownerGrant = (entry, line) => entry.type === 'ACL' && /^GRANT\b/.test(line) && rolesOf(line).every((role) => owners.has(role));
368
+ const entries = parts.kept.flatMap((entry) => {
369
+ if (!['ACL', 'DEFAULT ACL'].includes(entry.type))
370
+ return [entry];
371
+ const body = statementLines(entry.body).filter((line) => {
372
+ if (!keepLine(line))
373
+ return false;
374
+ if (!ownerGrant(entry, line))
375
+ return true;
376
+ parts.report.ownerGrants += 1;
377
+ return false;
378
+ }).join('\n').trim();
379
+ return body ? [{ ...entry, body }] : [];
380
+ });
381
+ const carried = new Set(entries.flatMap((entry) => naming(entry.body)));
382
+ const aclLines = (items) => items.flatMap((item) => statementLines((item.entry?.body ?? item.body)).filter((line) => /^(?:GRANT|REVOKE|ALTER DEFAULT PRIVILEGES)\b/.test(line) && keepLine(line)).map((line) => ({ line, schemaLevel: item.schemaLevel })));
383
+ const queueTable = new RegExp(`\\bpgmq\\.[aq]_(?:${[...scope.droppedQueues].map(escape).join('|') || '(?!)'})\\b`);
384
+ const outside = [...aclLines(parts.bootstrapAcl), ...aclLines(parts.extensionAcl)].filter((item) => naming(item.line).length && !queueTable.test(item.line));
385
+ for (const item of outside)
386
+ for (const role of naming(item.line))
387
+ carried.add(role);
388
+ const defaultGrants = aclLines(parts.globalAcl).map((item) => item.line);
389
+ for (const line of defaultGrants)
390
+ for (const role of naming(line))
391
+ carried.add(role);
392
+ parts.report.lostGrants = parts.report.lostGrants.filter((lost) => naming(lost.line).length).map((lost) => lost.line);
393
+ for (const role of facts.roles.filter((item) => carried.has(item.name) && (item.super || item.replication)))
394
+ refusals.push(`role ${role.name} is a superuser or replication role; a squash carries no such role`);
395
+ const section = sectionText({ facts, carried, bootstrapRoles: config.bootstrap.roles, schemaGrants: outside.filter((item) => item.schemaLevel).map((item) => item.line), defaultGrants, refusals });
396
+ for (const queue of keptQueues.filter((item) => item.partitioned))
397
+ refusals.push(`pgmq queue ${queue.name} is partitioned; a squash recreates plain and unlogged queues only`);
398
+ const creates = keptQueues.filter((queue) => !queue.partitioned).map((queue) => `SELECT pgmq.${queue.unlogged ? 'create_unlogged' : 'create'}(${literal(queue.name)});`);
399
+ const tail = [...creates, ...outside.filter((item) => !item.schemaLevel).map((item) => item.line)];
400
+ parts.report.droppedQueues = sorted(scope.droppedQueues);
401
+ parts.report.queues = keptQueues.map((queue) => queue.name);
402
+ const seed = config.seedTables.length ? seedText(target, config.seedTables, scope, refusals) : null;
403
+ return { facts, scope, dump, entries, tail, section, seed, carried, report: parts.report, refusals };
368
404
  }
369
-
370
405
  /* ---- the seed */
371
-
372
406
  function seedFactsSql(tables) {
373
- const pairs = tables.map(([schema, table]) => `(${literal(schema)}, ${literal(table)})`).join(', ');
374
- return `select coalesce(json_agg(json_build_object('schema', n.nspname, 'table', c.relname,
407
+ const pairs = tables.map(([schema, table]) => `(${literal(schema)}, ${literal(table)})`).join(', ');
408
+ return `select coalesce(json_agg(json_build_object('schema', n.nspname, 'table', c.relname,
375
409
  'unique', exists (select 1 from pg_catalog.pg_index i where i.indrelid = c.oid and i.indisunique and i.indpred is null),
376
410
  'references', (select coalesce(jsonb_agg(distinct jsonb_build_array(rn.nspname, rc.relname)), '[]') from pg_catalog.pg_constraint f
377
411
  join pg_catalog.pg_class rc on rc.oid = f.confrelid join pg_catalog.pg_namespace rn on rn.oid = rc.relnamespace where f.conrelid = c.oid and f.contype = 'f' and f.confrelid <> c.oid),
@@ -380,135 +414,164 @@ function seedFactsSql(tables) {
380
414
  from pg_catalog.pg_class c join pg_catalog.pg_namespace n on n.oid = c.relnamespace
381
415
  where c.relkind in ('r', 'p') and (n.nspname, c.relname) in (${pairs})`;
382
416
  }
383
-
384
417
  /** Seed tables in foreign-key order (a referenced table first), ties by name; a cycle among them is refused. */
385
418
  function seedOrder(tables, refusals) {
386
- const name = (table) => `${table.schema}.${table.table}`;
387
- const pending = new Map(tables.map((table) => [name(table), table]));
388
- const order = [];
389
- while (pending.size) {
390
- const ready = sorted([...pending.keys()].filter((key) => pending.get(key).references.every(([schema, table]) => !pending.has(`${schema}.${table}`) || `${schema}.${table}` === key)));
391
- if (!ready.length) { refusals.push(`seed tables reference each other in a cycle: ${sorted(pending.keys()).join(', ')}`); return order; }
392
- for (const key of ready) { order.push(pending.get(key)); pending.delete(key); }
393
- }
394
- return order;
419
+ const name = (table) => `${table.schema}.${table.table}`;
420
+ const pending = new Map(tables.map((table) => [name(table), table]));
421
+ const order = [];
422
+ while (pending.size) {
423
+ const ready = sorted([...pending.keys()].filter((key) => pending.get(key).references.every(([schema, table]) => !pending.has(`${schema}.${table}`) || `${schema}.${table}` === key)));
424
+ if (!ready.length) {
425
+ refusals.push(`seed tables reference each other in a cycle: ${sorted(pending.keys()).join(', ')}`);
426
+ return order;
427
+ }
428
+ for (const key of ready) {
429
+ order.push(pending.get(key));
430
+ pending.delete(key);
431
+ }
432
+ }
433
+ return order;
395
434
  }
396
-
397
435
  function seedText(target, requested, scope, refusals) {
398
- const pairs = requested.map((name) => (name.includes('.') ? name.split('.') : ['public', name]));
399
- const facts = queryJson(target, seedFactsSql(pairs));
400
- const found = new Set(facts.map((table) => `${table.schema}.${table.table}`));
401
- for (const [schema, table] of pairs) {
402
- if (!found.has(`${schema}.${table}`)) refusals.push(`seed table ${schema}.${table} does not exist in the source`);
403
- else if (!scope.appSchemas.has(schema)) refusals.push(`seed table ${schema}.${table} is outside the app schemas (${sorted(scope.appSchemas).join(', ')})`);
404
- }
405
- for (const table of facts.filter((item) => !item.unique)) refusals.push(`seed table ${table.schema}.${table.table} has no primary key or unique constraint, so its rows cannot be seeded idempotently`);
406
- const blocks = seedOrder(facts, refusals).map((table) => {
407
- const qualified = `${ident(table.schema)}.${ident(table.table)}`;
408
- const columns = table.columns.map((column) => ident(column.name)).join(', ');
409
- const overriding = table.columns.some((column) => column.identity === 'a') ? 'overriding system value ' : '';
410
- const values = `pg_catalog.concat_ws(', ', ${table.columns.map((column) => `pg_catalog.quote_nullable(${ident(column.name)})`).join(', ')})`;
411
- const rows = queryJson(target, `select coalesce(json_agg(line order by line collate "C"), '[]') from (select 'insert into ${qualified.replaceAll("'", "''")} (${columns.replaceAll("'", "''")}) ${overriding}values (' || ${values} || ') on conflict do nothing;' as line from ${qualified}) q`);
412
- const sequences = table.columns.filter((column) => column.sequence).map((column) => `select pg_catalog.setval(pg_catalog.pg_get_serial_sequence(${literal(qualified)}, ${literal(column.name)}), coalesce(max(${ident(column.name)}), 0) + 1, false) from ${qualified};`);
413
- return { name: `${table.schema}.${table.table}`, count: rows.length, sql: [`-- ${table.schema}.${table.table}: ${rows.length} row(s)`, ...rows, ...sequences].join('\n') };
414
- });
415
- return { counts: blocks.map((block) => [block.name, block.count]), text: `${SEED_HEADER} from the reference rows of ${blocks.map((block) => block.name).join(', ')}; rerun the verb to regenerate.\n${blocks.map((block) => `${block.sql}\n`).join('\n')}` };
436
+ const pairs = requested.map((name) => (name.includes('.') ? name.split('.') : ['public', name]));
437
+ const facts = queryJson(target, seedFactsSql(pairs));
438
+ const found = new Set(facts.map((table) => `${table.schema}.${table.table}`));
439
+ for (const [schema, table] of pairs) {
440
+ if (!found.has(`${schema}.${table}`))
441
+ refusals.push(`seed table ${schema}.${table} does not exist in the source`);
442
+ else if (!scope.appSchemas.has(schema))
443
+ refusals.push(`seed table ${schema}.${table} is outside the app schemas (${sorted(scope.appSchemas).join(', ')})`);
444
+ }
445
+ for (const table of facts.filter((item) => !item.unique))
446
+ refusals.push(`seed table ${table.schema}.${table.table} has no primary key or unique constraint, so its rows cannot be seeded idempotently`);
447
+ const blocks = seedOrder(facts, refusals).map((table) => {
448
+ const qualified = `${ident(table.schema)}.${ident(table.table)}`;
449
+ const columns = table.columns.map((column) => ident(column.name)).join(', ');
450
+ const overriding = table.columns.some((column) => column.identity === 'a') ? 'overriding system value ' : '';
451
+ const values = `pg_catalog.concat_ws(', ', ${table.columns.map((column) => `pg_catalog.quote_nullable(${ident(column.name)})`).join(', ')})`;
452
+ const rows = queryJson(target, `select coalesce(json_agg(line order by line collate "C"), '[]') from (select 'insert into ${qualified.replaceAll("'", "''")} (${columns.replaceAll("'", "''")}) ${overriding}values (' || ${values} || ') on conflict do nothing;' as line from ${qualified}) q`);
453
+ const sequences = table.columns.filter((column) => column.sequence).map((column) => `select pg_catalog.setval(pg_catalog.pg_get_serial_sequence(${literal(qualified)}, ${literal(column.name)}), coalesce(max(${ident(column.name)}), 0) + 1, false) from ${qualified};`);
454
+ return { name: `${table.schema}.${table.table}`, count: rows.length, sql: [`-- ${table.schema}.${table.table}: ${rows.length} row(s)`, ...rows, ...sequences].join('\n') };
455
+ });
456
+ return { counts: blocks.map((block) => [block.name, block.count]), text: `${SEED_HEADER} from the reference rows of ${blocks.map((block) => block.name).join(', ')}; rerun the verb to regenerate.\n${blocks.map((block) => `${block.sql}\n`).join('\n')}` };
416
457
  }
417
-
418
458
  /* ---- rendering, proof and writing */
419
-
420
459
  function baselineText(projection, last, urlEnv) {
421
- const header = [`-- starci:baseline ${last}`,
422
- `-- Generated by starci app db baseline --squash from the database ${urlEnv} names: the schema of ${sorted(projection.scope.appSchemas).join(', ')} with its privileges,`,
423
- `-- without the extensions ${sorted(projection.report.extensions).join(', ') || '(none)'} and the objects that reach them. Roles and database-wide grants live in ${BOOTSTRAP_FILE}.`];
424
- const tail = projection.tail.length ? [renderEntry({ name: 'pgmq queues and grants on objects outside the app schemas', type: 'ACL', schema: '-', body: projection.tail.join('\n') })] : [];
425
- return `${header.join('\n')}\n\n${projection.dump.preamble.join('\n')}\n\n${[...projection.entries.map(renderEntry), ...tail].join('')}`;
460
+ const header = [`-- starci:baseline ${last}`,
461
+ `-- Generated by starci app db baseline --squash from the database ${urlEnv} names: the schema of ${sorted(projection.scope.appSchemas).join(', ')} with its privileges,`,
462
+ `-- without the extensions ${sorted(projection.report.extensions).join(', ') || '(none)'} and the objects that reach them. Roles and database-wide grants live in ${BOOTSTRAP_FILE}.`];
463
+ const tail = projection.tail.length ? [renderEntry({ name: 'pgmq queues and grants on objects outside the app schemas', type: 'ACL', schema: '-', body: projection.tail.join('\n') })] : [];
464
+ return `${header.join('\n')}\n\n${projection.dump.preamble.join('\n')}\n\n${[...projection.entries.map(renderEntry), ...tail].join('')}`;
426
465
  }
427
-
428
466
  /** The differences between the source's projection and the replay's, as `-`/`+` lines; none means the replay is the source minus the drops. */
429
467
  function projectionDiff(source, replay) {
430
- const lines = [];
431
- const keyed = (projection) => new Map(projection.entries.map((entry) => [entryKey(entry), entry.body]));
432
- const [want, have] = [keyed(source), keyed(replay)];
433
- for (const [key, body] of want) {
434
- if (!have.has(key)) lines.push(`- baseline ${key}`);
435
- else if (have.get(key) !== body) lines.push(`~ baseline ${key}: ${firstDifference(body, have.get(key))}`);
436
- }
437
- for (const key of have.keys()) if (!want.has(key)) lines.push(`+ baseline ${key}`);
438
- const compare = (label, left, right) => { if (left !== right) lines.push(`~ ${label}: ${firstDifference(left ?? '', right ?? '')}`); };
439
- compare('baseline tail grants', source.tail.join('\n'), replay.tail.join('\n'));
440
- compare(BOOTSTRAP_FILE, source.section, replay.section);
441
- compare(SEED_FILE, source.seed?.text, replay.seed?.text);
442
- return lines;
468
+ const lines = [];
469
+ const keyed = (projection) => new Map(projection.entries.map((entry) => [entryKey(entry), entry.body]));
470
+ const [want, have] = [keyed(source), keyed(replay)];
471
+ for (const [key, body] of want) {
472
+ if (!have.has(key))
473
+ lines.push(`- baseline ${key}`);
474
+ else if (have.get(key) !== body)
475
+ lines.push(`~ baseline ${key}: ${firstDifference(body, have.get(key))}`);
476
+ }
477
+ for (const key of have.keys())
478
+ if (!want.has(key))
479
+ lines.push(`+ baseline ${key}`);
480
+ const compare = (label, left, right) => { if (left !== right)
481
+ lines.push(`~ ${label}: ${firstDifference(left ?? '', right ?? '')}`); };
482
+ compare('baseline tail grants', source.tail.join('\n'), replay.tail.join('\n'));
483
+ compare(BOOTSTRAP_FILE, source.section, replay.section);
484
+ compare(SEED_FILE, source.seed?.text, replay.seed?.text);
485
+ return lines;
443
486
  }
444
-
445
487
  function firstDifference(left, right) {
446
- const [want, have] = [left.split('\n'), right.split('\n')];
447
- const index = want.findIndex((line, at) => line !== have[at]);
448
- const at = index < 0 ? want.length : index;
449
- return `line ${at + 1} source ${JSON.stringify(want[at] ?? '(end)')}, replay ${JSON.stringify(have[at] ?? '(end)')}`;
488
+ const [want, have] = [left.split('\n'), right.split('\n')];
489
+ const index = want.findIndex((line, at) => line !== have[at]);
490
+ const at = index < 0 ? want.length : index;
491
+ return `line ${at + 1} source ${JSON.stringify(want[at] ?? '(end)')}, replay ${JSON.stringify(have[at] ?? '(end)')}`;
450
492
  }
451
-
452
493
  function replay(scratch, files) {
453
- const admin = scratchTarget(scratch, 'postgres');
454
- exec(admin, 'psql', [...psqlArgs(admin), '-c', `create database ${REPLAY_DB}`]);
455
- const target = scratchTarget(scratch, REPLAY_DB);
456
- for (const [label, text] of files) {
457
- try { exec(target, 'psql', [...psqlArgs(target), '-1', '-f', '-'], { input: text }); }
458
- catch (error) { throw new DbVerbError(`the replay of ${label} failed on ${scratch.image}: ${error.message}`); }
459
- }
460
- return target;
494
+ const admin = scratchTarget(scratch, 'postgres');
495
+ exec(admin, 'psql', [...psqlArgs(admin), '-c', `create database ${REPLAY_DB}`]);
496
+ const target = scratchTarget(scratch, REPLAY_DB);
497
+ for (const [label, text] of files) {
498
+ try {
499
+ exec(target, 'psql', [...psqlArgs(target), '-1', '-f', '-'], { input: text });
500
+ }
501
+ catch (error) {
502
+ throw new DbVerbError(`the replay of ${label} failed on ${scratch.image}: ${error.message}`);
503
+ }
504
+ }
505
+ return target;
461
506
  }
462
-
463
507
  function printReport(stdout, projection, config, files) {
464
- const { report } = projection;
465
- stdout(`app schemas: ${sorted(projection.scope.appSchemas).join(', ')}\n`);
466
- stdout(`dropped extensions: ${config.dropped.map((name) => (report.extensions.includes(name) ? name : `${name} (not installed)`)).join(', ')}\n`);
467
- for (const label of sorted(report.objects)) stdout(`excluded ${label}\n`);
468
- if (report.queues.length) stdout(`pgmq queues kept: ${report.queues.join(', ')}\n`);
469
- for (const name of report.droppedQueues) stdout(`dropped pgmq queue ${name}\n`);
470
- for (const name of sorted(report.eventTriggers)) stdout(`excluded event trigger ${name} (its function is outside the app schemas)\n`);
471
- for (const [schema, count] of sorted(report.platformSchemas.keys()).map((key) => [key, report.platformSchemas.get(key)])) stdout(`left out ${count} entr${count === 1 ? 'y' : 'ies'} of schema ${schema} (not the app's)\n`);
472
- if (report.platformStatements) stdout(`left out ${report.platformStatements} grant statement(s) naming platform roles\n`);
473
- if (report.ownerGrants) stdout(`left out ${report.ownerGrants} grant statement(s) to the migrating role, which owns every replayed object\n`);
474
- for (const line of report.lostGrants) stdout(`left out a grant to a carried role on a schema the target lacks: ${line}\n`);
475
- stdout(`bootstrap section: roles ${sorted(projection.carried).join(', ') || '(none)'}; ${projection.section.split('\n').length - 2} line(s)\n`);
476
- for (const [name, count] of projection.seed?.counts ?? []) stdout(`seed ${name}: ${count} row(s)\n`);
477
- for (const [file, text] of files) stdout(`${file}: ${text.split('\n').length} line(s)\n`);
508
+ const { report } = projection;
509
+ stdout(`app schemas: ${sorted(projection.scope.appSchemas).join(', ')}\n`);
510
+ stdout(`dropped extensions: ${config.dropped.map((name) => (report.extensions.includes(name) ? name : `${name} (not installed)`)).join(', ')}\n`);
511
+ for (const label of sorted(report.objects))
512
+ stdout(`excluded ${label}\n`);
513
+ if (report.queues.length)
514
+ stdout(`pgmq queues kept: ${report.queues.join(', ')}\n`);
515
+ for (const name of report.droppedQueues)
516
+ stdout(`dropped pgmq queue ${name}\n`);
517
+ for (const name of sorted(report.eventTriggers))
518
+ stdout(`excluded event trigger ${name} (its function is outside the app schemas)\n`);
519
+ for (const [schema, count] of sorted(report.platformSchemas.keys()).map((key) => [key, report.platformSchemas.get(key)]))
520
+ stdout(`left out ${count} entr${count === 1 ? 'y' : 'ies'} of schema ${schema} (not the app's)\n`);
521
+ if (report.platformStatements)
522
+ stdout(`left out ${report.platformStatements} grant statement(s) naming platform roles\n`);
523
+ if (report.ownerGrants)
524
+ stdout(`left out ${report.ownerGrants} grant statement(s) to the migrating role, which owns every replayed object\n`);
525
+ for (const line of report.lostGrants)
526
+ stdout(`left out a grant to a carried role on a schema the target lacks: ${line}\n`);
527
+ stdout(`bootstrap section: roles ${sorted(projection.carried).join(', ') || '(none)'}; ${projection.section.split('\n').length - 2} line(s)\n`);
528
+ for (const [name, count] of projection.seed?.counts ?? [])
529
+ stdout(`seed ${name}: ${count} row(s)\n`);
530
+ for (const [file, text] of files)
531
+ stdout(`${file}: ${text.split('\n').length} line(s)\n`);
478
532
  }
479
-
480
533
  /**
481
- * `starci app db baseline --squash`: plan is db-baseline-verb.mjs's baselinePlan, connection the source of --url-env, precondition the
534
+ * `starci app db baseline --squash`: plan is db-baseline-verb.mjs's baselinePlan, connection the source of --db-url-env, precondition the
482
535
  * history check (the source holds exactly the active chain) run through the given runPostgres port, throwing its refusal, and archive
483
- * the move of the active chain. Dry run by default; `apply` writes the baseline, the bootstrap section and the seed, then archives.
536
+ * the move of the active chain. Dry run by default; `execute` writes the baseline, the bootstrap section and the seed, then archives.
484
537
  */
485
538
  export function squashBaseline({ root, opts, plan, urlEnv, connection, commandOf, precondition, managedSchema, archive, stdout }) {
486
- if (!opts.image && !fs.existsSync(path.join(root, DB_DOCKERFILE))) throw new DbVerbError(`baseline --squash replays on the app's db image: ${DB_DOCKERFILE} is missing (run "npx starci app sync --write") and no --image names one`);
487
- const bootstrapPath = path.join(root, BOOTSTRAP_FILE);
488
- if (!fs.existsSync(bootstrapPath)) throw new DbVerbError(`baseline --squash needs ${BOOTSTRAP_FILE}; run "npx starci app sync --write" first`);
489
- const head = bootstrapSection(fs.readFileSync(bootstrapPath, 'utf8').replace(/\r\n/g, '\n')).head;
490
- const seedPath = path.join(root, SEED_FILE);
491
- const seedTables = flagList(opts['seed-tables'], []);
492
- if (seedTables.length && fs.existsSync(seedPath) && !fs.readFileSync(seedPath, 'utf8').startsWith(SEED_HEADER)) throw new DbVerbError(`${SEED_FILE} exists and was not generated by the squash; fold its rows into the source and remove it, or omit --seed-tables`);
493
- const config = { dropped: flagList(opts['drop-extension'], DEFAULT_DROPPED_EXTENSIONS), droppedQueues: flagList(opts['drop-pgmq-queue'], []), seedTables, bootstrap: bootstrapDeclares(head), managedSchema };
494
- const image = opts.image ?? buildImage(root);
495
- const scratch = startScratch(image);
496
- try {
497
- const source = sourceTarget(scratch, connection, `the database ${urlEnv} names`);
498
- precondition(targetRunner(source, commandOf));
499
- const projected = project(source, config);
500
- if (projected.refusals.length) throw new DbVerbError(`baseline --squash cannot carry the source:\n${[...new Set(projected.refusals)].map((line) => ` ${line}`).join('\n')}`);
501
- const files = [[BOOTSTRAP_FILE, `${head}${projected.section}`], [plan.file, baselineText(projected, plan.last, urlEnv)], ...(projected.seed ? [[SEED_FILE, projected.seed.text]] : [])];
502
- const replayTarget = replay(scratch, [...files, ...(projected.seed ? [[`${SEED_FILE} (second run)`, projected.seed.text]] : [])]);
503
- const replayed = project(replayTarget, config);
504
- const diff = projectionDiff(projected, replayed);
505
- stdout(`${opts.apply ? 'apply' : 'preview'} squash baseline ${plan.file}; archive ${plan.active.length} migrations; the source is read only\n`);
506
- printReport(stdout, projected, config, files);
507
- if (diff.length) throw new DbVerbError(`the replay on ${image} differs from the source minus the dropped extensions:\n${diff.slice(0, DIFF_LIMIT).map((line) => ` ${line}`).join('\n')}${diff.length > DIFF_LIMIT ? `\n ... ${diff.length - DIFF_LIMIT} more` : ''}`);
508
- stdout(`proof: bootstrap + baseline + seed (twice) replayed on ${image}; schema, grants, roles and seed rows equal the source minus ${config.dropped.join(', ')}\n`);
509
- if (!opts.apply) return 0;
510
- for (const [file, text] of files) fs.writeFileSync(path.join(root, file), text, { flag: file === plan.file ? 'wx' : 'w' });
511
- archive();
512
- return 0;
513
- } finally { scratch.stop(); }
539
+ if (!opts.image && !fs.existsSync(path.join(root, DB_DOCKERFILE)))
540
+ throw new DbVerbError(`baseline --squash replays on the app's db image: ${DB_DOCKERFILE} is missing (run "npx starci app sync --execute") and no --image names one`);
541
+ const bootstrapPath = path.join(root, BOOTSTRAP_FILE);
542
+ if (!fs.existsSync(bootstrapPath))
543
+ throw new DbVerbError(`baseline --squash needs ${BOOTSTRAP_FILE}; run "npx starci app sync --execute" first`);
544
+ const head = bootstrapSection(fs.readFileSync(bootstrapPath, 'utf8').replace(/\r\n/g, '\n')).head;
545
+ const seedPath = path.join(root, SEED_FILE);
546
+ const seedTables = flagList(opts['seed-tables'], []);
547
+ if (seedTables.length && fs.existsSync(seedPath) && !fs.readFileSync(seedPath, 'utf8').startsWith(SEED_HEADER))
548
+ throw new DbVerbError(`${SEED_FILE} exists and was not generated by the squash; fold its rows into the source and remove it, or omit --seed-tables`);
549
+ const config = { dropped: flagList(opts['drop-extension'], DEFAULT_DROPPED_EXTENSIONS), droppedQueues: flagList(opts['drop-pgmq-queue'], []), seedTables, bootstrap: bootstrapDeclares(head), managedSchema };
550
+ const image = opts.image ?? buildImage(root);
551
+ const scratch = startScratch(image);
552
+ try {
553
+ const source = sourceTarget(scratch, connection, `the database ${urlEnv} names`);
554
+ precondition(targetRunner(source, commandOf));
555
+ const projected = project(source, config);
556
+ if (projected.refusals.length)
557
+ throw new DbVerbError(`baseline --squash cannot carry the source:\n${[...new Set(projected.refusals)].map((line) => ` ${line}`).join('\n')}`);
558
+ const files = [[BOOTSTRAP_FILE, `${head}${projected.section}`], [plan.file, baselineText(projected, plan.last, urlEnv)], ...(projected.seed ? [[SEED_FILE, projected.seed.text]] : [])];
559
+ const replayTarget = replay(scratch, [...files, ...(projected.seed ? [[`${SEED_FILE} (second run)`, projected.seed.text]] : [])]);
560
+ const replayed = project(replayTarget, config);
561
+ const diff = projectionDiff(projected, replayed);
562
+ stdout(`${opts.execute ? 'apply' : 'preview'} squash baseline ${plan.file}; archive ${plan.active.length} migrations; the source is read only\n`);
563
+ printReport(stdout, projected, config, files);
564
+ if (diff.length)
565
+ throw new DbVerbError(`the replay on ${image} differs from the source minus the dropped extensions:\n${diff.slice(0, DIFF_LIMIT).map((line) => ` ${line}`).join('\n')}${diff.length > DIFF_LIMIT ? `\n ... ${diff.length - DIFF_LIMIT} more` : ''}`);
566
+ stdout(`proof: bootstrap + baseline + seed (twice) replayed on ${image}; schema, grants, roles and seed rows equal the source minus ${config.dropped.join(', ')}\n`);
567
+ if (!opts.execute)
568
+ return 0;
569
+ for (const [file, text] of files)
570
+ fs.writeFileSync(path.join(root, file), text, { flag: file === plan.file ? 'wx' : 'w' });
571
+ archive();
572
+ return 0;
573
+ }
574
+ finally {
575
+ scratch.stop();
576
+ }
514
577
  }