@cmmd-center/forge 0.13.116 → 0.13.118
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/bin.cjs +246 -10
- package/dist/bin.mjs +273 -35
- package/dist/client/assets/CanvasPanel-BOh2xUZv.js +441 -0
- package/dist/client/assets/{CompositeItem-B299hxbI.js → CompositeItem-DNVaubQt.js} +2 -2
- package/dist/client/assets/DiffPanel-DUNnxsQq.js +64 -0
- package/dist/client/assets/{DiffPanel.logic-CIKSbFr7.js → DiffPanel.logic-B5u-aRQa.js} +2 -2
- package/dist/client/assets/{DiffWorkerPoolProvider-D0KelzWk.js → DiffWorkerPoolProvider-CACmXsZU.js} +53 -53
- package/dist/client/assets/PullRequestCodePanel-QgCd7SjY.js +5 -0
- package/dist/client/assets/ThemeEditorPanel-v2mnY18G.js +2 -0
- package/dist/client/assets/Virtualizer-DIbjvGwY.js +2 -0
- package/dist/client/assets/WorkerPoolContext-DR8-tB5e.js +2538 -0
- package/dist/client/assets/canvasStateStore-xaZAenqH.js +2 -0
- package/dist/client/assets/debouncer-BmyJL8Gi.js +2 -0
- package/dist/client/assets/index-DbDT5T6P.js +169 -0
- package/dist/client/assets/index-DcAyKQAA.css +1 -0
- package/dist/client/assets/popover-Cxnw5kve.js +2 -0
- package/dist/client/assets/scroll-area-DDcW71H4.js +2 -0
- package/dist/client/assets/switch-Wi-2Dkh9.js +2 -0
- package/dist/client/assets/themePalette-eOA8OvYM.js +5 -0
- package/dist/client/assets/toggle-group-Cqh7gA8I.js +2 -0
- package/dist/client/assets/{toggle-C__ZPx5_.js → toggle-m8ncTpRB.js} +2 -2
- package/dist/client/assets/useOpenInteractionType-CN3hnnfp.js +2 -0
- package/dist/client/index.html +15 -9
- package/dist/forge-build.json +1 -1
- package/dist/{open-2-reBrpv.mjs → open-yHTUkd2Y.mjs} +3 -3
- package/package.json +1 -1
- package/dist/client/assets/CanvasPanel-DfDGOzVI.js +0 -441
- package/dist/client/assets/DiffPanel-CvrMUMq8.js +0 -64
- package/dist/client/assets/PullRequestCodePanel-CnW8NFMv.js +0 -5
- package/dist/client/assets/Virtualizer-BG7EhGpB.js +0 -2
- package/dist/client/assets/WorkerPoolContext-Dfz6dLLM.js +0 -2541
- package/dist/client/assets/canvasStateStore-Zj9xTxOR.js +0 -2
- package/dist/client/assets/index-CzefmTbf.js +0 -169
- package/dist/client/assets/index-Xrh_Zyox.css +0 -1
- package/dist/client/assets/popover-BSkrir4t.js +0 -2
package/dist/bin.cjs
CHANGED
|
@@ -66337,7 +66337,7 @@ function normalizeNumberish(value) {
|
|
|
66337
66337
|
}
|
|
66338
66338
|
//#endregion
|
|
66339
66339
|
//#region package.json
|
|
66340
|
-
var version$1 = "0.13.
|
|
66340
|
+
var version$1 = "0.13.118";
|
|
66341
66341
|
//#endregion
|
|
66342
66342
|
//#region src/sentry.ts
|
|
66343
66343
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -95424,6 +95424,16 @@ var RuntimeReplicationClient = class extends require_Schema$1.Service()("forge/r
|
|
|
95424
95424
|
* from the retry cap and registration can outlast the cap.
|
|
95425
95425
|
*/
|
|
95426
95426
|
const RUNTIME_NOT_REGISTERED_CODE = "RUNTIME_NOT_REGISTERED";
|
|
95427
|
+
/**
|
|
95428
|
+
* CMMD returned 409 THREAD_STREAM_PAYLOAD_CONFLICT or THREAD_STREAM_HEAD_MISMATCH:
|
|
95429
|
+
* its canonical history for a thread generation has diverged from the batch
|
|
95430
|
+
* Forge just sent (forge#3620). Exported so `processRuntimeReplicationOutboxItem`
|
|
95431
|
+
* can tell when a recorded `fail()` may have deferred the actual repair to a
|
|
95432
|
+
* full re-projection instead of finishing it inline -- see
|
|
95433
|
+
* `RuntimeReplicationOutbox.ts`'s `fail()`/`repairThreadStreamFromRemoteConflict`
|
|
95434
|
+
* for the repair itself.
|
|
95435
|
+
*/
|
|
95436
|
+
const THREAD_STREAM_REMOTE_CONFLICT_CODES = new Set(["THREAD_STREAM_PAYLOAD_CONFLICT", "THREAD_STREAM_HEAD_MISMATCH"]);
|
|
95427
95437
|
const retryableCodes = new Set([
|
|
95428
95438
|
"RUNTIME_NOT_READY",
|
|
95429
95439
|
"RUNTIME_THREAD_NOT_READY",
|
|
@@ -300397,7 +300407,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
300397
300407
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
300398
300408
|
*/
|
|
300399
300409
|
function readBakedBuildCommit() {
|
|
300400
|
-
return "
|
|
300410
|
+
return "1de5f431edc763d9146f9be86885d18e191ea9f7";
|
|
300401
300411
|
}
|
|
300402
300412
|
async function resolveServerBuildCommit(input) {
|
|
300403
300413
|
if (isFullCommitSha(input.baked)) return input.baked;
|
|
@@ -314744,6 +314754,33 @@ function applyPendingThreadArchives(input) {
|
|
|
314744
314754
|
}
|
|
314745
314755
|
//#endregion
|
|
314746
314756
|
//#region src/runtimeReplication/processOutboxItem.ts
|
|
314757
|
+
/** The thread id a `thread:<id>` outbox stream key was minted from. */
|
|
314758
|
+
function threadIdFromStreamKey(streamKey) {
|
|
314759
|
+
return ThreadId.make(streamKey.slice(7));
|
|
314760
|
+
}
|
|
314761
|
+
/**
|
|
314762
|
+
* Attempts `enqueueThread` gets for one outbox item before this gives up and
|
|
314763
|
+
* logs loudly. `dependencies.enqueueThread` is the closure RuntimeReplication.ts
|
|
314764
|
+
* builds for `processItem` specifically -- unlike the swallowing variant it
|
|
314765
|
+
* hands to the domain-event reactor and the 5-minute heartbeat (which retry
|
|
314766
|
+
* on their own cadence regardless), a Cause escaping this one is visible
|
|
314767
|
+
* here. Right after `fail()` parks a thread-events row under
|
|
314768
|
+
* THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE, this call is the
|
|
314769
|
+
* corrective re-projection that clears it; without a retry here, one failed
|
|
314770
|
+
* attempt left the row silently blocked until the next heartbeat, a later
|
|
314771
|
+
* domain event, or a manual retry (forge#3621 finding 2). A transient
|
|
314772
|
+
* failure gets a few immediate retries; one that survives them all is logged
|
|
314773
|
+
* loudly instead of disappearing.
|
|
314774
|
+
*/
|
|
314775
|
+
const ENQUEUE_THREAD_RETRY_ATTEMPTS = 3;
|
|
314776
|
+
function enqueueThreadWithBoundedRetry(enqueueThread, threadId, context, attemptsRemaining = ENQUEUE_THREAD_RETRY_ATTEMPTS) {
|
|
314777
|
+
return enqueueThread(threadId).pipe(require_Schema$1.catchCause((cause) => attemptsRemaining > 1 ? enqueueThreadWithBoundedRetry(enqueueThread, threadId, context, attemptsRemaining - 1) : require_Schema$1.logError("Runtime replication thread re-projection failed after retrying; it will run again on the next heartbeat, domain event, or manual retry.", {
|
|
314778
|
+
...context,
|
|
314779
|
+
threadId,
|
|
314780
|
+
cause,
|
|
314781
|
+
attempts: ENQUEUE_THREAD_RETRY_ATTEMPTS
|
|
314782
|
+
})));
|
|
314783
|
+
}
|
|
314747
314784
|
function publicationMetricAttributes(item, outcome, failure) {
|
|
314748
314785
|
return {
|
|
314749
314786
|
operation: item.operation,
|
|
@@ -314781,7 +314818,12 @@ function processRuntimeReplicationOutboxItem(item, dependencies) {
|
|
|
314781
314818
|
failureKind: failure.kind,
|
|
314782
314819
|
errorCode: failure.code
|
|
314783
314820
|
});
|
|
314821
|
+
return;
|
|
314784
314822
|
}
|
|
314823
|
+
if (item.operation === "thread-events" && THREAD_STREAM_REMOTE_CONFLICT_CODES.has(failure.code)) yield* enqueueThreadWithBoundedRetry(dependencies.enqueueThread, threadIdFromStreamKey(item.streamKey), {
|
|
314824
|
+
runtimeId: item.runtimeId,
|
|
314825
|
+
streamKey: item.streamKey
|
|
314826
|
+
});
|
|
314785
314827
|
});
|
|
314786
314828
|
return require_Schema$1.gen(function* () {
|
|
314787
314829
|
const payload = yield* require_Schema$1.sync(() => {
|
|
@@ -314822,7 +314864,10 @@ function processRuntimeReplicationOutboxItem(item, dependencies) {
|
|
|
314822
314864
|
return require_Schema$1.all([
|
|
314823
314865
|
increment(runtimeReplicationPublicationAttemptsTotal, publicationMetricAttributes(item, "success")),
|
|
314824
314866
|
require_Schema$1.update(require_Schema$1.withAttributes(runtimeReplicationPublicationLatency, metricAttributes({ operation: item.operation })), require_Schema$1.millis(publicationLatency)),
|
|
314825
|
-
item.operation === "thread-events" ? dependencies.enqueueThread(
|
|
314867
|
+
item.operation === "thread-events" ? enqueueThreadWithBoundedRetry(dependencies.enqueueThread, threadIdFromStreamKey(item.streamKey), {
|
|
314868
|
+
runtimeId: item.runtimeId,
|
|
314869
|
+
streamKey: item.streamKey
|
|
314870
|
+
}) : require_Schema$1.void_
|
|
314826
314871
|
]).pipe(require_Schema$1.asVoid);
|
|
314827
314872
|
}))
|
|
314828
314873
|
}));
|
|
@@ -314899,15 +314944,16 @@ const RuntimeReplicationLive = require_Schema$1.effect(RuntimeReplication, requi
|
|
|
314899
314944
|
operation: "inventory",
|
|
314900
314945
|
payload
|
|
314901
314946
|
}) : require_Schema$1.void_;
|
|
314902
|
-
const
|
|
314947
|
+
const enqueueThreadUnsafe = (threadId) => runtimeId ? snapshots.getThreadDetailById(threadId).pipe(require_Schema$1.flatMap((thread) => require_Schema$1.isSome(thread) ? logTurnsMissingModelSelection(threadId, thread.value).pipe(require_Schema$1.andThen(outbox.enqueueThread({
|
|
314903
314948
|
runtimeId,
|
|
314904
314949
|
streamKey: `thread:${threadId}`,
|
|
314905
314950
|
events: serializeRuntimeThreadEvents(thread.value),
|
|
314906
314951
|
historyFinalized: thread.value.archivedAt != null
|
|
314907
|
-
}))) : outbox.remove(runtimeId, `thread:${threadId}`)), require_Schema$1.
|
|
314952
|
+
}))) : outbox.remove(runtimeId, `thread:${threadId}`)), require_Schema$1.orDie) : require_Schema$1.void_;
|
|
314953
|
+
const enqueueThread = (threadId) => enqueueThreadUnsafe(threadId).pipe(require_Schema$1.catchCause((cause) => require_Schema$1.logError("Runtime replication thread projection failed.", {
|
|
314908
314954
|
cause,
|
|
314909
314955
|
threadId
|
|
314910
|
-
})))
|
|
314956
|
+
})));
|
|
314911
314957
|
const projectEvent = (event) => runtimeId ? activeHourMeter.markAwake(runtimeId).pipe(require_Schema$1.andThen(require_Schema$1.all([snapshots.getShellSnapshot(), providerRegistry.getProviders])), require_Schema$1.flatMap(([snapshot, providers]) => {
|
|
314912
314958
|
const inventory = serializeRuntimeInventory(snapshot, providers);
|
|
314913
314959
|
const threadId = event.aggregateKind === "thread" ? ThreadId.make(event.aggregateId) : null;
|
|
@@ -314920,7 +314966,7 @@ const RuntimeReplicationLive = require_Schema$1.effect(RuntimeReplication, requi
|
|
|
314920
314966
|
const processItem = (item) => processRuntimeReplicationOutboxItem(item, {
|
|
314921
314967
|
outbox,
|
|
314922
314968
|
client,
|
|
314923
|
-
enqueueThread
|
|
314969
|
+
enqueueThread: enqueueThreadUnsafe
|
|
314924
314970
|
});
|
|
314925
314971
|
const drain = outbox.claim(BATCH_SIZE).pipe(require_Schema$1.flatMap((items) => require_Schema$1.forEach(items, processItem, { concurrency: 1 })), require_Schema$1.catchCause((cause) => require_Schema$1.logError("Runtime replication outbox drain failed.", { cause })));
|
|
314926
314972
|
const start = () => {
|
|
@@ -315016,7 +315062,14 @@ const RuntimeReplicationLive = require_Schema$1.effect(RuntimeReplication, requi
|
|
|
315016
315062
|
observeOutbox
|
|
315017
315063
|
]).pipe(require_Schema$1.asVoid);
|
|
315018
315064
|
};
|
|
315019
|
-
|
|
315065
|
+
const retryReprojectionPending = (streamKeys) => require_Schema$1.forEach(streamKeys, (streamKey) => enqueueThread(threadIdFromStreamKey(streamKey)), {
|
|
315066
|
+
concurrency: 1,
|
|
315067
|
+
discard: true
|
|
315068
|
+
});
|
|
315069
|
+
return RuntimeReplication.of({
|
|
315070
|
+
start,
|
|
315071
|
+
retryReprojectionPending
|
|
315072
|
+
});
|
|
315020
315073
|
}));
|
|
315021
315074
|
//#endregion
|
|
315022
315075
|
//#region src/runtimeReplication/activeHourProtocol.ts
|
|
@@ -315315,6 +315368,100 @@ const RUNTIME_REPLICATION_RETRY_EXHAUSTED_CODE = "RUNTIME_REPLICATION_RETRY_EXHA
|
|
|
315315
315368
|
* claims it; only `recover()` and the manual retry endpoint do.
|
|
315316
315369
|
*/
|
|
315317
315370
|
const THREAD_STREAM_PAYLOAD_HASH_MISMATCH_CODE = "THREAD_STREAM_PAYLOAD_HASH_MISMATCH";
|
|
315371
|
+
/**
|
|
315372
|
+
* CMMD returned 409 THREAD_STREAM_PAYLOAD_CONFLICT or THREAD_STREAM_HEAD_MISMATCH:
|
|
315373
|
+
* its canonical history for this generation has diverged from the batch Forge
|
|
315374
|
+
* just sent (forge#3620). The common trigger is CMMD's own reclaim job
|
|
315375
|
+
* replacing the Sprite (CMMD-Center/CMMD#5865) mid-turn, which wipes this
|
|
315376
|
+
* runtime's local `runtime_replication_streams` cursor; Forge then rebuilds a
|
|
315377
|
+
* batch from sequence 1 that CMMD, still holding the old generation's
|
|
315378
|
+
* acknowledged history, refuses. `fail()` recovers by starting a brand-new
|
|
315379
|
+
* repair generation from sequence 1 with an empty previous head -- the one
|
|
315380
|
+
* shape CMMD's `validateRuntimeThreadAppend` accepts unconditionally as a
|
|
315381
|
+
* reset -- rather than blocking the thread forever.
|
|
315382
|
+
*
|
|
315383
|
+
* The refused batch's own `events` are never safe to rebuild that repair from
|
|
315384
|
+
* directly, regardless of `fromSequence`. A batch is capped at `batchSize`
|
|
315385
|
+
* (500, see `buildRuntimeThreadBatch` in protocol.ts) and `isFinalBatch` is
|
|
315386
|
+
* computed relative to whatever array it was handed -- so re-running
|
|
315387
|
+
* `buildRuntimeThreadBatch` on a refused batch's own, already-truncated
|
|
315388
|
+
* `events` array makes an 800-event thread's 500-event suffix look like a
|
|
315389
|
+
* complete 500-event history all over again, `isFinalBatch: true` included.
|
|
315390
|
+
* CMMD accepts that as this generation's full canonical history on the reset
|
|
315391
|
+
* rule and events 501 onward are gone for good (CodeRabbit finding on
|
|
315392
|
+
* forge#3621, forge#3620 finding 1). This holds even at `fromSequence === 1`:
|
|
315393
|
+
* a fresh generation's very first batch is truncated exactly the same way
|
|
315394
|
+
* once the thread has more events than `batchSize`.
|
|
315395
|
+
*
|
|
315396
|
+
* `fail()` instead always resets the local cursor to a new repair generation
|
|
315397
|
+
* and parks the row blocked under
|
|
315398
|
+
* `THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE` until
|
|
315399
|
+
* `processRuntimeReplicationOutboxItem` re-derives the FULL thread history
|
|
315400
|
+
* through the same `enqueueThread` projection path the success path already
|
|
315401
|
+
* runs after every publish (see `processOutboxItem.ts`) and overwrites this
|
|
315402
|
+
* row with a correctly full batch. That re-projection reads the domain
|
|
315403
|
+
* model's complete event list, not a persisted batch's truncated `events`,
|
|
315404
|
+
* so it is safe to rebuild from no matter how many events the thread holds.
|
|
315405
|
+
*
|
|
315406
|
+
* The reset is bounded to one per distinct conflict, keyed on whether the
|
|
315407
|
+
* reset itself has made any progress: see `refusedResetIsStuck` below. The
|
|
315408
|
+
* re-projection failing to land promptly does not strand the row forever --
|
|
315409
|
+
* `processRuntimeReplicationOutboxItem` retries it a bounded number of times
|
|
315410
|
+
* before giving up for this cycle, the 5-minute replication heartbeat and any
|
|
315411
|
+
* later domain event on this thread call `enqueueThread` again regardless,
|
|
315412
|
+
* and the manual `/api/runtime-replication/retry` endpoint calls it directly
|
|
315413
|
+
* for rows parked under this code (see `RuntimeReplication.ts`'s
|
|
315414
|
+
* `retryReprojectionPending`).
|
|
315415
|
+
*/
|
|
315416
|
+
const THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE = "THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING";
|
|
315417
|
+
/**
|
|
315418
|
+
* Whether a refused batch is the exact canonical reset shape (`fromSequence
|
|
315419
|
+
* === 1`, empty previous head, deterministic content) under a generation the
|
|
315420
|
+
* local cursor already holds with zero acknowledged progress -- i.e. this
|
|
315421
|
+
* exact reset was already sent once and CMMD refused it again, which makes
|
|
315422
|
+
* it genuinely corrupt local history rather than a race to retry. Applies to
|
|
315423
|
+
* any non-base generation (both the `:repair:` suffix this file mints and
|
|
315424
|
+
* the `:final:` suffix `runtimeThreadFinalGenerationId` mints for an
|
|
315425
|
+
* archived thread's first post-transition batch -- both always start a fresh
|
|
315426
|
+
* generation at sequence 1 from an empty head the same way).
|
|
315427
|
+
*
|
|
315428
|
+
* The base generation is exempt unconditionally: its own first-sequence
|
|
315429
|
+
* conflict is the original, always-eligible incident trigger (Forge's local
|
|
315430
|
+
* cursor was wiped and it resent from scratch), not a repeat of an
|
|
315431
|
+
* already-attempted reset. A repair or final generation that already made
|
|
315432
|
+
* progress (`fromSequence > 1` under it) is exempt too -- CMMD acknowledged
|
|
315433
|
+
* it at least once, so a later refusal is a new, unrelated conflict and
|
|
315434
|
+
* earns its own fresh reset (forge#3620 finding 2); the bound must not key
|
|
315435
|
+
* on a generation id merely containing `:repair:`/`:final:`, or a thread
|
|
315436
|
+
* repaired once could never recover from a later, unrelated conflict.
|
|
315437
|
+
*/
|
|
315438
|
+
function refusedResetIsStuck(input) {
|
|
315439
|
+
if (input.refusedGenerationId === input.baseGenerationId) return false;
|
|
315440
|
+
if (input.refusedFromSequence !== 1) return false;
|
|
315441
|
+
return input.cursor === void 0 || input.cursor.generationId !== input.refusedGenerationId || input.cursor.acknowledgedSequence === 0;
|
|
315442
|
+
}
|
|
315443
|
+
/**
|
|
315444
|
+
* The generation id, starting sequence, and raw source events out of a
|
|
315445
|
+
* persisted thread batch, read straight off the JSON rather than through
|
|
315446
|
+
* `decodeThreadBatch` -- that schema only keeps `sequence`/`payloadHash` per
|
|
315447
|
+
* event, and a repair needs the full canonical fields to re-hash from
|
|
315448
|
+
* scratch. Extra fields (the old `sequence`/`payloadHash`) are harmless:
|
|
315449
|
+
* `buildRuntimeThreadBatch` overwrites both and its hasher reads only the
|
|
315450
|
+
* named canonical fields.
|
|
315451
|
+
*/
|
|
315452
|
+
function parseThreadBatchForRepair(payloadJson) {
|
|
315453
|
+
try {
|
|
315454
|
+
const parsed = JSON.parse(payloadJson);
|
|
315455
|
+
if (typeof parsed.generationId !== "string" || typeof parsed.fromSequence !== "number" || !Array.isArray(parsed.events)) return null;
|
|
315456
|
+
return {
|
|
315457
|
+
generationId: parsed.generationId,
|
|
315458
|
+
fromSequence: parsed.fromSequence,
|
|
315459
|
+
events: parsed.events
|
|
315460
|
+
};
|
|
315461
|
+
} catch {
|
|
315462
|
+
return null;
|
|
315463
|
+
}
|
|
315464
|
+
}
|
|
315318
315465
|
const STARTUP_RECOVERABLE_BLOCKED_CODES = [
|
|
315319
315466
|
...CLAIMABLE_BLOCKED_CODES,
|
|
315320
315467
|
"RUNTIME_NOT_READY",
|
|
@@ -315525,6 +315672,26 @@ const retryBlockedRuntimeReplicationOutbox = require_Schema$1.gen(function* () {
|
|
|
315525
315672
|
RETURNING stream_key AS "streamKey"
|
|
315526
315673
|
`).length;
|
|
315527
315674
|
}).pipe(require_Schema$1.orDie);
|
|
315675
|
+
/**
|
|
315676
|
+
* Thread streams parked under THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE,
|
|
315677
|
+
* for the manual `/api/runtime-replication/retry` endpoint. This code is deliberately
|
|
315678
|
+
* absent from MANUALLY_RECOVERABLE_BLOCKED_CODES: flipping the row back to `pending`
|
|
315679
|
+
* the way `retryBlockedRuntimeReplicationOutbox` does for other blocked codes would
|
|
315680
|
+
* requeue the same truncated payload `fail()` already refused, undoing the very repair
|
|
315681
|
+
* it just started. `http.ts`'s retry handler passes these to `RuntimeReplication`'s
|
|
315682
|
+
* `retryReprojectionPending`, which re-runs `enqueueThread` for each -- the same
|
|
315683
|
+
* corrective re-projection that already clears this code after every publish and after
|
|
315684
|
+
* every conflict (forge#3621 finding 2). No `runtimeId` filter, matching
|
|
315685
|
+
* `retryBlockedRuntimeReplicationOutbox` above: this outbox table belongs to one Forge
|
|
315686
|
+
* server.
|
|
315687
|
+
*/
|
|
315688
|
+
const readReprojectionPendingThreadStreamKeys = require_Schema$1.gen(function* () {
|
|
315689
|
+
return (yield* (yield* require_SqlError.SqlClient)`
|
|
315690
|
+
SELECT stream_key AS "streamKey" FROM runtime_replication_outbox
|
|
315691
|
+
WHERE operation = 'thread-events' AND status = 'blocked'
|
|
315692
|
+
AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE}
|
|
315693
|
+
`).map((row) => row.streamKey);
|
|
315694
|
+
}).pipe(require_Schema$1.orDie);
|
|
315528
315695
|
const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationOutbox, require_Schema$1.gen(function* () {
|
|
315529
315696
|
const sql = yield* require_SqlError.SqlClient;
|
|
315530
315697
|
const upsertPayload = (input) => {
|
|
@@ -316052,7 +316219,69 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
|
|
|
316052
316219
|
discard: true
|
|
316053
316220
|
});
|
|
316054
316221
|
})).pipe(require_Schema$1.orDie);
|
|
316055
|
-
|
|
316222
|
+
/**
|
|
316223
|
+
* Recovers from a CMMD-reported thread stream conflict (see
|
|
316224
|
+
* THREAD_STREAM_REMOTE_CONFLICT_CODES). Returns `null` when this item is
|
|
316225
|
+
* not eligible (its payload does not decode, or `refusedResetIsStuck`
|
|
316226
|
+
* says the exact reset shape already failed once -- in which case the
|
|
316227
|
+
* caller falls back to blocking) and `false` when the row was superseded
|
|
316228
|
+
* before this could apply, mirroring `fail`'s own supersession signal.
|
|
316229
|
+
* Otherwise replaces the local cursor with a brand-new repair generation
|
|
316230
|
+
* from sequence 1, empty previous head -- the shape CMMD's reset rule
|
|
316231
|
+
* accepts unconditionally -- and parks the row blocked under
|
|
316232
|
+
* `THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE`, deferring the
|
|
316233
|
+
* actual rebuild to the caller's next full re-projection regardless of
|
|
316234
|
+
* `fromSequence` (see the big comment above
|
|
316235
|
+
* `THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE`): the refused
|
|
316236
|
+
* batch's own `events` are truncated at `batchSize` the same way whether
|
|
316237
|
+
* this was its first batch or its tenth, so rebuilding from them directly
|
|
316238
|
+
* can silently drop everything past the cap.
|
|
316239
|
+
*/
|
|
316240
|
+
const repairThreadStreamFromRemoteConflict = (item, failure) => require_Schema$1.gen(function* () {
|
|
316241
|
+
const parsedBatch = parseThreadBatchForRepair(item.payloadJson);
|
|
316242
|
+
if (!parsedBatch) return null;
|
|
316243
|
+
const baseGenerationId = runtimeThreadGenerationId(item.runtimeId, item.streamKey);
|
|
316244
|
+
const cursor = yield* readThreadCursor({
|
|
316245
|
+
runtimeId: item.runtimeId,
|
|
316246
|
+
streamKey: item.streamKey
|
|
316247
|
+
});
|
|
316248
|
+
if (refusedResetIsStuck({
|
|
316249
|
+
baseGenerationId,
|
|
316250
|
+
refusedGenerationId: parsedBatch.generationId,
|
|
316251
|
+
refusedFromSequence: parsedBatch.fromSequence,
|
|
316252
|
+
cursor
|
|
316253
|
+
})) return null;
|
|
316254
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
316255
|
+
const repairGenerationId = `${baseGenerationId}:repair:${node_crypto.randomUUID()}`;
|
|
316256
|
+
const message = `Runtime replication conflict (${failure.code}) refused a thread-events batch; deferring to a full-history re-projection under a new repair generation.`;
|
|
316257
|
+
if (!(yield* sql.withTransaction(require_Schema$1.gen(function* () {
|
|
316258
|
+
if ((yield* sql`
|
|
316259
|
+
UPDATE runtime_replication_outbox SET
|
|
316260
|
+
status = 'blocked', next_attempt_at = '9999-12-31T23:59:59.999Z',
|
|
316261
|
+
last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING"},
|
|
316262
|
+
last_error = ${message}, updated_at = ${now}
|
|
316263
|
+
WHERE runtime_id = ${item.runtimeId} AND stream_key = ${item.streamKey}
|
|
316264
|
+
AND payload_hash = ${item.payloadHash}
|
|
316265
|
+
RETURNING runtime_id AS "runtimeId"
|
|
316266
|
+
`).length !== 1) return false;
|
|
316267
|
+
yield* replaceThreadCursor({
|
|
316268
|
+
runtimeId: item.runtimeId,
|
|
316269
|
+
streamKey: item.streamKey,
|
|
316270
|
+
generationId: repairGenerationId,
|
|
316271
|
+
now
|
|
316272
|
+
});
|
|
316273
|
+
return true;
|
|
316274
|
+
})))) return false;
|
|
316275
|
+
yield* require_Schema$1.logWarning("Runtime replication deferred a thread-stream repair to a full re-projection after a remote conflict.", {
|
|
316276
|
+
runtimeId: item.runtimeId,
|
|
316277
|
+
streamKey: item.streamKey,
|
|
316278
|
+
errorCode: failure.code,
|
|
316279
|
+
previousGenerationId: parsedBatch.generationId,
|
|
316280
|
+
repairGenerationId
|
|
316281
|
+
});
|
|
316282
|
+
return true;
|
|
316283
|
+
}).pipe(require_Schema$1.orDie);
|
|
316284
|
+
const failGeneric = (item, failure) => {
|
|
316056
316285
|
const attempts = item.attemptCount + 1;
|
|
316057
316286
|
const exhausted = failure.kind === "retryable" && attempts >= 8 && failure.code !== "RUNTIME_NOT_REGISTERED";
|
|
316058
316287
|
const retryable = failure.kind === "retryable" && !exhausted;
|
|
@@ -316067,6 +316296,10 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
|
|
|
316067
316296
|
RETURNING runtime_id AS "runtimeId"
|
|
316068
316297
|
`.pipe(require_Schema$1.map((rows) => rows.length === 1), require_Schema$1.orDie);
|
|
316069
316298
|
};
|
|
316299
|
+
const fail = (item, failure) => {
|
|
316300
|
+
if (item.operation !== "thread-events" || !THREAD_STREAM_REMOTE_CONFLICT_CODES.has(failure.code)) return failGeneric(item, failure);
|
|
316301
|
+
return repairThreadStreamFromRemoteConflict(item, failure).pipe(require_Schema$1.flatMap((repaired) => repaired === null ? failGeneric(item, failure) : require_Schema$1.succeed(repaired)));
|
|
316302
|
+
};
|
|
316070
316303
|
const retryBlocked = () => retryBlockedRuntimeReplicationOutbox.pipe(require_Schema$1.provideService(require_SqlError.SqlClient, sql));
|
|
316071
316304
|
const recover = () => sql`
|
|
316072
316305
|
UPDATE runtime_replication_outbox SET status = 'pending',
|
|
@@ -316245,10 +316478,13 @@ const statusHandler = require_Schema$1.gen(function* () {
|
|
|
316245
316478
|
const retryHandler = require_Schema$1.gen(function* () {
|
|
316246
316479
|
const request = yield* require_HttpServer.HttpServerRequest;
|
|
316247
316480
|
yield* (yield* ServerAuth).authenticateHttpRequest(request);
|
|
316481
|
+
const replication = yield* require_Schema$1.serviceOption(RuntimeReplication);
|
|
316248
316482
|
const requeued = yield* retryBlockedRuntimeReplicationOutbox;
|
|
316483
|
+
const reprojectionPendingStreamKeys = yield* readReprojectionPendingThreadStreamKeys;
|
|
316484
|
+
if (require_Schema$1.isSome(replication)) yield* replication.value.retryReprojectionPending(reprojectionPendingStreamKeys);
|
|
316249
316485
|
const status = yield* readRuntimeReplicationOutboxStatus;
|
|
316250
316486
|
return require_HttpServer.jsonUnsafe({
|
|
316251
|
-
requeued,
|
|
316487
|
+
requeued: requeued + (require_Schema$1.isSome(replication) ? reprojectionPendingStreamKeys.length : 0),
|
|
316252
316488
|
...responseFromStatus(status)
|
|
316253
316489
|
});
|
|
316254
316490
|
}).pipe(require_Schema$1.catchTag("AuthError", respondToAuthError));
|