@rehearsal-db/core 1.1.0 → 1.3.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 (73) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/COMPATIBILITY.md +40 -0
  3. package/README.md +7 -3
  4. package/dist/package.json +4 -1
  5. package/dist/src/cli/arguments.d.mts +1 -0
  6. package/dist/src/cli/arguments.mjs +5 -0
  7. package/dist/src/cli/basic_command_handlers.mjs +6 -0
  8. package/dist/src/cli/cli_commands.mjs +2 -0
  9. package/dist/src/cli/editor_command_handlers.mjs +24 -0
  10. package/dist/src/cli/project_commands.d.mts +8 -0
  11. package/dist/src/cli/runtime_commands.d.mts +2 -19
  12. package/dist/src/cli/runtime_commands.mjs +3 -90
  13. package/dist/src/cli/runtime_stack_runner.d.mts +41 -0
  14. package/dist/src/cli/runtime_stack_runner.mjs +104 -0
  15. package/dist/src/cli/runtime_workload_admission.d.mts +8 -0
  16. package/dist/src/cli/runtime_workload_admission.mjs +22 -0
  17. package/dist/src/cli/source_commands.d.mts +8 -0
  18. package/dist/src/cli/workload_command_handlers.d.mts +2 -0
  19. package/dist/src/cli/workload_command_handlers.mjs +38 -0
  20. package/dist/src/project/configuration_contract.d.mts +5 -0
  21. package/dist/src/project/configuration_normalization.d.mts +4 -0
  22. package/dist/src/project/configuration_resolution.d.mts +4 -0
  23. package/dist/src/project/configuration_resource_policy.d.mts +4 -0
  24. package/dist/src/project/configuration_resource_policy.mjs +20 -1
  25. package/dist/src/project/configuration_schema.mjs +1 -0
  26. package/dist/src/runtime/migration_history.d.mts +5 -1
  27. package/dist/src/runtime/migration_history.mjs +24 -6
  28. package/dist/src/runtime/migration_impact.d.mts +125 -0
  29. package/dist/src/runtime/migration_impact.mjs +278 -0
  30. package/dist/src/runtime/migration_impact_catalog.d.mts +18 -0
  31. package/dist/src/runtime/migration_impact_catalog.mjs +116 -0
  32. package/dist/src/runtime/migration_impact_plan.d.mts +47 -0
  33. package/dist/src/runtime/migration_impact_plan.mjs +297 -0
  34. package/dist/src/runtime/migration_impact_queries.d.mts +22 -0
  35. package/dist/src/runtime/migration_impact_queries.mjs +270 -0
  36. package/dist/src/runtime/migration_insights.d.mts +137 -0
  37. package/dist/src/runtime/migration_insights.mjs +143 -0
  38. package/dist/src/runtime/migration_insights_input.d.mts +13 -0
  39. package/dist/src/runtime/migration_insights_input.mjs +153 -0
  40. package/dist/src/runtime/migration_sql_lexer.d.mts +14 -0
  41. package/dist/src/runtime/migration_sql_lexer.mjs +204 -0
  42. package/dist/src/runtime/migration_sql_scan.d.mts +16 -0
  43. package/dist/src/runtime/migration_sql_scan.mjs +369 -0
  44. package/dist/src/runtime/plan.d.mts +2 -1
  45. package/dist/src/runtime/plan.mjs +4 -4
  46. package/dist/src/runtime/topology.d.mts +1 -1
  47. package/dist/src/runtime/topology.mjs +2 -2
  48. package/dist/src/runtime/workload_queue.d.mts +39 -0
  49. package/dist/src/runtime/workload_queue.mjs +211 -0
  50. package/dist/src/runtime/workload_queue_location.d.mts +1 -0
  51. package/dist/src/runtime/workload_queue_location.mjs +43 -0
  52. package/dist/src/runtime/workload_queue_store.d.mts +27 -0
  53. package/dist/src/runtime/workload_queue_store.mjs +182 -0
  54. package/dist/src/shared/cancellation.mjs +2 -0
  55. package/dist/src/shared/held_path_ancestry.d.mts +7 -0
  56. package/dist/src/shared/held_path_ancestry.mjs +110 -0
  57. package/dist/src/shared/operation_guard.mjs +1 -0
  58. package/dist/src/targets/migration_impact_runtime.d.mts +22 -0
  59. package/dist/src/targets/migration_impact_runtime.mjs +367 -0
  60. package/docs/README.md +20 -7
  61. package/docs/adapters.md +10 -0
  62. package/docs/architecture.md +84 -1
  63. package/docs/commands.md +189 -34
  64. package/docs/configuration.md +56 -6
  65. package/docs/getting-started.md +1 -1
  66. package/docs/index.md +1 -0
  67. package/docs/releasing.md +15 -9
  68. package/docs/roadmap.md +27 -6
  69. package/docs/runtime-lifecycle.md +10 -0
  70. package/docs/stability.md +4 -4
  71. package/docs/updating.md +73 -0
  72. package/docs/vscode.md +23 -17
  73. package/package.json +4 -1
