@intentius/sql-yodeler 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +112 -2
  3. package/dist/bin.d.ts +2 -0
  4. package/dist/bin.js +29 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/chant/apply.d.ts +59 -0
  7. package/dist/chant/apply.js +116 -0
  8. package/dist/chant/apply.js.map +1 -0
  9. package/dist/chant/build.d.ts +34 -0
  10. package/dist/chant/build.js +48 -0
  11. package/dist/chant/build.js.map +1 -0
  12. package/dist/chant/drift.d.ts +112 -0
  13. package/dist/chant/drift.js +238 -0
  14. package/dist/chant/drift.js.map +1 -0
  15. package/dist/chant/plan.d.ts +45 -0
  16. package/dist/chant/plan.js +61 -0
  17. package/dist/chant/plan.js.map +1 -0
  18. package/dist/chant/process.d.ts +41 -0
  19. package/dist/chant/process.js +87 -0
  20. package/dist/chant/process.js.map +1 -0
  21. package/dist/chant/runnable.d.ts +19 -0
  22. package/dist/chant/runnable.js +47 -0
  23. package/dist/chant/runnable.js.map +1 -0
  24. package/dist/cli.d.ts +8 -0
  25. package/dist/cli.js +99 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/command.d.ts +23 -0
  28. package/dist/command.js +13 -0
  29. package/dist/command.js.map +1 -0
  30. package/dist/commands/apply.d.ts +3 -0
  31. package/dist/commands/apply.js +426 -0
  32. package/dist/commands/apply.js.map +1 -0
  33. package/dist/commands/approve.d.ts +5 -0
  34. package/dist/commands/approve.js +111 -0
  35. package/dist/commands/approve.js.map +1 -0
  36. package/dist/commands/drift.d.ts +14 -0
  37. package/dist/commands/drift.js +267 -0
  38. package/dist/commands/drift.js.map +1 -0
  39. package/dist/commands/index.d.ts +3 -0
  40. package/dist/commands/index.js +28 -0
  41. package/dist/commands/index.js.map +1 -0
  42. package/dist/commands/init.d.ts +6 -0
  43. package/dist/commands/init.js +139 -0
  44. package/dist/commands/init.js.map +1 -0
  45. package/dist/commands/lint.d.ts +3 -0
  46. package/dist/commands/lint.js +202 -0
  47. package/dist/commands/lint.js.map +1 -0
  48. package/dist/commands/mcp.d.ts +3 -0
  49. package/dist/commands/mcp.js +50 -0
  50. package/dist/commands/mcp.js.map +1 -0
  51. package/dist/commands/new.d.ts +5 -0
  52. package/dist/commands/new.js +292 -0
  53. package/dist/commands/new.js.map +1 -0
  54. package/dist/commands/override.d.ts +3 -0
  55. package/dist/commands/override.js +118 -0
  56. package/dist/commands/override.js.map +1 -0
  57. package/dist/commands/plan.d.ts +3 -0
  58. package/dist/commands/plan.js +183 -0
  59. package/dist/commands/plan.js.map +1 -0
  60. package/dist/commands/rebase.d.ts +3 -0
  61. package/dist/commands/rebase.js +167 -0
  62. package/dist/commands/rebase.js.map +1 -0
  63. package/dist/commands/repair.d.ts +14 -0
  64. package/dist/commands/repair.js +125 -0
  65. package/dist/commands/repair.js.map +1 -0
  66. package/dist/commands/status.d.ts +7 -0
  67. package/dist/commands/status.js +54 -0
  68. package/dist/commands/status.js.map +1 -0
  69. package/dist/config/define.d.ts +148 -0
  70. package/dist/config/define.js +20 -0
  71. package/dist/config/define.js.map +1 -0
  72. package/dist/config/index.d.ts +11 -0
  73. package/dist/config/index.js +12 -0
  74. package/dist/config/index.js.map +1 -0
  75. package/dist/config/load.d.ts +17 -0
  76. package/dist/config/load.js +162 -0
  77. package/dist/config/load.js.map +1 -0
  78. package/dist/config/settings.d.ts +53 -0
  79. package/dist/config/settings.js +116 -0
  80. package/dist/config/settings.js.map +1 -0
  81. package/dist/env-args.d.ts +24 -0
  82. package/dist/env-args.js +92 -0
  83. package/dist/env-args.js.map +1 -0
  84. package/dist/exit.d.ts +20 -0
  85. package/dist/exit.js +28 -0
  86. package/dist/exit.js.map +1 -0
  87. package/dist/history/client.d.ts +25 -0
  88. package/dist/history/client.js +42 -0
  89. package/dist/history/client.js.map +1 -0
  90. package/dist/history/ddl.d.ts +58 -0
  91. package/dist/history/ddl.js +100 -0
  92. package/dist/history/ddl.js.map +1 -0
  93. package/dist/history/index.d.ts +25 -0
  94. package/dist/history/index.js +26 -0
  95. package/dist/history/index.js.map +1 -0
  96. package/dist/history/lock.d.ts +104 -0
  97. package/dist/history/lock.js +230 -0
  98. package/dist/history/lock.js.map +1 -0
  99. package/dist/history/postgres/ddl.d.ts +22 -0
  100. package/dist/history/postgres/ddl.js +47 -0
  101. package/dist/history/postgres/ddl.js.map +1 -0
  102. package/dist/history/postgres/index.d.ts +12 -0
  103. package/dist/history/postgres/index.js +13 -0
  104. package/dist/history/postgres/index.js.map +1 -0
  105. package/dist/history/postgres/lock.d.ts +48 -0
  106. package/dist/history/postgres/lock.js +95 -0
  107. package/dist/history/postgres/lock.js.map +1 -0
  108. package/dist/history/postgres/store.d.ts +39 -0
  109. package/dist/history/postgres/store.js +68 -0
  110. package/dist/history/postgres/store.js.map +1 -0
  111. package/dist/history/rows.d.ts +96 -0
  112. package/dist/history/rows.js +73 -0
  113. package/dist/history/rows.js.map +1 -0
  114. package/dist/history/store.d.ts +72 -0
  115. package/dist/history/store.js +134 -0
  116. package/dist/history/store.js.map +1 -0
  117. package/dist/history/topology.d.ts +41 -0
  118. package/dist/history/topology.js +54 -0
  119. package/dist/history/topology.js.map +1 -0
  120. package/dist/index.d.ts +10 -0
  121. package/dist/index.js +10 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/init/adopt.d.ts +70 -0
  124. package/dist/init/adopt.js +143 -0
  125. package/dist/init/adopt.js.map +1 -0
  126. package/dist/init/baseline.d.ts +61 -0
  127. package/dist/init/baseline.js +88 -0
  128. package/dist/init/baseline.js.map +1 -0
  129. package/dist/init/files.d.ts +23 -0
  130. package/dist/init/files.js +77 -0
  131. package/dist/init/files.js.map +1 -0
  132. package/dist/init/index.d.ts +16 -0
  133. package/dist/init/index.js +17 -0
  134. package/dist/init/index.js.map +1 -0
  135. package/dist/init/live.d.ts +39 -0
  136. package/dist/init/live.js +68 -0
  137. package/dist/init/live.js.map +1 -0
  138. package/dist/lint/check.d.ts +88 -0
  139. package/dist/lint/check.js +227 -0
  140. package/dist/lint/check.js.map +1 -0
  141. package/dist/lint/config.d.ts +21 -0
  142. package/dist/lint/config.js +43 -0
  143. package/dist/lint/config.js.map +1 -0
  144. package/dist/lint/index.d.ts +31 -0
  145. package/dist/lint/index.js +43 -0
  146. package/dist/lint/index.js.map +1 -0
  147. package/dist/lint/project-rules.d.ts +19 -0
  148. package/dist/lint/project-rules.js +32 -0
  149. package/dist/lint/project-rules.js.map +1 -0
  150. package/dist/lint/report.d.ts +7 -0
  151. package/dist/lint/report.js +48 -0
  152. package/dist/lint/report.js.map +1 -0
  153. package/dist/lint/rules.d.ts +53 -0
  154. package/dist/lint/rules.js +42 -0
  155. package/dist/lint/rules.js.map +1 -0
  156. package/dist/lint/silences.d.ts +49 -0
  157. package/dist/lint/silences.js +91 -0
  158. package/dist/lint/silences.js.map +1 -0
  159. package/dist/lint/statements.d.ts +38 -0
  160. package/dist/lint/statements.js +120 -0
  161. package/dist/lint/statements.js.map +1 -0
  162. package/dist/mcp/server.d.ts +40 -0
  163. package/dist/mcp/server.js +106 -0
  164. package/dist/mcp/server.js.map +1 -0
  165. package/dist/mcp/tools.d.ts +72 -0
  166. package/dist/mcp/tools.js +178 -0
  167. package/dist/mcp/tools.js.map +1 -0
  168. package/dist/migrations/checks.d.ts +28 -0
  169. package/dist/migrations/checks.js +97 -0
  170. package/dist/migrations/checks.js.map +1 -0
  171. package/dist/migrations/checksum.d.ts +56 -0
  172. package/dist/migrations/checksum.js +72 -0
  173. package/dist/migrations/checksum.js.map +1 -0
  174. package/dist/migrations/create.d.ts +75 -0
  175. package/dist/migrations/create.js +120 -0
  176. package/dist/migrations/create.js.map +1 -0
  177. package/dist/migrations/directory.d.ts +119 -0
  178. package/dist/migrations/directory.js +229 -0
  179. package/dist/migrations/directory.js.map +1 -0
  180. package/dist/migrations/document.d.ts +106 -0
  181. package/dist/migrations/document.js +83 -0
  182. package/dist/migrations/document.js.map +1 -0
  183. package/dist/migrations/index.d.ts +21 -0
  184. package/dist/migrations/index.js +22 -0
  185. package/dist/migrations/index.js.map +1 -0
  186. package/dist/pr/comment.d.ts +32 -0
  187. package/dist/pr/comment.js +99 -0
  188. package/dist/pr/comment.js.map +1 -0
  189. package/dist/pr/forge.d.ts +54 -0
  190. package/dist/pr/forge.js +119 -0
  191. package/dist/pr/forge.js.map +1 -0
  192. package/dist/pr/issue.d.ts +68 -0
  193. package/dist/pr/issue.js +107 -0
  194. package/dist/pr/issue.js.map +1 -0
  195. package/dist/pr/model.d.ts +99 -0
  196. package/dist/pr/model.js +137 -0
  197. package/dist/pr/model.js.map +1 -0
  198. package/dist/pr/render.d.ts +29 -0
  199. package/dist/pr/render.js +279 -0
  200. package/dist/pr/render.js.map +1 -0
  201. package/dist/project.d.ts +62 -0
  202. package/dist/project.js +92 -0
  203. package/dist/project.js.map +1 -0
  204. package/dist/rebase/index.d.ts +121 -0
  205. package/dist/rebase/index.js +390 -0
  206. package/dist/rebase/index.js.map +1 -0
  207. package/dist/rebase/repair.d.ts +41 -0
  208. package/dist/rebase/repair.js +68 -0
  209. package/dist/rebase/repair.js.map +1 -0
  210. package/dist/replay/clickhouse.d.ts +33 -0
  211. package/dist/replay/clickhouse.js +106 -0
  212. package/dist/replay/clickhouse.js.map +1 -0
  213. package/dist/replay/index.d.ts +39 -0
  214. package/dist/replay/index.js +57 -0
  215. package/dist/replay/index.js.map +1 -0
  216. package/dist/replay/postgres.d.ts +31 -0
  217. package/dist/replay/postgres.js +140 -0
  218. package/dist/replay/postgres.js.map +1 -0
  219. package/dist/replay/replay.d.ts +114 -0
  220. package/dist/replay/replay.js +98 -0
  221. package/dist/replay/replay.js.map +1 -0
  222. package/dist/replay/report.d.ts +18 -0
  223. package/dist/replay/report.js +72 -0
  224. package/dist/replay/report.js.map +1 -0
  225. package/dist/replay/shared.d.ts +28 -0
  226. package/dist/replay/shared.js +77 -0
  227. package/dist/replay/shared.js.map +1 -0
  228. package/dist/schemas.d.ts +34 -0
  229. package/dist/schemas.js +38 -0
  230. package/dist/schemas.js.map +1 -0
  231. package/dist/server.d.ts +40 -0
  232. package/dist/server.js +87 -0
  233. package/dist/server.js.map +1 -0
  234. package/dist/steps/backfill.d.ts +132 -0
  235. package/dist/steps/backfill.js +307 -0
  236. package/dist/steps/backfill.js.map +1 -0
  237. package/dist/steps/index.d.ts +52 -0
  238. package/dist/steps/index.js +52 -0
  239. package/dist/steps/index.js.map +1 -0
  240. package/dist/steps/kinds.d.ts +101 -0
  241. package/dist/steps/kinds.js +143 -0
  242. package/dist/steps/kinds.js.map +1 -0
  243. package/dist/steps/postgres-migration.d.ts +95 -0
  244. package/dist/steps/postgres-migration.js +149 -0
  245. package/dist/steps/postgres-migration.js.map +1 -0
  246. package/dist/steps/rebuild.d.ts +97 -0
  247. package/dist/steps/rebuild.js +155 -0
  248. package/dist/steps/rebuild.js.map +1 -0
  249. package/dist/steps/run.d.ts +45 -0
  250. package/dist/steps/run.js +72 -0
  251. package/dist/steps/run.js.map +1 -0
  252. package/dist/topology/render.d.ts +17 -0
  253. package/dist/topology/render.js +17 -0
  254. package/dist/topology/render.js.map +1 -0
  255. package/dist/version.d.ts +2 -0
  256. package/dist/version.js +11 -0
  257. package/dist/version.js.map +1 -0
  258. package/dist/versioned/approval.d.ts +35 -0
  259. package/dist/versioned/approval.js +53 -0
  260. package/dist/versioned/approval.js.map +1 -0
  261. package/dist/versioned/checks.d.ts +29 -0
  262. package/dist/versioned/checks.js +54 -0
  263. package/dist/versioned/checks.js.map +1 -0
  264. package/dist/versioned/clickhouse.d.ts +24 -0
  265. package/dist/versioned/clickhouse.js +42 -0
  266. package/dist/versioned/clickhouse.js.map +1 -0
  267. package/dist/versioned/digest.d.ts +77 -0
  268. package/dist/versioned/digest.js +69 -0
  269. package/dist/versioned/digest.js.map +1 -0
  270. package/dist/versioned/driver.d.ts +51 -0
  271. package/dist/versioned/driver.js +13 -0
  272. package/dist/versioned/driver.js.map +1 -0
  273. package/dist/versioned/engine.d.ts +206 -0
  274. package/dist/versioned/engine.js +370 -0
  275. package/dist/versioned/engine.js.map +1 -0
  276. package/dist/versioned/explain.d.ts +123 -0
  277. package/dist/versioned/explain.js +294 -0
  278. package/dist/versioned/explain.js.map +1 -0
  279. package/dist/versioned/index.d.ts +46 -0
  280. package/dist/versioned/index.js +77 -0
  281. package/dist/versioned/index.js.map +1 -0
  282. package/dist/versioned/op.d.ts +72 -0
  283. package/dist/versioned/op.js +185 -0
  284. package/dist/versioned/op.js.map +1 -0
  285. package/dist/versioned/policy.d.ts +67 -0
  286. package/dist/versioned/policy.js +161 -0
  287. package/dist/versioned/policy.js.map +1 -0
  288. package/dist/versioned/postgres/catalog.d.ts +33 -0
  289. package/dist/versioned/postgres/catalog.js +34 -0
  290. package/dist/versioned/postgres/catalog.js.map +1 -0
  291. package/dist/versioned/postgres/driver.d.ts +71 -0
  292. package/dist/versioned/postgres/driver.js +140 -0
  293. package/dist/versioned/postgres/driver.js.map +1 -0
  294. package/dist/versioned/postgres/executor.d.ts +104 -0
  295. package/dist/versioned/postgres/executor.js +172 -0
  296. package/dist/versioned/postgres/executor.js.map +1 -0
  297. package/dist/versioned/postgres/index.d.ts +11 -0
  298. package/dist/versioned/postgres/index.js +12 -0
  299. package/dist/versioned/postgres/index.js.map +1 -0
  300. package/dist/versioned/postgres/waits.d.ts +46 -0
  301. package/dist/versioned/postgres/waits.js +78 -0
  302. package/dist/versioned/postgres/waits.js.map +1 -0
  303. package/dist/versioned/report.d.ts +25 -0
  304. package/dist/versioned/report.js +84 -0
  305. package/dist/versioned/report.js.map +1 -0
  306. package/dist/versioned/stand-down.d.ts +30 -0
  307. package/dist/versioned/stand-down.js +56 -0
  308. package/dist/versioned/stand-down.js.map +1 -0
  309. package/dist/versioned/state.d.ts +108 -0
  310. package/dist/versioned/state.js +153 -0
  311. package/dist/versioned/state.js.map +1 -0
  312. package/package.json +61 -4
  313. package/schemas/apply.schema.json +269 -0
  314. package/schemas/common.schema.json +213 -0
  315. package/schemas/drift.schema.json +36 -0
  316. package/schemas/init.schema.json +31 -0
  317. package/schemas/lint-rules.schema.json +54 -0
  318. package/schemas/lint.schema.json +290 -0
  319. package/schemas/mcp.schema.json +70 -0
  320. package/schemas/new.schema.json +71 -0
  321. package/schemas/plan.schema.json +371 -0
  322. package/schemas/rebase.schema.json +54 -0
  323. package/schemas/repair.schema.json +20 -0
  324. package/schemas/status.schema.json +310 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * The plan digest a versioned apply is approved under.
