@powerhousedao/reactor 6.2.3-dev.50 → 6.2.3-dev.52
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/{document-indexer-DwAMDVRI.js → document-indexer-3fkVWCaN.js} +26 -14
- package/dist/document-indexer-3fkVWCaN.js.map +1 -0
- package/dist/{drive-container-types-Ci1pdv4X.js → drive-container-types-BAfFzBOf.js} +133 -7
- package/dist/drive-container-types-BAfFzBOf.js.map +1 -0
- package/dist/entry.js +2 -2
- package/dist/{forwarding-logger-ByfBFgMS.js → forwarding-logger-B9LLXItm.js} +111 -79
- package/dist/forwarding-logger-B9LLXItm.js.map +1 -0
- package/dist/{index-CNhT69De.d.ts → index-dDm0fUBT.d.ts} +518 -68
- package/dist/index-dDm0fUBT.d.ts.map +1 -0
- package/dist/index.d.ts +1040 -138
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3110 -776
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.js +2 -2
- package/dist/{projection-shard-manager-CQ5FwQmv.js → projection-shard-manager-Drl9w8Eo.js} +3 -13
- package/dist/projection-shard-manager-Drl9w8Eo.js.map +1 -0
- package/dist/rpc.d.ts +32 -6
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +8 -4
- package/dist/rpc.js.map +1 -1
- package/dist/{worker-handle-CN2-oRv-.js → worker-handle-Nso8i5eU.js} +2 -2
- package/dist/{worker-handle-CN2-oRv-.js.map → worker-handle-Nso8i5eU.js.map} +1 -1
- package/dist/worker-pool-router-BROOEQSE.js +35 -0
- package/dist/worker-pool-router-BROOEQSE.js.map +1 -0
- package/package.json +3 -3
- package/dist/document-indexer-DwAMDVRI.js.map +0 -1
- package/dist/drive-container-types-Ci1pdv4X.js.map +0 -1
- package/dist/forwarding-logger-ByfBFgMS.js.map +0 -1
- package/dist/index-CNhT69De.d.ts.map +0 -1
- package/dist/projection-shard-manager-CQ5FwQmv.js.map +0 -1
|
@@ -1,6 +1,57 @@
|
|
|
1
|
-
import { A as GATED_DOCUMENT_ACTIONS, B as createOperation,
|
|
1
|
+
import { A as GATED_DOCUMENT_ACTIONS, At as yieldToMain, B as createOperation, Ct as ReactorEventTypes, F as buildErrorResult, G as relationshipTarget, H as isGenesisOperation, I as buildSuccessResult, J as targetDocumentId, K as submittedActionIds, M as TouchedStreams, N as applyDeleteDocumentAction$1, O as DOCUMENT_SCOPE_ACTIONS, P as applyUpgradeDocumentAction$1, R as createDocumentFromAction, Tt as delay, U as jobWriteIds, V as getNextIndexForScope, W as refusalError, Y as updateDocumentRevision, Z as AppendConditionFailedError, _ as acquirePurgeLocks, at as AuthorizationDeniedError, ct as DocumentNotDeletedError, dt as ExcessiveReshuffleError, ft as GroupInUseError, gt as PurgeTooLargeError, it as AuthTimestampNotMonotonicError, j as PurgeFence, k as FencedWriteCache, kt as withDeadline, lt as DocumentNotFoundError, mt as InvalidSignatureError, pt as InvalidOperationTimestampError, st as DocumentDeletedError, ut as DocumentPurgedError, v as findPurged, vt as ReservedActionError, wt as TIMED_OUT, xt as UpgradePreconditionFailedError, yt as UnsupportedProtocolVersionError } from "./forwarding-logger-B9LLXItm.js";
|
|
2
2
|
import { AUTH_ACTION_TYPES, AUTH_DENIED_BY_GRANT_REASON, AUTH_NO_GRANT_REASON, AUTH_VERSION_UNSUPPORTED_REASON, DOCUMENT_DELETED_REASON, PEER_CAPABILITIES, actionSignerIdentity, actionSigningTarget, applyAuthAction, applyDeleteDocumentAction, base58Decode, baseReducerVersion, buildOperationSignatureMessage, buildOperationSignatureParams, canonicalJson, decide, deriveDocumentId, deriveOperationId, evaluate, garbageCollect, groupDocumentType, groupMembershipActionTypes, hashActionV2, hashBrowser, hashDocumentStateForScope, isDenied, isDerivedDocumentId, isPurgeMarker, isUndoRedo, isV2ActionHash, localSupports, mentionedGroupIds, normalizeDocumentModelVersion, purgeDocumentAction, purgeMarkerOperation, referencedGroupIds, signaturePolicyOf, sortOperations, v2TupleProblem, withProtocolVersions } from "@powerhousedao/shared/document-model";
|
|
3
3
|
import { sql } from "kysely";
|
|
4
|
+
//#region src/shared/types.ts
|
|
5
|
+
/**
|
|
6
|
+
* Enum that determines deletion propagation.
|
|
7
|
+
*/
|
|
8
|
+
let PropagationMode = /* @__PURE__ */ function(PropagationMode) {
|
|
9
|
+
PropagationMode["None"] = "none";
|
|
10
|
+
PropagationMode["Cascade"] = "cascade";
|
|
11
|
+
return PropagationMode;
|
|
12
|
+
}({});
|
|
13
|
+
/**
|
|
14
|
+
* Enum that describes the type of relationship change.
|
|
15
|
+
*/
|
|
16
|
+
let RelationshipChangeType = /* @__PURE__ */ function(RelationshipChangeType) {
|
|
17
|
+
RelationshipChangeType["Added"] = "added";
|
|
18
|
+
RelationshipChangeType["Removed"] = "removed";
|
|
19
|
+
return RelationshipChangeType;
|
|
20
|
+
}({});
|
|
21
|
+
/**
|
|
22
|
+
* Marks a load whose operations a sync cursor keeps re-pullable, so its
|
|
23
|
+
* write-ready needs no flush. A symbol, not exported from the package: it
|
|
24
|
+
* cannot be set by an outside caller or survive any serialization boundary.
|
|
25
|
+
*/
|
|
26
|
+
const CURSOR_PROTECTED = Symbol("cursorProtected");
|
|
27
|
+
/** Load meta for the sync manager's inbox, the only owner of the cursor. */
|
|
28
|
+
function cursorProtectedLoadMeta(sourceRemote) {
|
|
29
|
+
return {
|
|
30
|
+
sourceRemote,
|
|
31
|
+
[CURSOR_PROTECTED]: true
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function isCursorProtectedLoad(job) {
|
|
35
|
+
const meta = job.meta;
|
|
36
|
+
return job.kind === "load" && meta[CURSOR_PROTECTED] === true;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Job execution statuses
|
|
40
|
+
*/
|
|
41
|
+
let JobStatus = /* @__PURE__ */ function(JobStatus) {
|
|
42
|
+
/** Job is queued but not yet started */
|
|
43
|
+
JobStatus["PENDING"] = "PENDING";
|
|
44
|
+
/** Job is currently being executed */
|
|
45
|
+
JobStatus["RUNNING"] = "RUNNING";
|
|
46
|
+
/** Operations have been written to the operation store (JOB_WRITE_READY event) */
|
|
47
|
+
JobStatus["WRITE_READY"] = "WRITE_READY";
|
|
48
|
+
/** Read models have finished indexing operations (JOB_READ_READY event) */
|
|
49
|
+
JobStatus["READ_READY"] = "READ_READY";
|
|
50
|
+
/** Job failed (may be retried) */
|
|
51
|
+
JobStatus["FAILED"] = "FAILED";
|
|
52
|
+
return JobStatus;
|
|
53
|
+
}({});
|
|
54
|
+
//#endregion
|
|
4
55
|
//#region src/cache/operation-index-types.ts
|
|
5
56
|
const DRIVE_COLLECTION_PREFIX = "drive.";
|
|
6
57
|
/**
|
|
@@ -28,6 +79,16 @@ var DriveCollectionId = class DriveCollectionId {
|
|
|
28
79
|
* The single deserializer for the wire/storage form. `branch` may contain
|
|
29
80
|
* dots, while `driveId` is a dot-free document id, so the drive id is the
|
|
30
81
|
* final dot-delimited segment.
|
|
82
|
+
*
|
|
83
|
+
* That asymmetry is a real constraint, not a convention: a drive id
|
|
84
|
+
* containing a dot does NOT survive `key` -> `fromKey`, because the split
|
|
85
|
+
* takes the LAST dot and so hands the tail of the drive id back as the drive
|
|
86
|
+
* id and everything before it as the branch. The key format is the wire and
|
|
87
|
+
* storage representation of existing `document_collections` rows, so it
|
|
88
|
+
* cannot be changed; callers that accept a drive id or branch from outside
|
|
89
|
+
* (a UI field, an RPC payload) must reject dots in the drive id at their own
|
|
90
|
+
* boundary. The round-trip is verifiable without parsing rules:
|
|
91
|
+
* `DriveCollectionId.fromKey(id.key).key === id.key`.
|
|
31
92
|
*/
|
|
32
93
|
static fromKey(key) {
|
|
33
94
|
if (!key.startsWith(DRIVE_COLLECTION_PREFIX)) throw new Error(`Unsupported collection id: ${key}`);
|
|
@@ -409,6 +470,7 @@ function selectDecisionModel(flags, registry) {
|
|
|
409
470
|
const DEFAULT_MAX_PURGE_OPERATIONS = 2e5;
|
|
410
471
|
/** How long a deferred job waits for its document before it fails. */
|
|
411
472
|
const DEFAULT_DEFERRED_JOB_TTL_MS = 3e4;
|
|
473
|
+
const DEFAULT_JOB_TIMEOUT_MS = 3e4;
|
|
412
474
|
/**
|
|
413
475
|
* Event types for the job executor
|
|
414
476
|
*/
|
|
@@ -687,6 +749,20 @@ var KyselyExecutionScope = class {
|
|
|
687
749
|
}
|
|
688
750
|
};
|
|
689
751
|
//#endregion
|
|
752
|
+
//#region src/storage/storage-flush.ts
|
|
753
|
+
/** The store's session is poisoned; only a host restart recovers it. */
|
|
754
|
+
var StoragePoisonedError = class extends Error {
|
|
755
|
+
constructor(message, cause) {
|
|
756
|
+
super(message, { cause });
|
|
757
|
+
this.name = "StoragePoisonedError";
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
var NoopStorageFlusher = class {
|
|
761
|
+
flush() {
|
|
762
|
+
return Promise.resolve();
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
//#endregion
|
|
690
766
|
//#region src/utils/reshuffle.ts
|
|
691
767
|
const STRICT_ORDER_ACTION_TYPES = /* @__PURE__ */ new Set([
|
|
692
768
|
"CREATE_DOCUMENT",
|
|
@@ -2184,6 +2260,8 @@ function sameContent(stored, submitted) {
|
|
|
2184
2260
|
//#endregion
|
|
2185
2261
|
//#region src/executor/simple-job-executor.ts
|
|
2186
2262
|
const MAX_SKIP_THRESHOLD = 1e3;
|
|
2263
|
+
/** Retries of a failed flush before a committed job's write-ready is withheld. */
|
|
2264
|
+
const MAX_ANNOUNCE_FLUSH_ATTEMPTS = 5;
|
|
2187
2265
|
const ISO_TIMESTAMP_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
|
|
2188
2266
|
function isValidISOTimestamp(value) {
|
|
2189
2267
|
if (!ISO_TIMESTAMP_REGEX.test(value)) return false;
|
|
@@ -2246,11 +2324,13 @@ var SimpleJobExecutor = class {
|
|
|
2246
2324
|
documentActionHandler;
|
|
2247
2325
|
executionScope;
|
|
2248
2326
|
signer;
|
|
2327
|
+
flusher;
|
|
2249
2328
|
/**
|
|
2250
2329
|
* `signer` signs the operations the reducer synthesizes; unsigned if omitted.
|
|
2251
2330
|
* `trustPolicy` decides which keys may sign as which users at admission.
|
|
2331
|
+
* `flusher` makes a job durable before its write-ready is announced.
|
|
2252
2332
|
*/
|
|
2253
|
-
constructor(logger, registry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, driveContainerTypes, config, executionScope, signer, trustPolicy) {
|
|
2333
|
+
constructor(logger, registry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, driveContainerTypes, config, executionScope, signer, trustPolicy, flusher = new NoopStorageFlusher()) {
|
|
2254
2334
|
this.logger = logger;
|
|
2255
2335
|
this.registry = registry;
|
|
2256
2336
|
this.operationStore = operationStore;
|
|
@@ -2260,6 +2340,7 @@ var SimpleJobExecutor = class {
|
|
|
2260
2340
|
this.documentMetaCache = documentMetaCache;
|
|
2261
2341
|
this.collectionMembershipCache = collectionMembershipCache;
|
|
2262
2342
|
this.driveContainerTypes = driveContainerTypes;
|
|
2343
|
+
this.flusher = flusher;
|
|
2263
2344
|
this.signer = signer ?? new PassthroughSigner();
|
|
2264
2345
|
this.featureFlags = resolveFeatureFlags(config.featureFlags);
|
|
2265
2346
|
this.config = {
|
|
@@ -2274,7 +2355,8 @@ var SimpleJobExecutor = class {
|
|
|
2274
2355
|
batchApplies: config.batchApplies ?? true,
|
|
2275
2356
|
signatureVerification: config.signatureVerification ?? "enforce",
|
|
2276
2357
|
protocolSupport: config.protocolSupport ?? localSupports(PEER_CAPABILITIES, this.featureFlags).protocols,
|
|
2277
|
-
maxPurgeOperations: config.maxPurgeOperations ?? 2e5
|
|
2358
|
+
maxPurgeOperations: config.maxPurgeOperations ?? 2e5,
|
|
2359
|
+
durabilityWaitMs: config.durabilityWaitMs ?? 0
|
|
2278
2360
|
};
|
|
2279
2361
|
this.decisionModel = selectDecisionModel(this.featureFlags, registry);
|
|
2280
2362
|
this.signatureAdmission = new SignatureAdmission(this.config.signatureVerification, logger, eventBus, this.featureFlags.documentDecisions ? "write-cache" : "meta", {
|
|
@@ -2305,7 +2387,7 @@ var SimpleJobExecutor = class {
|
|
|
2305
2387
|
* but nothing here closes it, and a caller that needs to know a write is
|
|
2306
2388
|
* real has the job status to ask.
|
|
2307
2389
|
*/
|
|
2308
|
-
async executeJob(job, signal) {
|
|
2390
|
+
async executeJob(job, signal, onCommitting) {
|
|
2309
2391
|
const startTime = Date.now();
|
|
2310
2392
|
const touchedStreams = new TouchedStreams();
|
|
2311
2393
|
const postCommitInvalidations = [];
|
|
@@ -2325,6 +2407,8 @@ var SimpleJobExecutor = class {
|
|
|
2325
2407
|
postCommitMetaInvalidations
|
|
2326
2408
|
});
|
|
2327
2409
|
if (!scoped.result.success) throw new JobRollbackSignal(scoped.result);
|
|
2410
|
+
signal?.throwIfAborted();
|
|
2411
|
+
onCommitting?.();
|
|
2328
2412
|
return scoped;
|
|
2329
2413
|
}, signal);
|
|
2330
2414
|
} catch (error) {
|
|
@@ -2336,12 +2420,54 @@ var SimpleJobExecutor = class {
|
|
|
2336
2420
|
for (const documentId of postCommitMembershipInvalidations) this.collectionMembershipCache.invalidate(documentId);
|
|
2337
2421
|
for (const documentId of postCommitMetaInvalidations) this.documentMetaCache.invalidate(documentId);
|
|
2338
2422
|
const { pendingEvent } = outcome;
|
|
2339
|
-
if (pendingEvent) this.eventBus.emit(ReactorEventTypes.JOB_WRITE_READY, pendingEvent).catch((error) => {
|
|
2423
|
+
if (pendingEvent && await this.awaitDurable(job, signal)) this.eventBus.emit(ReactorEventTypes.JOB_WRITE_READY, pendingEvent).catch((error) => {
|
|
2340
2424
|
this.logger.error("Failed to emit JOB_WRITE_READY event: @Event : @Error", pendingEvent, error);
|
|
2341
2425
|
});
|
|
2342
2426
|
return outcome.result;
|
|
2343
2427
|
}
|
|
2344
2428
|
/**
|
|
2429
|
+
* Waits for a flush covering the committed job before its write-ready goes
|
|
2430
|
+
* out; false withholds the announcement. A failed flush is never a failed
|
|
2431
|
+
* job, since the commit stands: it is retried, and the announcement is
|
|
2432
|
+
* withheld once the store is poisoned (the host restarts), the attempts run
|
|
2433
|
+
* out, the wait outlives `durabilityWaitMs`, or the job's signal aborts.
|
|
2434
|
+
*/
|
|
2435
|
+
async awaitDurable(job, signal) {
|
|
2436
|
+
if (isCursorProtectedLoad(job)) return true;
|
|
2437
|
+
const budgetMs = this.config.durabilityWaitMs;
|
|
2438
|
+
const deadline = budgetMs > 0 ? Date.now() + budgetMs : Infinity;
|
|
2439
|
+
for (let attempt = 1;; attempt++) {
|
|
2440
|
+
try {
|
|
2441
|
+
if (await this.withinDurabilityWait(this.flusher.flush(), deadline, signal) !== TIMED_OUT) return true;
|
|
2442
|
+
this.logger.error("Job @JobId committed but its flush outlived the durability wait or the job was aborted; its write-ready is withheld", job.id);
|
|
2443
|
+
return false;
|
|
2444
|
+
} catch (error) {
|
|
2445
|
+
this.logger.error("Flush before announcing job @JobId failed (attempt @Attempt): @Error", job.id, attempt, error);
|
|
2446
|
+
if (error instanceof StoragePoisonedError || attempt >= MAX_ANNOUNCE_FLUSH_ATTEMPTS) {
|
|
2447
|
+
this.logger.error("Job @JobId committed but is not durable; its write-ready is withheld", job.id);
|
|
2448
|
+
return false;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
if (await this.withinDurabilityWait(delay(Math.min(this.config.retryMaxDelayMs, this.config.retryBaseDelayMs * 2 ** (attempt - 1))), deadline, signal) === TIMED_OUT) return false;
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
/** Settles with TIMED_OUT at the deadline or on abort; the wait itself is abandoned. */
|
|
2455
|
+
async withinDurabilityWait(pending, deadline, signal) {
|
|
2456
|
+
pending.catch(() => void 0);
|
|
2457
|
+
if (signal?.aborted) return TIMED_OUT;
|
|
2458
|
+
let onAbort;
|
|
2459
|
+
const aborted = new Promise((resolve) => {
|
|
2460
|
+
onAbort = () => resolve(TIMED_OUT);
|
|
2461
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2462
|
+
});
|
|
2463
|
+
try {
|
|
2464
|
+
const raced = Promise.race([pending, aborted]);
|
|
2465
|
+
return deadline === Infinity ? await raced : await withDeadline(raced, Math.max(0, deadline - Date.now()));
|
|
2466
|
+
} finally {
|
|
2467
|
+
if (onAbort) signal?.removeEventListener("abort", onAbort);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
/**
|
|
2345
2471
|
* The body of a job, run inside the execution scope's transaction.
|
|
2346
2472
|
*
|
|
2347
2473
|
* The stores and caches it works through are the copies scoped to that
|
|
@@ -3761,6 +3887,6 @@ var SimpleJobExecutor = class {
|
|
|
3761
3887
|
//#region src/core/drive-container-types.ts
|
|
3762
3888
|
const DEFAULT_DRIVE_CONTAINER_TYPES = /* @__PURE__ */ new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
|
|
3763
3889
|
//#endregion
|
|
3764
|
-
export {
|
|
3890
|
+
export { PropagationMode as C, JobStatus as S, cursorProtectedLoadMeta as T, selectDecisionModel as _, NoopStorageFlusher as a, buildDecisionModel as b, DEFAULT_PURGE_DELETE_BATCH as c, resolveFeatureFlags as d, DEFAULT_DEFERRED_JOB_TTL_MS as f, decideAtHead as g, JobExecutorEventTypes as h, PassthroughSigner as i, KyselyDocumentPurger as l, DEFAULT_MAX_PURGE_OPERATIONS as m, SimpleJobExecutor as n, StoragePoisonedError as o, DEFAULT_JOB_TIMEOUT_MS as p, verifyActionSignature as r, KyselyExecutionScope as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, appliedDelete as u, documentDecisionModel as v, RelationshipChangeType as w, DriveCollectionId as x, authDecisionModel as y };
|
|
3765
3891
|
|
|
3766
|
-
//# sourceMappingURL=drive-container-types-
|
|
3892
|
+
//# sourceMappingURL=drive-container-types-BAfFzBOf.js.map
|