okmodel 0.0.0 → 0.1.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 (147) hide show
  1. package/README.md +44 -1
  2. package/dist/adapters/capabilities.d.ts +62 -0
  3. package/dist/adapters/error.d.ts +91 -0
  4. package/dist/adapters/failure.d.ts +39 -0
  5. package/dist/adapters/pg/batch.d.ts +36 -0
  6. package/dist/adapters/pg/call.d.ts +59 -0
  7. package/dist/adapters/pg/pglite.d.ts +21 -0
  8. package/dist/adapters/pg/postgres-extra.d.ts +43 -0
  9. package/dist/adapters/pg/postgresjs.d.ts +95 -0
  10. package/dist/adapters/pg/result.d.ts +30 -0
  11. package/dist/contracts/catalog/build.d.ts +18 -0
  12. package/dist/contracts/catalog/document.d.ts +79 -0
  13. package/dist/contracts/catalog/enum.d.ts +35 -0
  14. package/dist/contracts/catalog/identifier.d.ts +71 -0
  15. package/dist/contracts/catalog/identity.d.ts +98 -0
  16. package/dist/contracts/catalog/index.d.ts +11 -0
  17. package/dist/contracts/catalog/json.d.ts +17 -0
  18. package/dist/contracts/catalog/object.d.ts +142 -0
  19. package/dist/contracts/catalog/order.d.ts +43 -0
  20. package/dist/contracts/catalog/rewrite.d.ts +18 -0
  21. package/dist/contracts/catalog/types.d.ts +292 -0
  22. package/dist/contracts/catalog/words.d.ts +13 -0
  23. package/dist/contracts/connection.d.ts +33 -0
  24. package/dist/contracts/driver.d.ts +163 -0
  25. package/dist/contracts/error.d.ts +298 -0
  26. package/dist/contracts/index.d.ts +11 -0
  27. package/dist/contracts/index.js +16 -0
  28. package/dist/contracts/internal.d.ts +15 -0
  29. package/dist/contracts/internal.js +68 -0
  30. package/dist/contracts/nearest.d.ts +15 -0
  31. package/dist/contracts/rows.d.ts +84 -0
  32. package/dist/contracts/sha256.d.ts +14 -0
  33. package/dist/contracts/utf8.d.ts +33 -0
  34. package/dist/dialects/pg/array-literal.d.ts +25 -0
  35. package/dist/dialects/pg/bool.d.ts +46 -0
  36. package/dist/dialects/pg/column.d.ts +386 -0
  37. package/dist/dialects/pg/compile.d.ts +81 -0
  38. package/dist/dialects/pg/custom.d.ts +21 -0
  39. package/dist/dialects/pg/ddl.d.ts +70 -0
  40. package/dist/dialects/pg/decimal.d.ts +62 -0
  41. package/dist/dialects/pg/declarations.d.ts +34 -0
  42. package/dist/dialects/pg/emit.d.ts +18 -0
  43. package/dist/dialects/pg/enum.d.ts +30 -0
  44. package/dist/dialects/pg/errors.d.ts +30 -0
  45. package/dist/dialects/pg/finite.d.ts +15 -0
  46. package/dist/dialects/pg/geometry.d.ts +55 -0
  47. package/dist/dialects/pg/index.d.ts +45 -0
  48. package/dist/dialects/pg/index.js +1821 -0
  49. package/dist/dialects/pg/integer.d.ts +96 -0
  50. package/dist/dialects/pg/introspect.d.ts +43 -0
  51. package/dist/dialects/pg/json.d.ts +43 -0
  52. package/dist/dialects/pg/keys.d.ts +32 -0
  53. package/dist/dialects/pg/misuse.d.ts +31 -0
  54. package/dist/dialects/pg/model.d.ts +71 -0
  55. package/dist/dialects/pg/namespace.d.ts +63 -0
  56. package/dist/dialects/pg/network.d.ts +28 -0
  57. package/dist/dialects/pg/operators.d.ts +71 -0
  58. package/dist/dialects/pg/ops/between.d.ts +10 -0
  59. package/dist/dialects/pg/ops/contains.d.ts +8 -0
  60. package/dist/dialects/pg/ops/endsWith.d.ts +8 -0
  61. package/dist/dialects/pg/ops/eq.d.ts +9 -0
  62. package/dist/dialects/pg/ops/every.d.ts +9 -0
  63. package/dist/dialects/pg/ops/gt.d.ts +9 -0
  64. package/dist/dialects/pg/ops/gte.d.ts +9 -0
  65. package/dist/dialects/pg/ops/has.d.ts +9 -0
  66. package/dist/dialects/pg/ops/ilike.d.ts +8 -0
  67. package/dist/dialects/pg/ops/inList.d.ts +9 -0
  68. package/dist/dialects/pg/ops/inc.d.ts +13 -0
  69. package/dist/dialects/pg/ops/like.d.ts +8 -0
  70. package/dist/dialects/pg/ops/lt.d.ts +9 -0
  71. package/dist/dialects/pg/ops/lte.d.ts +9 -0
  72. package/dist/dialects/pg/ops/none.d.ts +9 -0
  73. package/dist/dialects/pg/ops/not.d.ts +9 -0
  74. package/dist/dialects/pg/ops/notIn.d.ts +9 -0
  75. package/dist/dialects/pg/ops/or.d.ts +9 -0
  76. package/dist/dialects/pg/ops/startsWith.d.ts +8 -0
  77. package/dist/dialects/pg/quote.d.ts +24 -0
  78. package/dist/dialects/pg/range.d.ts +50 -0
  79. package/dist/dialects/pg/relations.d.ts +45 -0
  80. package/dist/dialects/pg/schema.d.ts +77 -0
  81. package/dist/dialects/pg/search.d.ts +23 -0
  82. package/dist/dialects/pg/table.d.ts +201 -0
  83. package/dist/dialects/pg/temporal.d.ts +211 -0
  84. package/dist/dialects/pg/text.d.ts +44 -0
  85. package/dist/dialects/pg/time.d.ts +46 -0
  86. package/dist/okm.js +2 -0
  87. package/dist/okmodel.js +2 -0
  88. package/dist/runtime/client.d.ts +47 -0
  89. package/dist/runtime/conflict.d.ts +38 -0
  90. package/dist/runtime/drift-detail.d.ts +32 -0
  91. package/dist/runtime/drift.d.ts +29 -0
  92. package/dist/runtime/include.d.ts +15 -0
  93. package/dist/runtime/pg/pglite.d.ts +25 -0
  94. package/dist/runtime/pg/pglite.js +48 -0
  95. package/dist/runtime/pg/postgresjs.d.ts +29 -0
  96. package/dist/runtime/pg/postgresjs.js +62 -0
  97. package/dist/runtime/plan.d.ts +167 -0
  98. package/dist/runtime/tx.d.ts +29 -0
  99. package/dist/runtime/types.d.ts +369 -0
  100. package/dist/runtime/write.d.ts +48 -0
  101. package/dist/shared/4j72449d.js +37 -0
  102. package/dist/shared/79ggp945.js +148 -0
  103. package/dist/shared/7ck0dtry.js +259 -0
  104. package/dist/shared/8b04d28c.js +14 -0
  105. package/dist/shared/ax16zpvm.js +171 -0
  106. package/dist/shared/ckb1p0a4.js +566 -0
  107. package/dist/shared/cn0n68ft.js +16 -0
  108. package/dist/shared/dynq16vb.js +81 -0
  109. package/dist/shared/f5thc1s0.js +1050 -0
  110. package/dist/shared/fgqgsb5z.js +281 -0
  111. package/dist/shared/g2v6gnc8.js +347 -0
  112. package/dist/shared/g5fhqmwe.js +188 -0
  113. package/dist/shared/gk7cs7sp.js +566 -0
  114. package/dist/shared/hd293j9g.js +98 -0
  115. package/dist/shared/j5cn2963.js +701 -0
  116. package/dist/shared/kynx7vbm.js +1490 -0
  117. package/dist/shared/mkjmqjv4.js +456 -0
  118. package/dist/shared/mtkwv8wx.js +18 -0
  119. package/dist/shared/ngk8gj61.js +40 -0
  120. package/dist/shared/p0cbr3g2.js +79 -0
  121. package/dist/shared/px90pf9p.js +396 -0
  122. package/dist/shared/qk3hxfja.js +83 -0
  123. package/dist/shared/tb4ze1tr.js +119 -0
  124. package/dist/shared/vkm3q4x1.js +30 -0
  125. package/dist/shared/w8samvk1.js +18 -0
  126. package/dist/shared/xy79j5hq.js +177 -0
  127. package/dist/shared/y9m15dfv.js +888 -0
  128. package/dist/shared/ycwz42az.js +412 -0
  129. package/dist/shared/zhc5vmgf.js +257 -0
  130. package/dist/tooling/cli.d.ts +1 -0
  131. package/dist/tooling/cli.js +193 -0
  132. package/dist/tooling/errors/registry.d.ts +33 -0
  133. package/dist/tooling/migrate/apply.d.ts +86 -0
  134. package/dist/tooling/migrate/commands.d.ts +21 -0
  135. package/dist/tooling/migrate/config.d.ts +48 -0
  136. package/dist/tooling/migrate/dev.d.ts +16 -0
  137. package/dist/tooling/migrate/equal.d.ts +15 -0
  138. package/dist/tooling/migrate/files.d.ts +19 -0
  139. package/dist/tooling/migrate/index.d.ts +7 -0
  140. package/dist/tooling/migrate/index.js +7 -0
  141. package/dist/tooling/migrate/plan.d.ts +76 -0
  142. package/dist/tooling/migrate/policy.d.ts +78 -0
  143. package/dist/tooling/migrate/project.d.ts +69 -0
  144. package/dist/tooling/migrate/status.d.ts +74 -0
  145. package/dist/tooling/migrate/values.d.ts +29 -0
  146. package/package.json +94 -7
  147. package/dist/index.js +0 -0