3
+ *
4
+ * It covers what the run would do and the state it would start from:
5
+ *
6
+ * - the migrations it would apply, in order, each with its files' checksum
7
+ * and the statements of it that already succeeded (a part-way one resumes);
8
+ * - the history it starts from: every applied migration and its checksum;
9
+ * - the topology the statements are rendered for (#9): an approval given
10
+ * for a single node never applies on a cluster;
11
+ * - the live schema: a fingerprint of the server's catalog in the
12
+ * environment's databases (every table, view and dictionary there, by its
13
+ * `create_table_query`, and each database's engine). The history database
14
+ * is left out, so recording a run does not move the digest.
15
+ *
16
+ * It is chant's plan digest (`computePlanDigest`, kind `yodel-migrations`),
17
+ * so `chant approve <op> <gate> --plan <digest>` takes it. A change to any of
18
+ * it after approval (a new pending migration, an edited one, a table changed
19
+ * out of band, another run applying first) is a new digest, and the
20
+ * approval does not carry over.
21
+ */
22
+ import type { ClickHousePort } from "../history/index.js";
23
+ import type { AppliedMigration, PendingMigration } from "./state.js";
24
+ import type { Topology } from "../topology/render.js";
25
+ export declare const DIGEST_KIND = "yodel-migrations";
26
+ export interface LiveScope {
27
+ /** `sql.profiles.<env>.databases`; every database but the server's own when undefined. */
28
+ databases: string[] | undefined;
29
+ /** Left out of the fingerprint. */
30
+ historyDatabase: string;
31
+ }
32
+ export interface LiveCatalog {
33
+ databases: Array<{
34
+ name: string;
35
+ engine: string;
36
+ }>;
37
+ tables: Array<{
38
+ database: string;
39
+ name: string;
40
+ engine: string;
41
+ create: string;
42
+ }>;
43
+ }
44
+ /** Reads the catalog the fingerprint covers. */
45
+ export declare function readLiveCatalog(port: ClickHousePort, scope: LiveScope): Promise<LiveCatalog>;
46
+ /** `sha256:<hex>` over the catalog's canonical JSON. */
47
+ export declare function catalogFingerprint(catalog: LiveCatalog): string;
48
+ /** What the digest is taken over. */
49
+ export interface DigestSubject {
50
+ environment: string;
51
+ historyDatabase: string;
52
+ /** The environment's topology, with a cluster's Keeper path and replica name when it sets them. Always set by {@link digestSubject}; absent in a subject carried from before #9. */
53
+ topology?: Topology;
54
+ applied: Array<{
55
+ id: string;
56
+ checksum: string;
57
+ }>;
58
+ pending: Array<{
59
+ id: string;
60
+ checksum: string;
61
+ done: number[];
62
+ }>;
63
+ live: string;
64
+ }
65
+ export declare function digestSubject(input: {
66
+ environment: string;
67
+ historyDatabase: string;
68
+ /** Default: a single node. */
69
+ topology?: Topology | undefined;
70
+ applied: readonly AppliedMigration[];
71
+ toApply: readonly PendingMigration[];
72
+ live: string;
73
+ }): DigestSubject;
74
+ /** The plan digest of a subject, as chant computes and compares them. */
75
+ export declare function planDigest(subject: DigestSubject): Promise<string>;
76
+ /** Whether two digests name the same plan, by chant's rule. */
77
+ export declare function sameDigest(a: string | undefined, b: string | undefined): Promise<boolean>;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The plan digest a versioned apply is approved under.
3
+ *
4
+ * It covers what the run would do and the state it would start from:
5
+ *
6
+ * - the migrations it would apply, in order, each with its files' checksum
7
+ * and the statements of it that already succeeded (a part-way one resumes);
8
+ * - the history it starts from: every applied migration and its checksum;
9
+ * - the topology the statements are rendered for (#9): an approval given
10
+ * for a single node never applies on a cluster;
11
+ * - the live schema: a fingerprint of the server's catalog in the
12
+ * environment's databases (every table, view and dictionary there, by its
13
+ * `create_table_query`, and each database's engine). The history database
14
+ * is left out, so recording a run does not move the digest.
15
+ *
16
+ * It is chant's plan digest (`computePlanDigest`, kind `yodel-migrations`),
17
+ * so `chant approve <op> <gate> --plan <digest>` takes it. A change to any of
18
+ * it after approval (a new pending migration, an edited one, a table changed
19
+ * out of band, another run applying first) is a new digest, and the
20
+ * approval does not carry over.
21
+ */
22
+ import { createHash } from "node:crypto";
23
+ import { literal } from "../history/index.js";
24
+ export const DIGEST_KIND = "yodel-migrations";
25
+ /** The databases the server keeps for itself, never part of an environment's schema. */
26
+ const SYSTEM_DATABASES = ["system", "information_schema", "INFORMATION_SCHEMA"];
27
+ /** Reads the catalog the fingerprint covers. */
28
+ export async function readLiveCatalog(port, scope) {
29
+ const where = scope.databases
30
+ ? `name IN (${scope.databases.filter((d) => d !== scope.historyDatabase).map(literal).join(", ") || "''"})`
31
+ : `name NOT IN (${[...SYSTEM_DATABASES, scope.historyDatabase].map(literal).join(", ")})`;
32
+ const databases = await port.query(`SELECT name, engine FROM system.databases WHERE ${where} ORDER BY name`);
33
+ const names = databases.map((d) => d.name);
34
+ const tables = names.length === 0
35
+ ? []
36
+ : await port.query(`SELECT database, name, engine, create_table_query AS create FROM system.tables WHERE database IN (${names.map(literal).join(", ")}) AND NOT is_temporary ORDER BY database, name`);
37
+ return { databases, tables };
38
+ }
39
+ /** `sha256:<hex>` over the catalog's canonical JSON. */
40
+ export function catalogFingerprint(catalog) {
41
+ const canonical = {
42
+ databases: [...catalog.databases].sort((a, b) => a.name.localeCompare(b.name)).map((d) => [d.name, d.engine]),
43
+ tables: [...catalog.tables]
44
+ .sort((a, b) => a.database.localeCompare(b.database) || a.name.localeCompare(b.name))
45
+ .map((t) => [t.database, t.name, t.engine, t.create]),
46
+ };
47
+ return `sha256:${createHash("sha256").update(JSON.stringify(canonical), "utf8").digest("hex")}`;
48
+ }
49
+ export function digestSubject(input) {
50
+ return {
51
+ environment: input.environment,
52
+ historyDatabase: input.historyDatabase,
53
+ topology: input.topology ?? { kind: "single" },
54
+ applied: input.applied.map((a) => ({ id: a.id, checksum: a.checksum })),
55
+ pending: input.toApply.map((p) => ({ id: p.id, checksum: p.checksum, done: p.partWay?.done ?? [] })),
56
+ live: input.live,
57
+ };
58
+ }
59
+ /** The plan digest of a subject, as chant computes and compares them. */
60
+ export async function planDigest(subject) {
61
+ const { computePlanDigest } = await import("@intentius/chant/lifecycle/plan-digest");
62
+ return computePlanDigest(DIGEST_KIND, subject);
63
+ }
64
+ /** Whether two digests name the same plan, by chant's rule. */
65
+ export async function sameDigest(a, b) {
66
+ const { samePlanDigest } = await import("@intentius/chant/lifecycle/plan-digest");
67
+ return samePlanDigest(a, b);
68
+ }
69
+ //# sourceMappingURL=digest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest.js","sourceRoot":"","sources":["../../src/versioned/digest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * What the versioned apply needs from a dialect: its lock, the live-schema
3
+ * fingerprint the plan digest covers, and how one statement is sent and
4
+ * recorded. The engine (`./engine.ts`) is the same for both dialects: it
5
+ * reads the history, compares it with the directory, checks the digest and
6
+ * the approval, and walks the statements and Op steps in order, recording
7
+ * each one's start and failure; the driver does the dialect's part.
8
+ *
9
+ * ClickHouse ./clickhouse.ts the Keeper (or local) lock, system.tables, a statement then its row
10
+ * Postgres ./postgres/ the session advisory lock, chant's catalog reader, a statement and its row in one transaction
11
+ */
12
+ import type { SqlDialect } from "../project.js";
13
+ import type { StatementStep } from "@intentius/chant-lexicon-sql/migration-statements";
14
+ import type { Lock } from "../history/index.js";
15
+ import type { StepContext } from "../steps/index.js";
16
+ export interface StatementRun {
17
+ /**
18
+ * Writes the statement's `succeeded` row, with `note` (the retries it
19
+ * took, an INVALID index it dropped; `""` when there is nothing to say).
20
+ * The driver calls it where the row commits with the statement when the
21
+ * dialect can: inside the statement's transaction on Postgres.
22
+ */
23
+ succeeded: (note: string) => Promise<unknown>;
24
+ log: (line: string) => void;
25
+ }
26
+ /** One dialect's part of the versioned apply. A failed statement throws with the server's message, which the history's failed row records. */
27
+ export interface VersionedDriver {
28
+ dialect: SqlDialect;
29
+ /** The `note` a statement's rows carry: what ClickHouse rendered it for (#9); none on Postgres. */
30
+ rowNote?: string;
31
+ /** Takes the environment's apply lock, or throws a `LockHeldError` (exit 5). */
32
+ acquireLock(holder: string): Promise<Lock>;
33
+ /** A fingerprint of the live schema in the environment's scope, the history left out. */
34
+ liveFingerprint(): Promise<string>;
35
+ /**
36
+ * Runs a pre-migration check's query (#52) read-only and returns its rows:
37
+ * ClickHouse with `readonly = 2` and the migration's default database,
38
+ * Postgres in a `READ ONLY` transaction with `search_path` set to the
39
+ * migration's default schema, rolled back.
40
+ */
41
+ readCheck(sql: string, where: {
42
+ defaultDatabase?: string | undefined;
43
+ defaultSchema?: string | undefined;
44
+ }): Promise<Array<Record<string, unknown>>>;
45
+ /** Sends one statement and writes its `succeeded` row through `run`; throws with the server's message when it fails. */
46
+ runStatement(step: StatementStep, run: StatementRun): Promise<void>;
47
+ /** What an Op step's runner gets from the dialect: the ClickHouse port and endpoint; the Postgres lock timeout, and the connection, timeouts and receipts schema a backfill uses. */
48
+ stepContext(): Pick<StepContext, "port" | "endpoint" | "lockTimeoutMs" | "postgres">;
49
+ /** Closes what the driver opened (a Postgres connection). Safe to call twice. */
50
+ close(): Promise<void>;
51
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * What the versioned apply needs from a dialect: its lock, the live-schema
3
+ * fingerprint the plan digest covers, and how one statement is sent and
4
+ * recorded. The engine (`./engine.ts`) is the same for both dialects: it
5
+ * reads the history, compares it with the directory, checks the digest and
6
+ * the approval, and walks the statements and Op steps in order, recording
7
+ * each one's start and failure; the driver does the dialect's part.
8
+ *
9
+ * ClickHouse ./clickhouse.ts the Keeper (or local) lock, system.tables, a statement then its row
10
+ * Postgres ./postgres/ the session advisory lock, chant's catalog reader, a statement and its row in one transaction
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/versioned/driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * The versioned apply: pending migrations, statement by statement, each
3
+ * statement's start and outcome appended to the history, under the lock,
4
+ * after the plan digest the run was approved under is checked again.
5
+ *
6
+ * Statements are the `statement` steps of each migration's `migration.json`,
7
+ * written for a single node, each rendered for the environment's topology
8
+ * as it is sent (#9, `../topology/render.ts`). A statement whose latest
9
+ * history row is `succeeded` is never sent again, so a migration that failed
10
+ * part way resumes at the statement that failed. A statement whose latest
11
+ * row is `started` (the runner stopped while it ran) is sent again: the
12
+ * history cannot tell whether the server ran it.
13
+ *
14
+ * An Op step (#12) runs in its place among the statements: the statements
15
+ * before it first, the ones after it only once it succeeded. A
16
+ * `ClickHouseRebuildOp` step runs chant's rebuild Op, a backfill step the
17
+ * author's Op of `effect()` batches (`../steps/`), each in this process
18
+ * under the same lock, and behind the same approval: the step and any file
19
+ * it runs are in the migration's checksum, so in the plan digest. Its rows
20
+ * are `kind = 'step'`, with the step's summary (receipts skipped, work done)
21
+ * in `note`. A step that failed or was stopped runs again on the next run
22
+ * and resumes from its receipts: the partitions or batches that finished
23
+ * are skipped.
24
+ *
25
+ * On Postgres (#13) a `PostgresMigrationOp` step (a column rename or type
26
+ * change) runs the same way, as chant's expand-and-contract Op
27
+ * (`../steps/postgres-migration.ts`).
28
+ *
29
+ * What is the dialect's (the lock, the live-schema fingerprint, how a
30
+ * statement is sent and its `succeeded` row written) is the driver's
31
+ * (`./driver.ts`): on Postgres a transactional statement and its row commit
32
+ * in one transaction, under `lock_timeout`, retried on a lock timeout
33
+ * (`./postgres/executor.ts`).
34
+ *
35
+ * A migration's pre-migration checks (#52, `./checks.ts`) run under the
36
+ * lock just before its first statement; one that fails refuses (exit 4)
37
+ * with a `failed` migration row naming it, and nothing of it is sent.
38
+ *
39
+ * Manual steps, and Op steps apply does not run (a rebuild in app mode, a
40
+ * step of the other dialect), are refused before anything runs (exit 2).
41
+ */
42
+ import type { ClickHouseEndpoint } from "@intentius/chant-lexicon-sql/clickhouse/http";
43
+ import { YodelError, type Environment, type Project } from "../project.js";
44
+ import type { Migration } from "../migrations/index.js";
45
+ import type { Topology } from "../topology/render.js";
46
+ import { type StepRunner } from "../steps/index.js";
47
+ import type { ReceiptStore } from "@intentius/chant/op/receipt-store";
48
+ import { type ClickHousePort, type Lock, type HistoryLocation, type HistoryState, type HistoryStoreLike } from "../history/index.js";
49
+ import type { SqlDialect } from "../project.js";
50
+ import { type ServerInfo } from "../server.js";
51
+ import type { VersionedDriver } from "./driver.js";
52
+ import { type PgConnect } from "./postgres/driver.js";
53
+ import { type PendingMigration, type VersionedStatus } from "./state.js";
54
+ import { type LiveScope } from "./digest.js";
55
+ import type { PolicyOverride } from "./policy.js";
56
+ /** Exit code: a checksum mismatch, an applied migration gone from the directory, or an out-of-order migration without `--allow-out-of-order`. */
57
+ export declare const REFUSED = 4;
58
+ /** Everything a versioned verb needs about one environment. */
59
+ export interface VersionedEnv {
60
+ environment: string;
61
+ dialect: SqlDialect;
62
+ /** The dialect's lock, fingerprint and statement sending (`./driver.ts`). */
63
+ driver: VersionedDriver;
64
+ /** The ClickHouse server; absent on Postgres, whose connections the driver keeps. */
65
+ port?: ClickHousePort;
66
+ store: HistoryStoreLike;
67
+ /** Where the history is: on Postgres, `database` is the history's schema and the topology a single node. */
68
+ location: HistoryLocation;
69
+ /** ClickHouse: the databases the fingerprint covers. Postgres: `databases` is the profile's schemas. */
70
+ scope: LiveScope;
71
+ /** Identifies the server, for the local lock's file name. */
72
+ serverKey: string;
73
+ lockTtlMs: number;
74
+ /** How long an apply waits for a lock another apply holds (`../config/settings.ts`); 0 gives up at once. */
75
+ lockWaitMs?: number;
76
+ /** The project directory: an Op step reads chant.config.ts from it. */
77
+ projectDir?: string;
78
+ /** The server, for the receipt store a backfill step keeps its receipts in. */
79
+ endpoint?: ClickHouseEndpoint;
80
+ /** What each statement is rendered for when it is sent (`../config/settings.ts`). Default: a single node. */
81
+ topology?: Topology;
82
+ /** Where {@link topology} came from: `yodel.config.ts`, `sql.profiles.<env>.topology`, `YODEL_TOPOLOGY` or `default`. */
83
+ topologySource?: string;
84
+ /** Which server this is, with its version (#11), for the header of status, plan and apply. */
85
+ server?: () => Promise<ServerInfo>;
86
+ }
87
+ /**
88
+ * Binds an environment's server (`sql.profiles.<env>`) and history. `port`
89
+ * (ClickHouse) and `pgConnect` (Postgres) stand in for the server in tests.
90
+ * Close it with {@link closeVersioned} when done: on Postgres it holds a
91
+ * connection.
92
+ */
93
+ export declare function openVersioned(project: Project, env: Environment, processEnv?: NodeJS.ProcessEnv, port?: ClickHousePort, pgConnect?: PgConnect): Promise<VersionedEnv>;
94
+ /** Closes what {@link openVersioned} opened (a Postgres connection). Safe to call twice. */
95
+ export declare const closeVersioned: (venv: VersionedEnv) => Promise<void>;
96
+ /** The history and how the directory compares with it. Reads only; takes no lock. */
97
+ export declare function inspect(venv: VersionedEnv, ordered: readonly Migration[], options?: {
98
+ sync?: boolean;
99
+ }): Promise<{
100
+ state: HistoryState;
101
+ status: VersionedStatus;
102
+ }>;
103
+ /** The refusal for an integrity problem, or `undefined`. */
104
+ export declare function integrityRefusal(status: VersionedStatus, allowOutOfOrder: boolean): YodelError | undefined;
105
+ /** The refusal for the steps among `toApply` that apply does not run (manual steps, Ops it does not run), or `undefined`. */
106
+ export declare function stepRefusal(toApply: readonly PendingMigration[]): YodelError | undefined;
107
+ /** What a run would apply, in chain order, or the refusal. */
108
+ export declare function selectToApply(status: VersionedStatus, allowOutOfOrder: boolean): PendingMigration[];
109
+ /** The plan digest of applying `toApply` from the server's state now. */
110
+ export declare function currentDigest(venv: VersionedEnv, status: VersionedStatus, toApply: readonly PendingMigration[]): Promise<string>;
111
+ export declare class ApplyFailed extends YodelError {
112
+ readonly migration: string;
113
+ readonly statement: number;
114
+ readonly serverError: string;
115
+ constructor(migration: string, statement: number, serverError: string);
116
+ }
117
+ /** An Op step failed or stopped part way; the next run resumes it from its receipts. */
118
+ export declare class StepFailed extends YodelError {
119
+ readonly migration: string;
120
+ readonly step: number;
121
+ readonly detail: string;
122
+ constructor(migration: string, step: number, detail: string);
123
+ }
124
+ export interface ExecuteOptions {
125
+ /** The digest the Op's gate handed the Apply step. A run whose digest has moved since is refused. */
126
+ approvedDigest: string;
127
+ /**
128
+ * Checks the approval itself, given the digest computed under the lock;
129
+ * throws to refuse. `--execute` looks it up in chant's gate ledger
130
+ * (`./approval.ts`). Required: the digest alone is never an approval.
131
+ */
132
+ approve: (digest: string) => Promise<unknown>;
133
+ allowOutOfOrder: boolean;
134
+ /** Progress, one line at a time. */
135
+ log: (line: string) => void;
136
+ /** The silences in force for a migration, as JSON text for the history's `silences` field. Default `"[]"`. */
137
+ silences?: (m: Migration) => string;
138
+ processEnv?: NodeJS.ProcessEnv;
139
+ /**
140
+ * Judges the policy (#53) for the digest computed under the lock; throws to
141
+ * refuse. Returns the overrides the run relies on, which each migration's
142
+ * `started` row records. Absent: no policy.
143
+ */
144
+ policy?: (digest: string, toApply: readonly PendingMigration[]) => Promise<PolicyOverride[]>;
145
+ /** Runs an Op step. Default: chant's local executor (`../steps/`). */
146
+ runStep?: StepRunner;
147
+ /** The receipt store a backfill step uses. Default: chant's, on the server. */
148
+ receipts?: ReceiptStore;
149
+ /** How long to wait for a lock another apply holds. Default: the environment's ({@link VersionedEnv.lockWaitMs}). */
150
+ lockWaitMs?: number;
151
+ /**
152
+ * `--stand-down`: asked before the lock is taken and between tries while
153
+ * it waits; a line (`./stand-down.ts`) when a newer commit will apply the
154
+ * environment, and this run then applies nothing.
155
+ */
156
+ standDown?: () => Promise<string | undefined>;
157
+ /** How long to sleep between tries for a held lock. Default 1 s. */
158
+ lockPollMs?: number;
159
+ }
160
+ /** How long a run waited for the lock, and who held it. */
161
+ export interface LockWait {
162
+ ms: number;
163
+ /** Who held it when the run first found it held, as the lock names its owner. */
164
+ heldBy: string;
165
+ where: string;
166
+ }
167
+ export interface ExecuteResult {
168
+ applied: string[];
169
+ /** Statements not sent because an earlier run had already succeeded with them. */
170
+ skipped: Array<{
171
+ id: string;
172
+ statement: number;
173
+ }>;
174
+ lock: {
175
+ kind: "keeper" | "local" | "advisory";
176
+ where: string;
177
+ notice?: string;
178
+ waited?: LockWait;
179
+ };
180
+ /** Set when the run stood down (`--stand-down`) and applied nothing: why. */
181
+ stoodDown?: string;
182
+ }
183
+ /**
184
+ * Takes the lock, waiting up to `waitMs` while another apply holds it: a
185
+ * line naming the holder when the wait starts and whenever the holder
186
+ * changes, a try every `pollMs`. A lock still held at the end of the wait
187
+ * is the {@link LockHeldError} it was (exit 5), saying how long it waited.
188
+ * With `standDown`, a newer commit ends the wait: `{ stoodDown }`.
189
+ */
190
+ export declare function acquireWaiting(venv: Pick<VersionedEnv, "driver">, holder: string, options: {
191
+ waitMs: number;
192
+ pollMs?: number;
193
+ log: (line: string) => void;
194
+ standDown?: (() => Promise<string | undefined>) | undefined;
195
+ }): Promise<{
196
+ lock: Lock;
197
+ waited?: LockWait;
198
+ } | {
199
+ stoodDown: string;
200
+ }>;
201
+ /**
202
+ * Applies the pending migrations: takes the lock, reads the history again
203
+ * under it, refuses on any integrity problem or a digest that moved since
204
+ * approval, then runs each statement not yet succeeded.
205
+ */
206
+ export declare function executeApply(venv: VersionedEnv, ordered: readonly Migration[], options: ExecuteOptions): Promise<ExecuteResult>;