@@ -0,0 +1,369 @@
1
+ import { lexMigrationSql, } from "./migration_sql_lexer.mjs";
2
+ const word = (token, value) => token?.kind === "word" && token.value === value;
3
+ const name = (token) => token?.kind === "word" || token?.kind === "identifier";
4
+ const qualifiedNameEnd = (tokens, start) => {
5
+ if (!name(tokens[start]))
6
+ return start;
7
+ let end = start + 1;
8
+ while (tokens[end]?.value === "." && name(tokens[end + 1]))
9
+ end += 2;
10
+ return end;
11
+ };
12
+ const topTokens = (tokens, depth) => tokens.filter((token) => token.depth === depth);
13
+ /** Static, local-only review hints. Never executes SQL or returns input text. */
14
+ export const scanMigrationSql = (source) => {
15
+ const lexical = lexMigrationSql(source);
16
+ const reasons = new Set(lexical.unknownReasons);
17
+ // Runtime-dependent plain-string escape settings can change statement boundaries.
18
+ if (lexical.ambiguousQuotes)
19
+ return {
20
+ complete: false,
21
+ findings: [],
22
+ unknownReasons: [...reasons].sort(),
23
+ };
24
+ const findings = [];
25
+ const findingKeys = new Set();
26
+ const add = (rule, token) => {
27
+ const key = `${rule}:${token.line}:${token.column}`;
28
+ if (findingKeys.has(key))
29
+ return;
30
+ if (findings.length >= 1000) {
31
+ reasons.add("input_limit");
32
+ return;
33
+ }
34
+ findingKeys.add(key);
35
+ findings.push({
36
+ rule,
37
+ severity: ["not_null", "new_constraint"].includes(rule)
38
+ ? "warning"
39
+ : "danger",
40
+ line: token.line,
41
+ column: token.column,
42
+ });
43
+ };
44
+ const unsupported = () => {
45
+ reasons.add("unsupported_sql");
46
+ };
47
+ const malformed = () => {
48
+ reasons.add("malformed_sql");
49
+ };
50
+ const cascade = (tokens, trailing = true) => {
51
+ const last = tokens.at(-1);
52
+ if (trailing && word(last, "CASCADE"))
53
+ add("cascade", last);
54
+ for (let index = 2; index < tokens.length; index += 1)
55
+ if (word(tokens[index], "CASCADE") &&
56
+ (word(tokens[index - 1], "DELETE") ||
57
+ word(tokens[index - 1], "UPDATE")) &&
58
+ word(tokens[index - 2], "ON"))
59
+ add("cascade", tokens[index]);
60
+ };
61
+ const relationList = (tokens, start) => {
62
+ if (start >= tokens.length)
63
+ return false;
64
+ let index = start;
65
+ while (index < tokens.length) {
66
+ if (word(tokens[index], "ONLY"))
67
+ index += 1;
68
+ const end = qualifiedNameEnd(tokens, index);
69
+ if (end === index)
70
+ return false;
71
+ index = end;
72
+ if (tokens[index]?.value === "*")
73
+ index += 1;
74
+ if (tokens[index]?.value !== ",")
75
+ break;
76
+ index += 1;
77
+ if (index === tokens.length)
78
+ return false;
79
+ }
80
+ if ((word(tokens[index], "RESTART") || word(tokens[index], "CONTINUE")) &&
81
+ word(tokens[index + 1], "IDENTITY"))
82
+ index += 2;
83
+ if (word(tokens[index], "CASCADE") || word(tokens[index], "RESTRICT"))
84
+ index += 1;
85
+ return index === tokens.length;
86
+ };
87
+ const analyze = (tokens, depth) => {
88
+ if (tokens.length === 0)
89
+ return;
90
+ const top = topTokens(tokens, depth);
91
+ const first = top[0];
92
+ if (!first || first.kind !== "word") {
93
+ unsupported();
94
+ return;
95
+ }
96
+ if (word(first, "WITH")) {
97
+ let index = 1;
98
+ if (word(tokens[index], "RECURSIVE"))
99
+ index += 1;
100
+ const afterGroup = (start) => {
101
+ const opening = tokens[start];
102
+ if (opening?.value !== "(")
103
+ return start;
104
+ const close = tokens.findIndex((token, candidate) => candidate > start &&
105
+ token.value === ")" &&
106
+ token.depth === opening.depth);
107
+ return close < 0 ? start : close + 1;
108
+ };
109
+ while (index < tokens.length) {
110
+ if (!name(tokens[index])) {
111
+ unsupported();
112
+ return;
113
+ }
114
+ index += 1;
115
+ if (tokens[index]?.value === "(") {
116
+ const next = afterGroup(index);
117
+ if (next === index) {
118
+ malformed();
119
+ return;
120
+ }
121
+ index = next;
122
+ }
123
+ if (!word(tokens[index], "AS")) {
124
+ unsupported();
125
+ return;
126
+ }
127
+ index += 1;
128
+ if (word(tokens[index], "NOT")) {
129
+ index += 1;
130
+ if (!word(tokens[index], "MATERIALIZED")) {
131
+ unsupported();
132
+ return;
133
+ }
134
+ }
135
+ if (word(tokens[index], "MATERIALIZED"))
136
+ index += 1;
137
+ const end = afterGroup(index);
138
+ if (end === index) {
139
+ unsupported();
140
+ return;
141
+ }
142
+ if (end === index + 2) {
143
+ malformed();
144
+ return;
145
+ }
146
+ analyze(tokens.slice(index + 1, end - 1), depth + 1);
147
+ index = end;
148
+ if (tokens[index]?.value !== ",")
149
+ break;
150
+ index += 1;
151
+ }
152
+ if (index >= tokens.length) {
153
+ malformed();
154
+ return;
155
+ }
156
+ analyze(tokens.slice(index), depth);
157
+ return;
158
+ }
159
+ if (word(first, "DROP") && word(top[1], "TABLE")) {
160
+ let start = 2;
161
+ if (word(top[start], "IF") && word(top[start + 1], "EXISTS"))
162
+ start += 2;
163
+ if (!relationList(top, start)) {
164
+ malformed();
165
+ return;
166
+ }
167
+ add("drop_table", first);
168
+ cascade(top);
169
+ return;
170
+ }
171
+ if (word(first, "TRUNCATE")) {
172
+ const start = word(top[1], "TABLE") ? 2 : 1;
173
+ if (!relationList(top, start)) {
174
+ malformed();
175
+ return;
176
+ }
177
+ add("truncate", first);
178
+ cascade(top);
179
+ return;
180
+ }
181
+ if (word(first, "UPDATE") || word(first, "DELETE")) {
182
+ const update = word(first, "UPDATE");
183
+ let start = update ? 1 : 2;
184
+ if (!update && !word(top[1], "FROM")) {
185
+ malformed();
186
+ return;
187
+ }
188
+ if (word(top[start], "ONLY"))
189
+ start += 1;
190
+ const end = qualifiedNameEnd(top, start);
191
+ if (end === start) {
192
+ malformed();
193
+ return;
194
+ }
195
+ const where = top.findIndex((token, index) => index >= end && word(token, "WHERE"));
196
+ const returning = top.findIndex((token, index) => index >= end && word(token, "RETURNING"));
197
+ const set = top.findIndex((token, index) => index >= end && word(token, "SET"));
198
+ const boundary = Math.min(...[where, returning, top.length].filter((position) => position >= 0));
199
+ const assignment = top.slice(set + 1, boundary);
200
+ const equals = assignment.findIndex((token) => token.value === "=");
201
+ if (update &&
202
+ (set < 0 ||
203
+ equals <= 0 ||
204
+ equals === assignment.length - 1 ||
205
+ assignment.at(-1)?.value === ",")) {
206
+ malformed();
207
+ return;
208
+ }
209
+ if (where >= 0 &&
210
+ (where === top.length - 1 || (returning >= 0 && returning <= where + 1))) {
211
+ malformed();
212
+ return;
213
+ }
214
+ if (where < 0 || (returning >= 0 && returning < where))
215
+ add(update ? "broad_update" : "broad_delete", first);
216
+ return;
217
+ }
218
+ if (word(first, "ALTER") && word(top[1], "TABLE")) {
219
+ let index = 2;
220
+ if (word(top[index], "IF") && word(top[index + 1], "EXISTS"))
221
+ index += 2;
222
+ if (word(top[index], "ONLY"))
223
+ index += 1;
224
+ const end = qualifiedNameEnd(top, index);
225
+ if (end === index) {
226
+ malformed();
227
+ return;
228
+ }
229
+ index = end + (top[end]?.value === "*" ? 1 : 0);
230
+ if (index >= top.length) {
231
+ malformed();
232
+ return;
233
+ }
234
+ while (index < top.length) {
235
+ const comma = top.findIndex((token, candidate) => candidate >= index && token.value === ",");
236
+ const clause = top.slice(index, comma < 0 ? undefined : comma);
237
+ if (clause.length === 0) {
238
+ malformed();
239
+ return;
240
+ }
241
+ if (word(clause[0], "DROP")) {
242
+ let column = 1;
243
+ if (word(clause[column], "COLUMN"))
244
+ column += 1;
245
+ if (word(clause[column], "IF") && word(clause[column + 1], "EXISTS"))
246
+ column += 2;
247
+ if (word(clause[1], "CONSTRAINT"))
248
+ unsupported();
249
+ else if (name(clause[column]) &&
250
+ (column + 1 === clause.length ||
251
+ ((word(clause[column + 1], "CASCADE") ||
252
+ word(clause[column + 1], "RESTRICT")) &&
253
+ column + 2 === clause.length)))
254
+ add("drop_column", clause[0]);
255
+ else
256
+ malformed();
257
+ cascade(clause);
258
+ }
259
+ else if (word(clause[0], "ALTER")) {
260
+ const column = word(clause[1], "COLUMN") ? 2 : 1;
261
+ if (name(clause[column]) &&
262
+ word(clause[column + 1], "SET") &&
263
+ word(clause[column + 2], "NOT") &&
264
+ word(clause[column + 3], "NULL") &&
265
+ column + 4 === clause.length)
266
+ add("not_null", clause[column + 2]);
267
+ else
268
+ unsupported();
269
+ }
270
+ else if (word(clause[0], "ADD")) {
271
+ let caseDepth = 0;
272
+ let constraint = false;
273
+ for (let cursor = 1; cursor < clause.length; cursor += 1) {
274
+ const token = clause[cursor];
275
+ if (word(token, "CASE"))
276
+ caseDepth += 1;
277
+ if (word(token, "END"))
278
+ caseDepth = Math.max(0, caseDepth - 1);
279
+ if (caseDepth > 0)
280
+ continue;
281
+ if (word(token, "NOT") &&
282
+ word(clause[cursor + 1], "NULL") &&
283
+ !word(clause[cursor - 1], "IS"))
284
+ add("not_null", token);
285
+ if (["CONSTRAINT", "CHECK", "UNIQUE", "REFERENCES", "EXCLUDE"].some((keyword) => word(token, keyword)) ||
286
+ (word(token, "PRIMARY") && word(clause[cursor + 1], "KEY")) ||
287
+ (word(token, "FOREIGN") && word(clause[cursor + 1], "KEY")))
288
+ constraint = true;
289
+ }
290
+ if (constraint)
291
+ add("new_constraint", clause[0]);
292
+ // Column definitions/types/expressions are deliberately not parsed.
293
+ unsupported();
294
+ cascade(clause, false);
295
+ }
296
+ else
297
+ unsupported();
298
+ if (comma < 0)
299
+ break;
300
+ index = comma + 1;
301
+ if (index >= top.length)
302
+ malformed();
303
+ }
304
+ return;
305
+ }
306
+ // Other DDL can contain a meaningful trailing CASCADE, but remains unknown.
307
+ if (word(first, "DROP")) {
308
+ let target = 2;
309
+ if ((word(top[1], "MATERIALIZED") && word(top[2], "VIEW")) ||
310
+ (word(top[1], "FOREIGN") && word(top[2], "TABLE")))
311
+ target += 1;
312
+ if (word(top[target], "IF") && word(top[target + 1], "EXISTS"))
313
+ target += 2;
314
+ const end = qualifiedNameEnd(top, target);
315
+ if (end > target && end === top.length - 1 && word(top[end], "CASCADE"))
316
+ cascade(top);
317
+ }
318
+ if (word(first, "CREATE") && word(top[1], "TABLE"))
319
+ cascade(tokens, false);
320
+ unsupported();
321
+ };
322
+ let start = 0;
323
+ let atomicDepth = 0;
324
+ let opaqueBody = false;
325
+ for (let index = 0; index <= lexical.tokens.length; index += 1) {
326
+ const token = lexical.tokens[index];
327
+ let routine = start + 1;
328
+ if (word(lexical.tokens[routine], "OR") &&
329
+ word(lexical.tokens[routine + 1], "REPLACE"))
330
+ routine += 2;
331
+ const routineStatement = word(lexical.tokens[start], "CREATE") &&
332
+ (word(lexical.tokens[routine], "FUNCTION") ||
333
+ word(lexical.tokens[routine], "PROCEDURE"));
334
+ if (routineStatement &&
335
+ token?.depth === 0 &&
336
+ word(token, "BEGIN") &&
337
+ word(lexical.tokens[index + 1], "ATOMIC")) {
338
+ opaqueBody = true;
339
+ atomicDepth += 1;
340
+ }
341
+ else if (opaqueBody &&
342
+ token?.depth === 0 &&
343
+ (word(token, "BEGIN") || word(token, "CASE")))
344
+ atomicDepth += 1;
345
+ else if (opaqueBody && token?.depth === 0 && word(token, "END"))
346
+ atomicDepth -= 1;
347
+ if (index === lexical.tokens.length ||
348
+ (token?.value === ";" && token.depth === 0 && atomicDepth === 0)) {
349
+ const statement = lexical.tokens.slice(start, index);
350
+ if (opaqueBody)
351
+ unsupported();
352
+ else if (index !== lexical.tokens.length ||
353
+ !lexical.unknownReasons.some((reason) => reason === "input_limit" || reason === "malformed_sql"))
354
+ analyze(statement, 0);
355
+ start = index + 1;
356
+ opaqueBody = false;
357
+ }
358
+ }
359
+ if (atomicDepth > 0)
360
+ malformed();
361
+ findings.sort((left, right) => left.line - right.line ||
362
+ left.column - right.column ||
363
+ left.rule.localeCompare(right.rule));
364
+ return {
365
+ complete: reasons.size === 0,
366
+ findings,
367
+ unknownReasons: [...reasons].sort(),
368
+ };
369
+ };
@@ -4,6 +4,7 @@
4
4
  * this module is reusable script infrastructure.
