@objectstack/core 17.0.0-rc.1 → 17.0.0-rc.3

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.
package/dist/index.cjs CHANGED
@@ -40,6 +40,8 @@ __export(index_exports, {
40
40
  DependencyResolver: () => DependencyResolver,
41
41
  HotReloadManager: () => HotReloadManager,
42
42
  LiteKernel: () => LiteKernel,
43
+ MigrationJournalRefusal: () => MigrationJournalRefusal,
44
+ MigrationPlanRegistry: () => MigrationPlanRegistry,
43
45
  NamespaceResolver: () => NamespaceResolver,
44
46
  ObjectKernel: () => ObjectKernel,
45
47
  ObjectKernelBase: () => ObjectKernelBase,
@@ -82,14 +84,17 @@ __export(index_exports, {
82
84
  defaultIsTransientError: () => defaultIsTransientError,
83
85
  derivePosture: () => derivePosture,
84
86
  describeInitOrderFault: () => describeInitOrderFault,
87
+ engineCanRollBack: () => engineCanRollBack,
85
88
  evaluateAuthGate: () => evaluateAuthGate,
86
89
  extractApiKey: () => extractApiKey,
87
90
  filterTokenContextFrom: () => filterTokenContextFrom,
91
+ findInterruptedRuns: () => findInterruptedRuns,
88
92
  generateApiKey: () => generateApiKey,
89
93
  generateEd25519KeyPair: () => generateEd25519KeyPair,
90
94
  getEnv: () => getEnv,
91
95
  getMemoryUsage: () => getMemoryUsage,
92
96
  hashApiKey: () => hashApiKey,
97
+ hashMigrationPlan: () => hashMigrationPlan,
93
98
  isAuthGateAllowlisted: () => isAuthGateAllowlisted,
94
99
  isExpired: () => isExpired,
95
100
  isGrantActive: () => isGrantActive,
@@ -98,8 +103,10 @@ __export(index_exports, {
98
103
  nextUtcCalendarDay: () => import_data.nextUtcCalendarDay,
99
104
  parseScopes: () => parseScopes,
100
105
  parseSignature: () => parseSignature,
106
+ planChunks: () => planChunks,
101
107
  postureVisibleRows: () => postureVisibleRows,
102
108
  readAuthoredTranslationLayer: () => readAuthoredTranslationLayer,
109
+ readRunJournal: () => readRunJournal,
103
110
  resolveApiKeyPrincipal: () => resolveApiKeyPrincipal,
104
111
  resolveAuthzContext: () => resolveAuthzContext,
105
112
  resolveFilterToken: () => resolveFilterToken,
@@ -108,6 +115,8 @@ __export(index_exports, {
108
115
  resolveLocalizationContext: () => resolveLocalizationContext,
109
116
  resolvePluginOrder: () => resolvePluginOrder,
110
117
  resolveUserAuthzGrants: () => resolveUserAuthzGrants,
118
+ resumeMigrationJournal: () => resumeMigrationJournal,
119
+ runMigrationJournal: () => runMigrationJournal,
111
120
  safeExit: () => safeExit,
112
121
  shouldDenyAnonymous: () => shouldDenyAnonymous,
113
122
  signPayload: () => signPayload,
@@ -1958,17 +1967,51 @@ var ObjectKernel = class {
1958
1967
  assertInitServiceRequirements(plugin, (name) => this.hasAnyService(name));
1959
1968
  this.currentlyInitializing = plugin.name;
1960
1969
  try {
1961
- const initPromise = plugin.init(this.context);
1962
- const timeoutPromise = new Promise((_, reject) => {
1963
- setTimeout(() => {
1964
- reject(new Error(`Plugin ${plugin.name} init timeout after ${timeout}ms`));
1965
- }, timeout);
1966
- });
1967
- await Promise.race([initPromise, timeoutPromise]);
1970
+ await this.raceStartupTimeout(
1971
+ plugin.init(this.context),
1972
+ timeout,
1973
+ `Plugin ${plugin.name} init timeout after ${timeout}ms`
1974
+ );
1968
1975
  } finally {
1969
1976
  this.currentlyInitializing = void 0;
1970
1977
  }
1971
1978
  }
1979
+ /**
1980
+ * Race a plugin lifecycle hook against its startup-timeout guard, and
1981
+ * reclaim the guard the moment the race settles (#4813).
1982
+ *
1983
+ * The guard used to be armed and then abandoned: when the plugin won the
1984
+ * race, its `setTimeout` stayed ref'd in the event loop for the full
1985
+ * `startupTimeout`, so every process idled that long after its work was
1986
+ * done. One `os migrate` finished in 3s and then sat for 120s
1987
+ * (`ObjectQLPlugin.startupTimeout`), held open by 8 orphaned guards — one
1988
+ * per init plus one per start.
1989
+ *
1990
+ * Clearing on settle rather than `unref()`-ing at arm time is deliberate.
1991
+ * An unref'd guard also stops pinning the loop, but it stops being a guard
1992
+ * as well: if the hook never settles and nothing else keeps the loop alive,
1993
+ * Node exits before the timer can fire and the timeout is never reported.
1994
+ * The guard has to stay ref'd exactly as long as the race is undecided,
1995
+ * which is what `clearTimeout` in a `finally` expresses.
1996
+ *
1997
+ * `operation` is widened to `T | PromiseLike<T>` because the Plugin
1998
+ * contract permits a synchronous hook (`init`/`start` return
1999
+ * `void | Promise<void>`); such a hook wins the race immediately and the
2000
+ * guard is reclaimed on the same turn.
2001
+ */
2002
+ async raceStartupTimeout(operation, timeout, message) {
2003
+ let guard;
2004
+ const timeoutPromise = new Promise((_, reject) => {
2005
+ guard = setTimeout(() => {
2006
+ reject(new Error(message));
2007
+ }, timeout);
2008
+ });
2009
+ try {
2010
+ return await Promise.race([operation, timeoutPromise]);
2011
+ } finally {
2012
+ clearTimeout(guard);
2013
+ }
2014
+ }
1972
2015
  /**
1973
2016
  * Whether a service is resolvable on this kernel right now — direct
1974
2017
  * registration or a loader-registered factory. Backs the init-service
@@ -1994,13 +2037,11 @@ var ObjectKernel = class {
1994
2037
  const startTime = Date.now();
1995
2038
  this.logger.debug(`Start: ${plugin.name}`, { plugin: plugin.name });
1996
2039
  try {
1997
- const startPromise = plugin.start(this.context);
1998
- const timeoutPromise = new Promise((_, reject) => {
1999
- setTimeout(() => {
2000
- reject(new Error(`Plugin ${plugin.name} start timeout after ${timeout}ms`));
2001
- }, timeout);
2002
- });
2003
- await Promise.race([startPromise, timeoutPromise]);
2040
+ await this.raceStartupTimeout(
2041
+ plugin.start(this.context),
2042
+ timeout,
2043
+ `Plugin ${plugin.name} start timeout after ${timeout}ms`
2044
+ );
2004
2045
  const duration = Date.now() - startTime;
2005
2046
  this.startedPlugins.add(plugin.name);
2006
2047
  this.pluginStartTimes.set(plugin.name, duration);
@@ -5057,6 +5098,393 @@ async function bulkWrite(rows, opts) {
5057
5098
  return results;
5058
5099
  }
5059
5100
 
5101
+ // src/utils/migration-journal.ts
5102
+ var import_node_crypto3 = require("crypto");
5103
+ var import_system3 = require("@objectstack/spec/system");
5104
+ var SYSTEM_CTX = { isSystem: true };
5105
+ var DEFAULT_CHUNK_SIZE = 200;
5106
+ function engineCanRollBack(engine) {
5107
+ const e = engine;
5108
+ if (typeof e?.transaction !== "function") return false;
5109
+ const defaultDriverName = e.getDefaultDriverName?.();
5110
+ const defaultDriver = defaultDriverName ? e.getDriverByName?.(defaultDriverName) : void 0;
5111
+ return !defaultDriver || typeof defaultDriver.beginTransaction === "function";
5112
+ }
5113
+ var MigrationPlanRegistry = class {
5114
+ constructor() {
5115
+ this.plans = /* @__PURE__ */ new Map();
5116
+ }
5117
+ register(plan) {
5118
+ this.plans.set(plan.id, plan);
5119
+ }
5120
+ get(planId) {
5121
+ return this.plans.get(planId);
5122
+ }
5123
+ list() {
5124
+ return [...this.plans.values()];
5125
+ }
5126
+ };
5127
+ var MigrationJournalRefusal = class extends Error {
5128
+ constructor(code, message) {
5129
+ super(message);
5130
+ this.name = "MigrationJournalRefusal";
5131
+ this.code = code;
5132
+ }
5133
+ };
5134
+ function planChunks(plan, rowCounts, chunkSize = plan.chunkSize ?? DEFAULT_CHUNK_SIZE) {
5135
+ const size = Math.max(1, chunkSize);
5136
+ const chunks = [];
5137
+ plan.steps.forEach((step, stepIndex) => {
5138
+ const total = rowCounts[stepIndex] ?? 0;
5139
+ for (let offset = 0; offset < total; offset += size) {
5140
+ chunks.push({
5141
+ index: chunks.length,
5142
+ stepIndex,
5143
+ stepName: step.name,
5144
+ offset,
5145
+ length: Math.min(size, total - offset)
5146
+ });
5147
+ }
5148
+ });
5149
+ return chunks;
5150
+ }
5151
+ function hashMigrationPlan(plan, chunks) {
5152
+ const shape = JSON.stringify({
5153
+ id: plan.id,
5154
+ steps: plan.steps.map((s) => s.name),
5155
+ chunks: chunks.map((c) => [c.stepIndex, c.offset, c.length])
5156
+ });
5157
+ return (0, import_node_crypto3.createHash)("sha256").update(shape, "utf8").digest("hex").slice(0, 32);
5158
+ }
5159
+ async function appendEvent(engine, event, execContext) {
5160
+ await engine.insert(
5161
+ import_system3.MIGRATION_JOURNAL_OBJECT,
5162
+ { ...event, created_at: event.created_at ?? (/* @__PURE__ */ new Date()).toISOString() },
5163
+ { context: execContext ?? { ...SYSTEM_CTX } }
5164
+ );
5165
+ }
5166
+ async function readRunJournal(engine, runId) {
5167
+ const rows = await engine.find(
5168
+ import_system3.MIGRATION_JOURNAL_OBJECT,
5169
+ { where: { run_id: runId } },
5170
+ { context: { ...SYSTEM_CTX } }
5171
+ );
5172
+ return [...rows ?? []].sort((a, b) => Number(a.seq) - Number(b.seq));
5173
+ }
5174
+ function chunkSetOf(events, kind) {
5175
+ const out = /* @__PURE__ */ new Set();
5176
+ for (const e of events) {
5177
+ if (e.kind === kind && typeof e.chunk_index === "number") out.add(e.chunk_index);
5178
+ }
5179
+ return out;
5180
+ }
5181
+ async function findInterruptedRuns(engine) {
5182
+ const started = await engine.find(
5183
+ import_system3.MIGRATION_JOURNAL_OBJECT,
5184
+ { where: { kind: "run_started" } },
5185
+ { context: { ...SYSTEM_CTX } }
5186
+ );
5187
+ const out = [];
5188
+ for (const start of started ?? []) {
5189
+ const events = await readRunJournal(engine, start.run_id);
5190
+ if (events.some((e) => e.kind === "run_done")) continue;
5191
+ const committed = chunkSetOf(events, "chunk_done");
5192
+ const compensated = chunkSetOf(events, "compensated");
5193
+ const outstanding = [...committed].filter((i) => !compensated.has(i));
5194
+ if (events.some((e) => e.kind === "run_failed") && outstanding.length === 0) continue;
5195
+ const unknown = [...chunkSetOf(events, "chunk_started")].filter((i) => !committed.has(i));
5196
+ let planId = start.run_id;
5197
+ try {
5198
+ planId = start.detail ? JSON.parse(start.detail).planId ?? start.run_id : start.run_id;
5199
+ } catch {
5200
+ }
5201
+ out.push({
5202
+ runId: start.run_id,
5203
+ planId,
5204
+ planHash: start.plan_hash ?? "",
5205
+ migrationId: start.migration_id,
5206
+ startedAt: start.created_at,
5207
+ committedChunks: [...committed].sort((a, b) => a - b),
5208
+ unknownChunks: unknown.sort((a, b) => a - b),
5209
+ compensatedChunks: [...compensated].sort((a, b) => a - b)
5210
+ });
5211
+ }
5212
+ return out;
5213
+ }
5214
+ async function loadPlan(engine, plan, chunkSize) {
5215
+ const rowsByStep = [];
5216
+ for (const step of plan.steps) rowsByStep.push(await step.load(engine) ?? []);
5217
+ const chunks = planChunks(plan, rowsByStep.map((r) => r.length), chunkSize ?? plan.chunkSize);
5218
+ return { chunks, planHash: hashMigrationPlan(plan, chunks), rowsByStep };
5219
+ }
5220
+ async function runMigrationJournal(engine, plan, options = {}) {
5221
+ const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
5222
+ if (!engineCanRollBack(engine)) {
5223
+ throw new MigrationJournalRefusal(
5224
+ "NOT_IMPLEMENTED",
5225
+ `Migration plan '${plan.id}' requires engine transaction support; this runtime cannot roll back. The journal's chunk_done markers would not mean "committed", so the run is refused rather than started.`
5226
+ );
5227
+ }
5228
+ const { chunks, planHash, rowsByStep } = await loadPlan(engine, plan, options.chunkSize);
5229
+ const resuming = Boolean(options.runId);
5230
+ const runId = options.runId ?? (0, import_node_crypto3.randomUUID)();
5231
+ let events = [];
5232
+ let seq = 0;
5233
+ let committed = /* @__PURE__ */ new Set();
5234
+ let compensated = /* @__PURE__ */ new Set();
5235
+ const attemptsByChunk = /* @__PURE__ */ new Map();
5236
+ if (resuming) {
5237
+ events = await readRunJournal(engine, runId);
5238
+ if (events.length === 0) {
5239
+ throw new MigrationJournalRefusal("NO_SUCH_RUN", `No journal rows for run '${runId}'.`);
5240
+ }
5241
+ const start = events.find((e) => e.kind === "run_started");
5242
+ if (start?.plan_hash && start.plan_hash !== planHash) {
5243
+ throw new MigrationJournalRefusal(
5244
+ "PLAN_CHANGED",
5245
+ `Refusing to resume run '${runId}': plan hash ${planHash} does not match the journal's ${start.plan_hash}. The chunk boundaries recorded in the journal describe a different plan.`
5246
+ );
5247
+ }
5248
+ if (events.some((e) => e.kind === "run_done")) {
5249
+ return {
5250
+ runId,
5251
+ status: "completed",
5252
+ chunksTotal: chunks.length,
5253
+ chunksCommitted: chunkSetOf(events, "chunk_done").size,
5254
+ chunksCompensated: chunkSetOf(events, "compensated").size,
5255
+ planHash
5256
+ };
5257
+ }
5258
+ seq = events.reduce((m, e) => Math.max(m, Number(e.seq) + 1), 0);
5259
+ committed = chunkSetOf(events, "chunk_done");
5260
+ compensated = chunkSetOf(events, "compensated");
5261
+ for (const e of events) {
5262
+ if (e.kind === "chunk_started" && typeof e.chunk_index === "number") {
5263
+ attemptsByChunk.set(e.chunk_index, (attemptsByChunk.get(e.chunk_index) ?? 0) + 1);
5264
+ }
5265
+ }
5266
+ }
5267
+ for (const step of plan.steps) {
5268
+ if (!step.preflight) continue;
5269
+ try {
5270
+ await step.preflight(engine);
5271
+ } catch (err) {
5272
+ throw new MigrationJournalRefusal(
5273
+ "PREFLIGHT_FAILED",
5274
+ `Migration plan '${plan.id}' refused: preflight for step '${step.name}' failed: ${errText(err)}`
5275
+ );
5276
+ }
5277
+ }
5278
+ if (plan.onCrash === "compensate") {
5279
+ const missing = plan.steps.filter((s) => !s.compensate).map((s) => s.name);
5280
+ if (missing.length > 0) {
5281
+ throw new MigrationJournalRefusal(
5282
+ "NOT_COMPENSABLE",
5283
+ `Migration plan '${plan.id}' declares onCrash: 'compensate' but step(s) ${missing.join(", ")} declare no compensate().`
5284
+ );
5285
+ }
5286
+ }
5287
+ const rowsOf = (c) => rowsByStep[c.stepIndex].slice(c.offset, c.offset + c.length);
5288
+ const next = () => seq++;
5289
+ if (!resuming) {
5290
+ await appendEvent(engine, {
5291
+ run_id: runId,
5292
+ seq: next(),
5293
+ kind: "run_started",
5294
+ plan_hash: planHash,
5295
+ migration_id: plan.migrationId,
5296
+ created_at: now(),
5297
+ detail: JSON.stringify({
5298
+ planId: plan.id,
5299
+ onCrash: plan.onCrash ?? "resume",
5300
+ chunks: chunks.map((c) => ({ i: c.index, step: c.stepName, offset: c.offset, length: c.length }))
5301
+ })
5302
+ });
5303
+ }
5304
+ if (resuming && plan.onCrash === "compensate") {
5305
+ return await unwind(engine, plan, {
5306
+ runId,
5307
+ planHash,
5308
+ chunks,
5309
+ rowsOf,
5310
+ next,
5311
+ now,
5312
+ committed,
5313
+ compensated,
5314
+ chunksTotal: chunks.length,
5315
+ cause: new Error(`run '${runId}' rediscovered after interruption; plan policy is compensate`)
5316
+ });
5317
+ }
5318
+ for (const chunk of chunks) {
5319
+ if (committed.has(chunk.index)) continue;
5320
+ const attempt = (attemptsByChunk.get(chunk.index) ?? 0) + 1;
5321
+ attemptsByChunk.set(chunk.index, attempt);
5322
+ const step = plan.steps[chunk.stepIndex];
5323
+ const rows = rowsOf(chunk);
5324
+ await appendEvent(engine, {
5325
+ run_id: runId,
5326
+ seq: next(),
5327
+ kind: "chunk_started",
5328
+ chunk_index: chunk.index,
5329
+ attempt,
5330
+ migration_id: plan.migrationId,
5331
+ created_at: now()
5332
+ });
5333
+ try {
5334
+ await engine.transaction(async (trxCtx) => {
5335
+ await step.forward(rows, { runId, chunkIndex: chunk.index, attempt, context: trxCtx }, engine);
5336
+ await appendEvent(
5337
+ engine,
5338
+ {
5339
+ run_id: runId,
5340
+ seq: next(),
5341
+ kind: "chunk_done",
5342
+ chunk_index: chunk.index,
5343
+ attempt,
5344
+ migration_id: plan.migrationId,
5345
+ created_at: now()
5346
+ },
5347
+ trxCtx
5348
+ );
5349
+ }, { ...SYSTEM_CTX });
5350
+ committed.add(chunk.index);
5351
+ } catch (err) {
5352
+ return await unwind(engine, plan, {
5353
+ runId,
5354
+ planHash,
5355
+ chunks,
5356
+ rowsOf,
5357
+ next,
5358
+ now,
5359
+ committed,
5360
+ compensated,
5361
+ chunksTotal: chunks.length,
5362
+ cause: err
5363
+ });
5364
+ }
5365
+ }
5366
+ await appendEvent(engine, {
5367
+ run_id: runId,
5368
+ seq: next(),
5369
+ kind: "run_done",
5370
+ migration_id: plan.migrationId,
5371
+ created_at: now()
5372
+ });
5373
+ return {
5374
+ runId,
5375
+ status: "completed",
5376
+ chunksTotal: chunks.length,
5377
+ chunksCommitted: committed.size,
5378
+ chunksCompensated: compensated.size,
5379
+ planHash
5380
+ };
5381
+ }
5382
+ async function unwind(engine, plan, a) {
5383
+ const order = [...a.committed].sort((x, y) => y - x);
5384
+ for (const index of order) {
5385
+ if (a.compensated.has(index)) continue;
5386
+ const chunk = a.chunks[index];
5387
+ const step = plan.steps[chunk.stepIndex];
5388
+ if (!step.compensate) {
5389
+ await appendEvent(engine, {
5390
+ run_id: a.runId,
5391
+ seq: a.next(),
5392
+ kind: "run_failed",
5393
+ chunk_index: index,
5394
+ migration_id: plan.migrationId,
5395
+ created_at: a.now(),
5396
+ detail: JSON.stringify({
5397
+ phase: "compensate",
5398
+ reason: "step declares no compensate()",
5399
+ step: step.name,
5400
+ cause: errText(a.cause)
5401
+ })
5402
+ });
5403
+ return {
5404
+ runId: a.runId,
5405
+ status: "failed",
5406
+ chunksTotal: a.chunksTotal,
5407
+ chunksCommitted: a.committed.size,
5408
+ chunksCompensated: a.compensated.size,
5409
+ planHash: a.planHash,
5410
+ error: a.cause
5411
+ };
5412
+ }
5413
+ const attempt = 1;
5414
+ try {
5415
+ await engine.transaction(async (trxCtx) => {
5416
+ await step.compensate(a.rowsOf(chunk), { runId: a.runId, chunkIndex: index, attempt, context: trxCtx }, engine);
5417
+ await appendEvent(
5418
+ engine,
5419
+ {
5420
+ run_id: a.runId,
5421
+ seq: a.next(),
5422
+ kind: "compensated",
5423
+ chunk_index: index,
5424
+ attempt,
5425
+ migration_id: plan.migrationId,
5426
+ created_at: a.now()
5427
+ },
5428
+ trxCtx
5429
+ );
5430
+ }, { ...SYSTEM_CTX });
5431
+ a.compensated.add(index);
5432
+ } catch (err) {
5433
+ await appendEvent(engine, {
5434
+ run_id: a.runId,
5435
+ seq: a.next(),
5436
+ kind: "run_failed",
5437
+ chunk_index: index,
5438
+ migration_id: plan.migrationId,
5439
+ created_at: a.now(),
5440
+ detail: JSON.stringify({
5441
+ phase: "compensate",
5442
+ step: step.name,
5443
+ error: errText(err),
5444
+ cause: errText(a.cause)
5445
+ })
5446
+ });
5447
+ return {
5448
+ runId: a.runId,
5449
+ status: "failed",
5450
+ chunksTotal: a.chunksTotal,
5451
+ chunksCommitted: a.committed.size,
5452
+ chunksCompensated: a.compensated.size,
5453
+ planHash: a.planHash,
5454
+ error: err
5455
+ };
5456
+ }
5457
+ }
5458
+ await appendEvent(engine, {
5459
+ run_id: a.runId,
5460
+ seq: a.next(),
5461
+ kind: "run_failed",
5462
+ migration_id: plan.migrationId,
5463
+ created_at: a.now(),
5464
+ detail: JSON.stringify({ phase: "forward", error: errText(a.cause), compensated: [...a.compensated].sort((x, y) => x - y) })
5465
+ });
5466
+ return {
5467
+ runId: a.runId,
5468
+ status: "compensated",
5469
+ chunksTotal: a.chunksTotal,
5470
+ chunksCommitted: a.committed.size,
5471
+ chunksCompensated: a.compensated.size,
5472
+ planHash: a.planHash,
5473
+ error: a.cause
5474
+ };
5475
+ }
5476
+ async function resumeMigrationJournal(engine, plan, runId, options = {}) {
5477
+ return runMigrationJournal(engine, plan, { ...options, runId });
5478
+ }
5479
+ function errText(err) {
5480
+ if (err instanceof Error) return err.message;
5481
+ try {
5482
+ return String(err);
5483
+ } catch {
5484
+ return "<unprintable error>";
5485
+ }
5486
+ }
5487
+
5060
5488
  // src/utils/filter-tokens.ts
5061
5489
  var import_data2 = require("@objectstack/spec/data");
5062
5490
  var UnknownFilterTokenError = class extends Error {
@@ -5503,7 +5931,7 @@ var PluginHealthMonitor = class {
5503
5931
  };
5504
5932
 
5505
5933
  // src/hot-reload.ts
5506
- var import_node_crypto3 = require("crypto");
5934
+ var import_node_crypto4 = require("crypto");
5507
5935
  var generateUUID = () => {
5508
5936
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
5509
5937
  return crypto.randomUUID();
@@ -5598,7 +6026,7 @@ var PluginStateManager = class {
5598
6026
  */
5599
6027
  calculateChecksum(state) {
5600
6028
  const stateStr = JSON.stringify(state);
5601
- return (0, import_node_crypto3.createHash)("sha256").update(stateStr).digest("hex");
6029
+ return (0, import_node_crypto4.createHash)("sha256").update(stateStr).digest("hex");
5602
6030
  }
5603
6031
  /**
5604
6032
  * Shutdown state manager
@@ -6185,6 +6613,8 @@ var NamespaceResolver = class {
6185
6613
  DependencyResolver,
6186
6614
  HotReloadManager,
6187
6615
  LiteKernel,
6616
+ MigrationJournalRefusal,
6617
+ MigrationPlanRegistry,
6188
6618
  NamespaceResolver,
6189
6619
  ObjectKernel,
6190
6620
  ObjectKernelBase,
@@ -6227,14 +6657,17 @@ var NamespaceResolver = class {
6227
6657
  defaultIsTransientError,
6228
6658
  derivePosture,
6229
6659
  describeInitOrderFault,
6660
+ engineCanRollBack,
6230
6661
  evaluateAuthGate,
6231
6662
  extractApiKey,
6232
6663
  filterTokenContextFrom,
6664
+ findInterruptedRuns,
6233
6665
  generateApiKey,
6234
6666
  generateEd25519KeyPair,
6235
6667
  getEnv,
6236
6668
  getMemoryUsage,
6237
6669
  hashApiKey,
6670
+ hashMigrationPlan,
6238
6671
  isAuthGateAllowlisted,
6239
6672
  isExpired,
6240
6673
  isGrantActive,
@@ -6243,8 +6676,10 @@ var NamespaceResolver = class {
6243
6676
  nextUtcCalendarDay,
6244
6677
  parseScopes,
6245
6678
  parseSignature,
6679
+ planChunks,
6246
6680
  postureVisibleRows,
6247
6681
  readAuthoredTranslationLayer,
6682
+ readRunJournal,
6248
6683
  resolveApiKeyPrincipal,
6249
6684
  resolveAuthzContext,
6250
6685
  resolveFilterToken,
@@ -6253,6 +6688,8 @@ var NamespaceResolver = class {
6253
6688
  resolveLocalizationContext,
6254
6689
  resolvePluginOrder,
6255
6690
  resolveUserAuthzGrants,
6691
+ resumeMigrationJournal,
6692
+ runMigrationJournal,
6256
6693
  safeExit,
6257
6694
  shouldDenyAnonymous,
6258
6695
  signPayload,