@@ -0,0 +1,281 @@
1
+ import {
2
+ PGLITE_CAPABILITIES,
3
+ driverErrors,
4
+ rejectClosed,
5
+ rejectCancelled,
6
+ rejectTimedOut,
7
+ mapFailure,
8
+ nextTransactionDepth,
9
+ runCall,
10
+ EMPTY_NOTICES,
11
+ resultFrom
12
+ } from "./xy79j5hq.js";
13
+
14
+ // src/adapters/pg/pglite.ts
15
+ import { PGlite } from "@electric-sql/pglite";
16
+ var RESET_ALL = "RESET ALL";
17
+ var UNLOCK = "SELECT pg_advisory_unlock_all()";
18
+ async function open3(config = {}) {
19
+ const db = new PGlite({
20
+ dataDir: config.dataDir ?? "memory://",
21
+ parsers: WIRE,
22
+ serializers: WIRE
23
+ });
24
+ await db.waitReady;
25
+ const acquireMs = config.timeouts?.acquire;
26
+ const slot = new Slot;
27
+ const notices = noticeBuffer();
28
+ const queryOptions = {
29
+ rowMode: "array",
30
+ parsers: WIRE,
31
+ serializers: WIRE,
32
+ onNotice(notice) {
33
+ notices.push(notice);
34
+ }
35
+ };
36
+ let closed = false;
37
+ let inflight = 0;
38
+ const session = new PgliteSession(db, queryOptions, notices, () => {
39
+ inflight += 1;
40
+ }, () => {
41
+ inflight = Math.max(0, inflight - 1);
42
+ });
43
+ async function lease() {
44
+ if (closed)
45
+ return rejectClosed();
46
+ await slot.acquire(acquireMs);
47
+ let released = false;
48
+ return {
49
+ execute: (text, params, options) => runCall(closed, options, (watch) => session.query(text, params, watch)),
50
+ batch: (statements, options) => runCall(closed, options, async (watch) => {
51
+ const { runAtomicBatch } = await import("./dynq16vb.js");
52
+ return runAtomicBatch(session, statements, watch);
53
+ }),
54
+ async release() {
55
+ if (released)
56
+ return;
57
+ released = true;
58
+ try {
59
+ await reset(session);
60
+ } finally {
61
+ slot.release();
62
+ }
63
+ }
64
+ };
65
+ }
66
+ return {
67
+ capabilities: PGLITE_CAPABILITIES,
68
+ execute(text, params, options) {
69
+ return runCall(closed, options, async (watch) => {
70
+ await slot.acquire(acquireMs);
71
+ try {
72
+ return await session.query(text, params, watch);
73
+ } finally {
74
+ slot.release();
75
+ }
76
+ });
77
+ },
78
+ async batch(statements, options) {
79
+ const connection = await lease();
80
+ try {
81
+ return await connection.batch(statements, options);
82
+ } finally {
83
+ await connection.release();
84
+ }
85
+ },
86
+ reserve: () => lease(),
87
+ async describe(text) {
88
+ await slot.acquire(acquireMs);
89
+ try {
90
+ const described = await db.describeQuery(text);
91
+ return {
92
+ columns: described.resultFields.map((field) => field.name),
93
+ parameterCount: described.queryParams.length
94
+ };
95
+ } finally {
96
+ slot.release();
97
+ }
98
+ },
99
+ async listen(channel, onNotify) {
100
+ const unlisten = await db.listen(channel, onNotify);
101
+ return async () => {
102
+ await unlisten();
103
+ };
104
+ },
105
+ stats() {
106
+ return { size: 1, idle: slot.idle, inflight, waiting: slot.waiting };
107
+ },
108
+ async close() {
109
+ closed = true;
110
+ await db.close();
111
+ }
112
+ };
113
+ }
114
+
115
+ class PgliteSession {
116
+ canCancel = false;
117
+ transactionDepth = 0;
118
+ db;
119
+ options;
120
+ notices;
121
+ enter;
122
+ leave;
123
+ constructor(db, options, notices, enter, leave) {
124
+ this.db = db;
125
+ this.options = options;
126
+ this.notices = notices;
127
+ this.enter = enter;
128
+ this.leave = leave;
129
+ }
130
+ query(text, params, watch) {
131
+ const why = watch?.reason();
132
+ if (why === "timeout")
133
+ return rejectTimedOut();
134
+ if (why === "cancelled" || watch?.signal.aborted === true)
135
+ return rejectCancelled();
136
+ this.enter();
137
+ const pending = params === undefined || params.length === 0 ? this.db.query(text, undefined, this.options) : this.db.query(text, params, this.options);
138
+ return pending.then((result) => {
139
+ this.leave();
140
+ this.note(text);
141
+ return resultFrom(result, this.notices.take());
142
+ }, (error) => {
143
+ this.leave();
144
+ this.notices.take();
145
+ return mapFailure(error).then((mapped) => {
146
+ throw mapped;
147
+ });
148
+ });
149
+ }
150
+ inTransaction() {
151
+ return this.transactionDepth > 0;
152
+ }
153
+ abandon() {
154
+ this.transactionDepth = 0;
155
+ }
156
+ rollback() {
157
+ return this.query("ROLLBACK", undefined, undefined).then(() => {
158
+ this.transactionDepth = 0;
159
+ }, (error) => {
160
+ this.transactionDepth = 0;
161
+ throw error;
162
+ });
163
+ }
164
+ note(text) {
165
+ this.transactionDepth = nextTransactionDepth(this.transactionDepth, text);
166
+ }
167
+ }
168
+ function noticeBuffer() {
169
+ let notices;
170
+ return {
171
+ push(notice) {
172
+ const severity = notice.severity ?? "";
173
+ const message = notice.message ?? "";
174
+ const code = notice.code !== undefined && notice.code.length > 0 ? notice.code : undefined;
175
+ const entry = code === undefined ? { severity, message } : { severity, message, code };
176
+ (notices ??= []).push(entry);
177
+ },
178
+ take() {
179
+ const taken = notices ?? EMPTY_NOTICES;
180
+ notices = undefined;
181
+ return taken;
182
+ }
183
+ };
184
+ }
185
+
186
+ class Slot {
187
+ held = false;
188
+ waiters = [];
189
+ acquire(acquireMs) {
190
+ if (!this.held) {
191
+ this.held = true;
192
+ return Promise.resolve();
193
+ }
194
+ return new Promise((resolve, reject) => {
195
+ const waiter = {
196
+ resolve,
197
+ timer: undefined
198
+ };
199
+ if (acquireMs !== undefined) {
200
+ waiter.timer = setTimeout(() => {
201
+ const index = this.waiters.indexOf(waiter);
202
+ if (index !== -1)
203
+ this.waiters.splice(index, 1);
204
+ driverErrors().then((errors) => {
205
+ reject(errors.acquireTimeout());
206
+ });
207
+ }, acquireMs);
208
+ }
209
+ this.waiters.push(waiter);
210
+ });
211
+ }
212
+ release() {
213
+ const next = this.waiters.shift();
214
+ if (next === undefined) {
215
+ this.held = false;
216
+ return;
217
+ }
218
+ if (next.timer !== undefined)
219
+ clearTimeout(next.timer);
220
+ next.resolve();
221
+ }
222
+ get idle() {
223
+ return this.held ? 0 : 1;
224
+ }
225
+ get waiting() {
226
+ return this.waiters.length;
227
+ }
228
+ }
229
+ async function reset(session) {
230
+ if (session.inTransaction())
231
+ await session.rollback().catch(() => {
232
+ return;
233
+ });
234
+ try {
235
+ await session.query(RESET_ALL, undefined, undefined);
236
+ await session.query(UNLOCK, undefined, undefined);
237
+ } catch {
238
+ await session.rollback().catch(() => {
239
+ return;
240
+ });
241
+ await session.query(RESET_ALL, undefined, undefined);
242
+ await session.query(UNLOCK, undefined, undefined);
243
+ }
244
+ }
245
+ function wireMap(oids) {
246
+ const parsers = {};
247
+ for (const oid of oids)
248
+ parsers[oid] = (value) => value;
249
+ return parsers;
250
+ }
251
+ var WIRE = wireMap([
252
+ 16,
253
+ 17,
254
+ 20,
255
+ 21,
256
+ 23,
257
+ 25,
258
+ 114,
259
+ 700,
260
+ 701,
261
+ 1043,
262
+ 1082,
263
+ 1114,
264
+ 1184,
265
+ 1700,
266
+ 2950,
267
+ 3802,
268
+ 1000,
269
+ 1005,
270
+ 1007,
271
+ 1009,
272
+ 1016,
273
+ 1115,
274
+ 1182,
275
+ 1185,
276
+ 1231,
277
+ 199,
278
+ 3807
279
+ ]);
280
+
281
+ export { open3 };
@@ -0,0 +1,347 @@
1
+ import {
2
+ open2
3
+ } from "./7ck0dtry.js";
4
+ import {
5
+ catalogHash2
6
+ } from "./y9m15dfv.js";
7
+ import {
8
+ loadMigrations
9
+ } from "./4j72449d.js";
10
+ import {
11
+ quoteIdent,
12
+ planMigration,
13
+ assertTargetPolicy,
14
+ assertDirectConnection,
15
+ selectTarget,
16
+ projectHead,
17
+ loadConfig
18
+ } from "./kynx7vbm.js";
19
+ import {
20
+ OkmError2
21
+ } from "./ycwz42az.js";
22
+
23
+ // src/tooling/migrate/apply.ts
24
+ import { join } from "node:path";
25
+ var DEFAULT_LOCK_MS = 5000;
26
+ var DEFAULT_STATEMENT_MS = 30000;
27
+ var DEFAULT_RETRIES = 3;
28
+ var BACKOFF_MS = 50;
29
+ var META = `create table if not exists okm_meta (
30
+ id text primary key,
31
+ catalog_hash text not null,
32
+ migration_id text not null
33
+ )`;
34
+ var HISTORY = `create table if not exists okm_history (
35
+ migration_id text not null,
36
+ step_index integer not null,
37
+ class text not null,
38
+ catalog_hash text not null,
39
+ applied_at timestamptz not null default now(),
40
+ primary key (migration_id, step_index)
41
+ )`;
42
+ function applyUnits(migration, done) {
43
+ const pending = [];
44
+ for (let index = 0;index < migration.steps.length; index += 1) {
45
+ if (done.has(`${migration.id}:${String(index)}`))
46
+ continue;
47
+ const step = migration.steps[index];
48
+ if (step !== undefined)
49
+ pending.push({ index, step });
50
+ }
51
+ const units = [];
52
+ let batch = [];
53
+ const flush = () => {
54
+ if (batch.length === 0)
55
+ return;
56
+ const last = batch.at(-1);
57
+ units.push({
58
+ migrationId: migration.id,
59
+ catalogHash: migration.catalogHash,
60
+ finishes: last !== undefined && last.index === migration.steps.length - 1,
61
+ transactional: true,
62
+ steps: batch
63
+ });
64
+ batch = [];
65
+ };
66
+ for (const item of pending) {
67
+ if (item.step.transactional && !outsideTransaction(item.step.sql)) {
68
+ batch.push(item);
69
+ continue;
70
+ }
71
+ flush();
72
+ units.push({
73
+ migrationId: migration.id,
74
+ catalogHash: migration.catalogHash,
75
+ finishes: item.index === migration.steps.length - 1,
76
+ transactional: false,
77
+ steps: [item]
78
+ });
79
+ }
80
+ flush();
81
+ return units;
82
+ }
83
+ async function applyTarget(request) {
84
+ assertDirectConnection(request.url, request.allowPooler === true);
85
+ const pool = open2({
86
+ url: request.url,
87
+ max: 1,
88
+ ...request.searchPath !== undefined ? { searchPath: request.searchPath } : {}
89
+ });
90
+ if (pool.reserve === undefined) {
91
+ await pool.close();
92
+ throw new OkmError2("invalid", "The driver cannot reserve a connection.", {
93
+ fix: { summary: "Apply needs a driver with interactive transactions." }
94
+ });
95
+ }
96
+ const connection = await pool.reserve();
97
+ let locked = false;
98
+ try {
99
+ await setTimeouts(connection, request);
100
+ await lockTarget(connection, request.target);
101
+ locked = true;
102
+ request.onLocked?.();
103
+ await connection.execute(META);
104
+ await connection.execute(HISTORY);
105
+ const done = await readDone(connection);
106
+ const applied = [];
107
+ for (const migration of request.migrations) {
108
+ const units = applyUnits(migration, done);
109
+ if (units.length === 0)
110
+ continue;
111
+ for (const unit of units) {
112
+ assertUnitPolicy(request, unit);
113
+ await runUnit(connection, unit, request);
114
+ for (const item of unit.steps)
115
+ done.add(`${unit.migrationId}:${String(item.index)}`);
116
+ }
117
+ applied.push(migration.id);
118
+ }
119
+ return { target: request.target, applied };
120
+ } finally {
121
+ if (locked)
122
+ await connection.execute("select pg_advisory_unlock(hashtext($1))", [
123
+ lockKey(request.target)
124
+ ]);
125
+ await connection.release();
126
+ await pool.close();
127
+ }
128
+ }
129
+ async function applyProject(cwd, flags) {
130
+ const config = await loadConfig(cwd);
131
+ const target = selectTarget(config, flags.target);
132
+ const migrations = loadMigrations(joinMigrations(cwd, config.migrations));
133
+ if (migrations.length === 0)
134
+ return `target ${target.name}
135
+ nothing to apply
136
+ `;
137
+ const report = await applyTarget({
138
+ url: target.url,
139
+ target: target.name,
140
+ protected: target.protected,
141
+ allowProtected: flags.allowProtected,
142
+ allowPooler: flags.allowPooler || config.allowPooler === true,
143
+ ...flags.lockTimeoutMs !== undefined ? { lockTimeoutMs: flags.lockTimeoutMs } : config.timeouts?.lock !== undefined ? { lockTimeoutMs: config.timeouts.lock } : {},
144
+ ...flags.statementTimeoutMs !== undefined ? { statementTimeoutMs: flags.statementTimeoutMs } : config.timeouts?.statement !== undefined ? { statementTimeoutMs: config.timeouts.statement } : {},
145
+ migrations
146
+ });
147
+ return formatReport(report);
148
+ }
149
+ async function pushProject(cwd, flags) {
150
+ const config = await loadConfig(cwd);
151
+ const target = selectTarget(config, flags.target);
152
+ assertTargetPolicy(target, "push", flags.allowProtected);
153
+ const head = await projectHead(cwd);
154
+ const plan = planMigration({
155
+ before: head.previous,
156
+ after: head.catalog,
157
+ renames: head.renames,
158
+ name: "push"
159
+ });
160
+ if (plan.steps.length === 0)
161
+ return `target ${target.name}
162
+ no changes
163
+ `;
164
+ const report = await applyTarget({
165
+ url: target.url,
166
+ target: target.name,
167
+ protected: target.protected,
168
+ allowProtected: flags.allowProtected,
169
+ allowPooler: flags.allowPooler || config.allowPooler === true,
170
+ migrations: [
171
+ {
172
+ id: "push",
173
+ catalogHash: catalogHash2(head.catalog),
174
+ steps: plan.steps
175
+ }
176
+ ]
177
+ });
178
+ return formatReport(report);
179
+ }
180
+ function formatReport(report) {
181
+ if (report.applied.length === 0)
182
+ return `target ${report.target}
183
+ nothing to apply
184
+ `;
185
+ return `target ${report.target}
186
+ ${report.applied.map((id) => `applied ${id}`).join(`
187
+ `)}
188
+ `;
189
+ }
190
+ function joinMigrations(cwd, migrations) {
191
+ return join(cwd, migrations ?? "migrations");
192
+ }
193
+ function assertUnitPolicy(request, unit) {
194
+ let operation = "expand";
195
+ for (const item of unit.steps) {
196
+ if (item.step.action === "backfill") {
197
+ operation = "backfill";
198
+ break;
199
+ }
200
+ if (item.step.class === "contract" || item.step.class === "unclassified")
201
+ operation = item.step.class;
202
+ }
203
+ assertTargetPolicy({ name: request.target, protected: request.protected }, operation, request.allowProtected === true);
204
+ }
205
+ async function setTimeouts(connection, request) {
206
+ const lock = request.lockTimeoutMs ?? DEFAULT_LOCK_MS;
207
+ const statement = request.statementTimeoutMs ?? DEFAULT_STATEMENT_MS;
208
+ await connection.execute("select set_config('lock_timeout', $1, false)", [String(lock)]);
209
+ await connection.execute("select set_config('statement_timeout', $1, false)", [
210
+ String(statement)
211
+ ]);
212
+ }
213
+ async function lockTarget(connection, target) {
214
+ const result = await connection.execute("select pg_try_advisory_lock(hashtext($1))", [
215
+ lockKey(target)
216
+ ]);
217
+ if (!wireTrue(result.rows[0]?.[0])) {
218
+ throw new OkmError2("OKM1522", `Another apply holds the lock for ${target}.`, {
219
+ kind: "conflict",
220
+ fix: {
221
+ summary: "Wait for that apply to finish. Do not start a second apply against the same target."
222
+ }
223
+ });
224
+ }
225
+ }
226
+ function lockKey(target) {
227
+ return `okm:${target}`;
228
+ }
229
+ async function readDone(connection) {
230
+ const result = await connection.execute("select migration_id, step_index::text from okm_history");
231
+ const done = new Set;
232
+ for (const row of result.rows) {
233
+ const id = row[0];
234
+ const step = row[1];
235
+ if (id !== null && step !== null)
236
+ done.add(`${id}:${step}`);
237
+ }
238
+ return done;
239
+ }
240
+ async function runUnit(connection, unit, request) {
241
+ const retries = request.retries ?? DEFAULT_RETRIES;
242
+ for (let attempt = 0;attempt <= retries; attempt += 1) {
243
+ let began = false;
244
+ let current = unit.steps[0]?.index ?? 0;
245
+ try {
246
+ if (unit.transactional) {
247
+ await connection.execute("begin");
248
+ began = true;
249
+ }
250
+ for (const item of unit.steps) {
251
+ current = item.index;
252
+ await rebuildInvalidIndex(connection, item.step.sql);
253
+ await connection.execute(item.step.sql);
254
+ if (!unit.transactional) {
255
+ await connection.execute("begin");
256
+ began = true;
257
+ await writeStep(connection, unit, item);
258
+ if (unit.finishes)
259
+ await writeMeta(connection, unit);
260
+ await connection.execute("commit");
261
+ began = false;
262
+ }
263
+ }
264
+ if (unit.transactional) {
265
+ for (const item of unit.steps)
266
+ await writeStep(connection, unit, item);
267
+ if (unit.finishes)
268
+ await writeMeta(connection, unit);
269
+ await connection.execute("commit");
270
+ began = false;
271
+ }
272
+ return;
273
+ } catch (error) {
274
+ if (began)
275
+ await connection.execute("rollback").catch(() => {
276
+ return;
277
+ });
278
+ if (!isLockTimeout(error) || attempt === retries)
279
+ throw failStep(unit, current, error);
280
+ await delay(BACKOFF_MS * 2 ** attempt);
281
+ }
282
+ }
283
+ }
284
+ async function writeStep(connection, unit, item) {
285
+ await connection.execute("insert into okm_history (migration_id, step_index, class, catalog_hash) values ($1, $2, $3, $4)", [unit.migrationId, String(item.index), item.step.class, unit.catalogHash]);
286
+ }
287
+ async function writeMeta(connection, unit) {
288
+ await connection.execute(`insert into okm_meta (id, catalog_hash, migration_id) values ('head', $1, $2)
289
+ on conflict (id) do update set catalog_hash = excluded.catalog_hash, migration_id = excluded.migration_id`, [unit.catalogHash, unit.migrationId]);
290
+ }
291
+ async function rebuildInvalidIndex(connection, sql) {
292
+ const name = concurrentIndexName(sql);
293
+ if (name === undefined)
294
+ return;
295
+ const found = await connection.execute(`select i.indisvalid
296
+ from pg_class c
297
+ join pg_namespace n on n.oid = c.relnamespace
298
+ join pg_index i on i.indexrelid = c.oid
299
+ where c.relname = $1 and n.nspname = current_schema()`, [name]);
300
+ const valid = found.rows[0]?.[0];
301
+ if (valid === undefined || wireTrue(valid))
302
+ return;
303
+ await connection.execute(`drop index concurrently if exists ${quoteIdent(name)}`);
304
+ }
305
+ function concurrentIndexName(sql) {
306
+ const match = /^create\s+(?:unique\s+)?index\s+concurrently\s+(?:if\s+not\s+exists\s+)?(?:"([^"]+)"|([A-Za-z_][\w$]*))/i.exec(sql.trim());
307
+ return match?.[1] ?? match?.[2];
308
+ }
309
+ function outsideTransaction(sql) {
310
+ const text = sql.trim().toLowerCase();
311
+ if (text.startsWith("vacuum"))
312
+ return true;
313
+ if (/^alter\s+type\b/.test(text) && /\badd\s+value\b/.test(text))
314
+ return true;
315
+ if (/^create\s+(?:unique\s+)?index\s+concurrently\b/.test(text))
316
+ return true;
317
+ return false;
318
+ }
319
+ function failStep(unit, index, error) {
320
+ if (error instanceof OkmError2)
321
+ return error;
322
+ const message = error instanceof Error ? error.message : "The step failed.";
323
+ return new OkmError2("invalid", `Migration ${unit.migrationId} failed at step ${String(index)}: ${message}`, {
324
+ cause: error,
325
+ fix: { summary: "Fix the step and run apply again. Finished steps are not repeated." }
326
+ });
327
+ }
328
+ function isLockTimeout(error) {
329
+ if (typeof error !== "object" || error === null)
330
+ return false;
331
+ if ("sqlstate" in error && error.sqlstate === "55P03")
332
+ return true;
333
+ const message = error instanceof Error ? error.message : "";
334
+ return message.includes("lock timeout");
335
+ }
336
+ function wireTrue(value) {
337
+ return value === "t" || value === "true";
338
+ }
339
+ function delay(ms) {
340
+ return new Promise((resolve) => {
341
+ setTimeout(resolve, ms);
342
+ });
343
+ }
344
+ export {
345
+ applyProject,
346
+ pushProject
347
+ };