5
5
  */
6
6
  import type { RehearsalConfigPathOptions } from "../project/configuration.mjs";
7
+ import { readMigrationFileInventory } from "./migration_history.mjs";
7
8
  export declare const buildRehearsalPlan: (options?: RehearsalConfigPathOptions) => Promise<{
8
9
  config: {
9
10
  schemaVersion: 1;
@@ -228,7 +229,7 @@ export declare const inspectRehearsalBaseline: (options?: RehearsalConfigPathOpt
228
229
  };
229
230
  };
230
231
  }>;
231
- export declare const inspectRehearsalMigrations: (options?: RehearsalConfigPathOptions) => Promise<{
232
+ export declare const inspectRehearsalMigrations: (options?: RehearsalConfigPathOptions, inventoryReader?: typeof readMigrationFileInventory) => Promise<{
232
233
  baselineGenerationId: string;
233
234
  baselineCutoff: string;
234
235
  candidateSha256: string;
@@ -155,7 +155,7 @@ const assertConfiguredCommand = async ({ command, projectRoot, }) => {
155
155
  }
156
156
  return `package script ${scriptName} exists`;
157
157
  };
158
- const loadPlanInputs = async (options = {}) => {
158
+ const loadPlanInputs = async (options = {}, inventoryReader = readMigrationFileInventory) => {
159
159
  const loaded = await loadRehearsalConfig(options);
160
160
  const [baseline, policyBytes] = await Promise.all([
161
161
  verifyActiveBaseline({
@@ -167,7 +167,7 @@ const loadPlanInputs = async (options = {}) => {
167
167
  bytes: policyBytes,
168
168
  expectedSha256: baseline.sanitizationPolicySha256,
169
169
  });
170
- const currentFiles = await readMigrationFileInventory(new URL("./", pathToFileURL(`${loaded.paths.migrationDirectory}/`)));
170
+ const currentFiles = await inventoryReader(new URL("./", pathToFileURL(`${loaded.paths.migrationDirectory}/`)));
171
171
  const candidateReceipt = createCandidateMigrationReceipt({
172
172
  baselineManifest: baseline,
173
173
  currentFiles,
@@ -363,8 +363,8 @@ export const inspectRehearsalBaseline = async (options = {}) => {
363
363
  review: plan.baselineReview,
364
364
  };
365
365
  };
366
- export const inspectRehearsalMigrations = async (options = {}) => {
367
- const inputs = await loadPlanInputs(options);
366
+ export const inspectRehearsalMigrations = async (options = {}, inventoryReader = readMigrationFileInventory) => {
367
+ const inputs = await loadPlanInputs(options, inventoryReader);
368
368
  return {
369
369
  baselineGenerationId: inputs.baseline.generationId,
370
370
  baselineCutoff: inputs.baseline.migrationCutoff,
@@ -36,7 +36,7 @@ declare const candidateSummary: (inspection: MigrationInspection) => Readonly<{
36
36
  status: string;
37
37
  }[];
38
38
  }>;
39
- export declare const inspectRuntimeTopologyCandidates: (options?: RehearsalConfigPathOptions) => Promise<Readonly<{
39
+ export declare const inspectRuntimeTopologyCandidates: (options?: RehearsalConfigPathOptions, inspectMigrations?: typeof inspectRehearsalMigrations) => Promise<Readonly<{
40
40
  topology: Readonly<{
41
41
  projectRoot: string;
42
42
  primary: RuntimeTopologyTarget;
@@ -103,13 +103,13 @@ const candidateSummary = (inspection) => {
103
103
  candidates: Object.freeze(candidates),
104
104
  });
105
105
  };
106
- export const inspectRuntimeTopologyCandidates = async (options = {}) => {
106
+ export const inspectRuntimeTopologyCandidates = async (options = {}, inspectMigrations = inspectRehearsalMigrations) => {
107
107
  const topology = await loadRuntimeTopology(options);
108
108
  const targets = [];
109
109
  for (const target of topology.targets) {
110
110
  targets.push(Object.freeze({
111
111
  ...target,
112
- summary: candidateSummary(await inspectRehearsalMigrations({
112
+ summary: candidateSummary(await inspectMigrations({
113
113
  projectRoot: topology.projectRoot,
114
114
  configPath: target.configPath,
115
115
  })),
@@ -0,0 +1,39 @@
1
+ import { type WorkloadTicket } from "./workload_queue_store.mjs";
2
+ interface QueueOptions {
3
+ stateDirectory?: string;
4
+ }
5
+ export declare const withWorkloadDiagnostics: <Result>(task: () => Promise<Result>) => Promise<Result>;
6
+ export declare const inspectWorkloads: (options?: QueueOptions) => Promise<{
7
+ schemaVersion: 1;
8
+ scope: "local-user-boot-process-namespace";
9
+ capacity: "not-measured";
10
+ recoveryDigest: string | null;
11
+ entries: {
12
+ id: string;
13
+ command: "migrate" | "reset" | "run" | "start";
14
+ pid: number;
15
+ phase: "choosing" | "held" | "queued";
16
+ owner: "alive" | "dead";
17
+ createdAt: string;
18
+ recoveryRequired: boolean;
19
+ }[];
20
+ }>;
21
+ /** Explicitly reviewed metadata recovery only; never stops processes or removes data. */
22
+ export declare const recoverWorkloads: (confirmation: string, options?: QueueOptions) => Promise<{
23
+ schemaVersion: 1;
24
+ recovered: number;
25
+ databasesChanged: false;
26
+ }>;
27
+ export declare const acquireWorkload: ({ command, waitTimeoutSeconds, signal, onWaiting, ...options }: QueueOptions & {
28
+ command: WorkloadTicket["command"];
29
+ waitTimeoutSeconds: number;
30
+ signal?: AbortSignal;
31
+ onWaiting?: () => void;
32
+ }) => Promise<{
33
+ release: () => Promise<void>;
34
+ }>;
35
+ export declare const withWorkloadAdmission: <Result>({ enabled, execute, ...options }: Parameters<typeof acquireWorkload>[0] & {
36
+ enabled: boolean;
37
+ execute: () => Promise<Result>;
38
+ }) => Promise<Result>;
39
+ export {};