@sekiban/dcb-runtime 0.2.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 (153) hide show
  1. package/LICENSE +86 -0
  2. package/README.md +13 -0
  3. package/dist/allocator/AllocatorDurableObject.d.ts +36 -0
  4. package/dist/allocator/AllocatorDurableObject.js +658 -0
  5. package/dist/allocator/IssuanceLedger.d.ts +188 -0
  6. package/dist/allocator/IssuanceLedger.js +608 -0
  7. package/dist/allocator/IssuanceReconciler.d.ts +41 -0
  8. package/dist/allocator/IssuanceReconciler.js +191 -0
  9. package/dist/allocator/OrderClock.d.ts +41 -0
  10. package/dist/allocator/OrderClock.js +92 -0
  11. package/dist/allocator/SortableUniqueId.d.ts +46 -0
  12. package/dist/allocator/SortableUniqueId.js +156 -0
  13. package/dist/allocator/types.d.ts +52 -0
  14. package/dist/allocator/types.js +1 -0
  15. package/dist/bootstrap/BootstrapCoordinatorDurableObject.d.ts +28 -0
  16. package/dist/bootstrap/BootstrapCoordinatorDurableObject.js +357 -0
  17. package/dist/bootstrap/BootstrapStoreAdapter.d.ts +51 -0
  18. package/dist/bootstrap/BootstrapStoreAdapter.js +162 -0
  19. package/dist/bootstrap/OperatorBootstrap.d.ts +20 -0
  20. package/dist/bootstrap/OperatorBootstrap.js +104 -0
  21. package/dist/bootstrap/manifest.d.ts +9 -0
  22. package/dist/bootstrap/manifest.js +115 -0
  23. package/dist/bootstrap/types.d.ts +75 -0
  24. package/dist/bootstrap/types.js +1 -0
  25. package/dist/cli/OperatorRepairCli.d.ts +11 -0
  26. package/dist/cli/OperatorRepairCli.js +108 -0
  27. package/dist/cloudflare.d.ts +164 -0
  28. package/dist/cloudflare.js +19487 -0
  29. package/dist/commit/CommitWorker.d.ts +101 -0
  30. package/dist/commit/CommitWorker.js +921 -0
  31. package/dist/commit/types.d.ts +25 -0
  32. package/dist/commit/types.js +13 -0
  33. package/dist/completeness/GlobalCompletenessReconciler.d.ts +77 -0
  34. package/dist/completeness/GlobalCompletenessReconciler.js +572 -0
  35. package/dist/completeness/types.d.ts +85 -0
  36. package/dist/completeness/types.js +6 -0
  37. package/dist/composition.d.ts +123 -0
  38. package/dist/composition.js +237 -0
  39. package/dist/cosmos.d.ts +11 -0
  40. package/dist/cosmos.js +1411 -0
  41. package/dist/d1-mv.d.ts +8 -0
  42. package/dist/d1-mv.js +2001 -0
  43. package/dist/d1.d.ts +16 -0
  44. package/dist/d1.js +3418 -0
  45. package/dist/diagnostics/G60DurableHop.d.ts +99 -0
  46. package/dist/diagnostics/G60DurableHop.js +248 -0
  47. package/dist/diagnostics/G65Admission.d.ts +23 -0
  48. package/dist/diagnostics/G65Admission.js +43 -0
  49. package/dist/diagnostics/G65DirectRing.d.ts +33 -0
  50. package/dist/diagnostics/G65DirectRing.js +85 -0
  51. package/dist/diagnostics/G69AdmissionAttempt.d.ts +43 -0
  52. package/dist/diagnostics/G69AdmissionAttempt.js +22 -0
  53. package/dist/downstream/DeliveryCore.d.ts +127 -0
  54. package/dist/downstream/DeliveryCore.js +304 -0
  55. package/dist/downstream/Doorbell.d.ts +43 -0
  56. package/dist/downstream/Doorbell.js +137 -0
  57. package/dist/downstream/DownstreamAdapter.d.ts +52 -0
  58. package/dist/downstream/DownstreamAdapter.js +299 -0
  59. package/dist/downstream/ExclusionLookup.d.ts +36 -0
  60. package/dist/downstream/ExclusionLookup.js +50 -0
  61. package/dist/downstream/InconsistencyDetector.d.ts +27 -0
  62. package/dist/downstream/InconsistencyDetector.js +82 -0
  63. package/dist/downstream/OutboxDrain.d.ts +35 -0
  64. package/dist/downstream/OutboxDrain.js +126 -0
  65. package/dist/downstream/types.d.ts +77 -0
  66. package/dist/downstream/types.js +64 -0
  67. package/dist/eventIdentity.d.ts +30 -0
  68. package/dist/eventIdentity.js +49 -0
  69. package/dist/eventRecord.d.ts +26 -0
  70. package/dist/eventRecord.js +53 -0
  71. package/dist/g42-probe.d.ts +9 -0
  72. package/dist/g42-probe.js +9 -0
  73. package/dist/http/SerializedQueryWorker.d.ts +58 -0
  74. package/dist/http/SerializedQueryWorker.js +497 -0
  75. package/dist/http/commitResponse.d.ts +46 -0
  76. package/dist/http/commitResponse.js +80 -0
  77. package/dist/http/testServiceId.d.ts +17 -0
  78. package/dist/http/testServiceId.js +14 -0
  79. package/dist/index.d.ts +112 -0
  80. package/dist/index.js +19926 -0
  81. package/dist/journal/JournalDurableObject.d.ts +30 -0
  82. package/dist/journal/JournalDurableObject.js +481 -0
  83. package/dist/journal/JournalFirstTouchProbe.d.ts +190 -0
  84. package/dist/journal/JournalFirstTouchProbe.js +375 -0
  85. package/dist/journal/types.d.ts +116 -0
  86. package/dist/journal/types.js +52 -0
  87. package/dist/mv/MaterializedViewCatchUp.d.ts +60 -0
  88. package/dist/mv/MaterializedViewCatchUp.js +353 -0
  89. package/dist/mv/MaterializedViewRuntime.d.ts +80 -0
  90. package/dist/mv/MaterializedViewRuntime.js +328 -0
  91. package/dist/mv/MaterializedViewStore.d.ts +257 -0
  92. package/dist/mv/MaterializedViewStore.js +875 -0
  93. package/dist/mv/UnsafeWindowMaterializedView.d.ts +112 -0
  94. package/dist/mv/UnsafeWindowMaterializedView.js +495 -0
  95. package/dist/mv/index.d.ts +8 -0
  96. package/dist/mv/index.js +2357 -0
  97. package/dist/projection/LiveProjectionWorker.d.ts +86 -0
  98. package/dist/projection/LiveProjectionWorker.js +240 -0
  99. package/dist/projection/ProjectionRuntime.d.ts +113 -0
  100. package/dist/projection/ProjectionRuntime.js +346 -0
  101. package/dist/projection/ProjectorRegistry.d.ts +51 -0
  102. package/dist/projection/ProjectorRegistry.js +116 -0
  103. package/dist/query/ProjectionQueryStore.d.ts +102 -0
  104. package/dist/query/ProjectionQueryStore.js +185 -0
  105. package/dist/query/QueryRegistry.d.ts +22 -0
  106. package/dist/query/QueryRegistry.js +36 -0
  107. package/dist/read/SerializedReadWorker.d.ts +36 -0
  108. package/dist/read/SerializedReadWorker.js +225 -0
  109. package/dist/repair/RepairWorker.d.ts +72 -0
  110. package/dist/repair/RepairWorker.js +461 -0
  111. package/dist/safeWindow.d.ts +20 -0
  112. package/dist/safeWindow.js +36 -0
  113. package/dist/scope/ControlRouteScope.d.ts +22 -0
  114. package/dist/scope/ControlRouteScope.js +36 -0
  115. package/dist/scope/ScopeName.d.ts +34 -0
  116. package/dist/scope/ScopeName.js +67 -0
  117. package/dist/service/ServiceIdentityProvider.d.ts +35 -0
  118. package/dist/service/ServiceIdentityProvider.js +56 -0
  119. package/dist/store/CosmosEventStore.d.ts +126 -0
  120. package/dist/store/CosmosEventStore.js +950 -0
  121. package/dist/store/D1EventStore.d.ts +111 -0
  122. package/dist/store/D1EventStore.js +1111 -0
  123. package/dist/store/PostgresEventStore.d.ts +35 -0
  124. package/dist/store/PostgresEventStore.js +811 -0
  125. package/dist/store/provider.d.ts +25 -0
  126. package/dist/store/provider.js +32 -0
  127. package/dist/store/types.d.ts +205 -0
  128. package/dist/store/types.js +18 -0
  129. package/dist/tag/EventDigest.d.ts +40 -0
  130. package/dist/tag/EventDigest.js +158 -0
  131. package/dist/tag/TagDurableObject.d.ts +286 -0
  132. package/dist/tag/TagDurableObject.js +4071 -0
  133. package/dist/tag/TagSqlMeasurement.d.ts +26 -0
  134. package/dist/tag/TagSqlMeasurement.js +44 -0
  135. package/dist/tag/TagSqlSchema.d.ts +29 -0
  136. package/dist/tag/TagSqlSchema.js +211 -0
  137. package/dist/tag/types.d.ts +173 -0
  138. package/dist/tag/types.js +3 -0
  139. package/dist/tagstate/TagStateDurableObject.d.ts +57 -0
  140. package/dist/tagstate/TagStateDurableObject.js +499 -0
  141. package/dist/trace/AttributionRatio.d.ts +12 -0
  142. package/dist/trace/AttributionRatio.js +68 -0
  143. package/dist/trace/CloudflareTracing.d.ts +7 -0
  144. package/dist/trace/CloudflareTracing.js +9 -0
  145. package/dist/trace/CommitTrace.d.ts +405 -0
  146. package/dist/trace/CommitTrace.js +1008 -0
  147. package/dist/trace/CommitTraceConsoleSink.d.ts +68 -0
  148. package/dist/trace/CommitTraceConsoleSink.js +114 -0
  149. package/dist/trace/CommitTraceVerifier.d.ts +22 -0
  150. package/dist/trace/CommitTraceVerifier.js +295 -0
  151. package/dist/trace/ObservationStream.d.ts +118 -0
  152. package/dist/trace/ObservationStream.js +148 -0
  153. package/package.json +71 -0
@@ -0,0 +1,1111 @@
1
+ import { decayedLagEstimateMs } from "../safeWindow";
2
+ import { appendG69AdmissionAttempt } from "../diagnostics/G69AdmissionAttempt";
3
+ import { resolveDeliveryIdentity } from "../eventIdentity";
4
+ import { assertSortableUniqueId } from "../allocator/SortableUniqueId";
5
+ import { CANONICAL_UTC_TIMESTAMP_PATTERN, isRfc4122Uuid, isUuidV7, serializedEventMetadata } from "../eventRecord";
6
+ import { CanonicalEventIdentityConflictError, } from "./types";
7
+ /** A contradictory EventId identity is a typed fail-closed outcome. */
8
+ export class D1IdentityConflictError extends CanonicalEventIdentityConflictError {
9
+ beforeMutation;
10
+ constructor(message, beforeMutation = false) {
11
+ super("d1", "", message);
12
+ this.name = "D1IdentityConflictError";
13
+ this.beforeMutation = beforeMutation;
14
+ }
15
+ }
16
+ function asString(value, name) {
17
+ if (typeof value !== "string")
18
+ throw new Error(`D1 ${name} was not a string`);
19
+ return value;
20
+ }
21
+ function nullableString(value, name) {
22
+ return value === null ? null : asString(value, name);
23
+ }
24
+ function asNumber(value, name) {
25
+ const number = typeof value === "number" ? value : Number(value);
26
+ if (!Number.isSafeInteger(number))
27
+ throw new Error(`D1 ${name} was not a safe integer`);
28
+ return number;
29
+ }
30
+ function errorText(error) {
31
+ return (error instanceof Error ? error.message : String(error)).slice(0, 256);
32
+ }
33
+ function sortedUnique(values) {
34
+ return [...new Set(values)].sort((left, right) => binaryCompare(left, right));
35
+ }
36
+ function equalStrings(left, right) {
37
+ return left.length === right.length && left.every((value, index) => value === right[index]);
38
+ }
39
+ /** Reject an envelope whose declared source facts cannot describe this tag copy. */
40
+ function assertCompletenessEnvelope(message) {
41
+ const facts = message.completeness;
42
+ const declared = sortedUnique(message.eventTags);
43
+ if (!equalStrings(declared, sortedUnique(facts.declaredTagSet))) {
44
+ throw new D1IdentityConflictError(`EventId ${message.eventId} declared tag set differs from its source obligation`);
45
+ }
46
+ if (facts.localCommittedMembership.length !== 1) {
47
+ throw new D1IdentityConflictError(`EventId ${message.eventId} source obligation has no exact local membership`);
48
+ }
49
+ const membership = facts.localCommittedMembership[0];
50
+ if (membership.serviceId !== message.serviceId || membership.eventId !== message.eventId || membership.tag !== message.tag) {
51
+ throw new D1IdentityConflictError(`EventId ${message.eventId} source obligation membership differs from its delivery envelope`);
52
+ }
53
+ }
54
+ /** V1 SUIDs and path names are opaque bytewise ordinals, never locale/numeric values. */
55
+ export function binaryCompare(left, right) {
56
+ const leftBytes = new TextEncoder().encode(left);
57
+ const rightBytes = new TextEncoder().encode(right);
58
+ const shared = Math.min(leftBytes.length, rightBytes.length);
59
+ for (let index = 0; index < shared; index += 1) {
60
+ const difference = leftBytes[index] - rightBytes[index];
61
+ if (difference !== 0)
62
+ return difference;
63
+ }
64
+ return leftBytes.length - rightBytes.length;
65
+ }
66
+ function asStringArray(value, name) {
67
+ const decoded = typeof value === "string" ? JSON.parse(value) : value;
68
+ if (!Array.isArray(decoded) || !decoded.every((entry) => typeof entry === "string")) {
69
+ throw new Error(`D1 ${name} was not a string array`);
70
+ }
71
+ return [...decoded];
72
+ }
73
+ function eventFrom(row, arrivals) {
74
+ const serviceId = asString(row.service_id, "service_id");
75
+ const eventId = asString(row.event_id, "event_id");
76
+ const tags = asStringArray(row.event_tags, "event_tags");
77
+ return {
78
+ serviceId,
79
+ id: eventId,
80
+ eventId,
81
+ sortableUniqueId: asString(row.suid, "suid"),
82
+ suid: asString(row.suid, "suid"),
83
+ payload: asString(row.payload, "payload"),
84
+ tags,
85
+ eventTags: tags,
86
+ eventType: asString(row.event_type, "event_type"),
87
+ timestamp: asString(row.timestamp, "timestamp"),
88
+ causationId: nullableString(row.causation_id, "causation_id"),
89
+ correlationId: nullableString(row.correlation_id, "correlation_id"),
90
+ executedUser: nullableString(row.executed_user, "executed_user"),
91
+ provenance: "g32",
92
+ ...(typeof row.event_digest === "string" && row.event_digest.length > 0 ? { eventDigest: row.event_digest } : {}),
93
+ firstArrivedAt: asNumber(row.first_arrived_at, "first_arrived_at"),
94
+ lastArrivedAt: asNumber(row.last_arrived_at, "last_arrived_at"),
95
+ maxDeliveryLagMs: asNumber(row.max_delivery_lag_ms, "max_delivery_lag_ms"),
96
+ arrivals: arrivals.map((arrival) => ({
97
+ serviceId: asString(arrival.service_id, "arrival.service_id"),
98
+ eventId: asString(arrival.event_id, "arrival.event_id"),
99
+ tag: asString(arrival.tag, "arrival.tag"),
100
+ enqueuedAt: asNumber(arrival.enqueued_at, "arrival.enqueued_at"),
101
+ arrivedAt: asNumber(arrival.arrived_at, "arrival.arrived_at"),
102
+ lagMs: asNumber(arrival.lag_ms, "arrival.lag_ms"),
103
+ })),
104
+ };
105
+ }
106
+ function pendingFrom(row) {
107
+ return {
108
+ serviceId: asString(row.service_id, "service_id"),
109
+ eventId: asString(row.event_id, "event_id"),
110
+ attemptId: asString(row.attempt_id, "attempt_id"),
111
+ suid: asString(row.suid, "suid"),
112
+ expectedPaths: asStringArray(row.expected_paths, "expected_paths"),
113
+ observedPaths: asStringArray(row.observed_paths, "observed_paths"),
114
+ firstObservedAt: asNumber(row.first_observed_at, "first_observed_at"),
115
+ lagBoundMs: asNumber(row.lag_bound_ms, "lag_bound_ms"),
116
+ };
117
+ }
118
+ function findingFrom(row) {
119
+ const classification = asString(row.classification, "classification");
120
+ if (classification !== "MISSING_STABLE" && classification !== "EXCLUDED_AUDITED" && classification !== "RESOLVED_LATE") {
121
+ throw new Error("D1 finding classification was invalid");
122
+ }
123
+ return {
124
+ serviceId: asString(row.service_id, "service_id"),
125
+ eventId: asString(row.event_id, "event_id"),
126
+ path: asString(row.path, "path"),
127
+ classification,
128
+ firstObservedAt: asNumber(row.first_observed_at, "first_observed_at"),
129
+ lagBoundMs: asNumber(row.lag_bound_ms, "lag_bound_ms"),
130
+ observedAt: asNumber(row.observed_at, "observed_at"),
131
+ };
132
+ }
133
+ function incidentClassification(value) {
134
+ const classification = asString(value, "classification");
135
+ if (classification !== "SUID_COLLISION" && classification !== "ORDER_VIOLATION" && classification !== "LINEAGE_MISMATCH" && classification !== "ORDERING_DETECTOR_UNKNOWN") {
136
+ throw new Error("D1 delivery incident classification was invalid");
137
+ }
138
+ return classification;
139
+ }
140
+ function optionalString(value, name) {
141
+ if (value === null || value === undefined)
142
+ return undefined;
143
+ return asString(value, name);
144
+ }
145
+ function incidentFrom(row) {
146
+ return {
147
+ serviceId: asString(row.service_id, "service_id"),
148
+ identityKey: asString(row.identity_key, "identity_key"),
149
+ classification: incidentClassification(row.classification),
150
+ suid: optionalString(row.suid, "suid"),
151
+ existingEventId: optionalString(row.existing_event_id, "existing_event_id"),
152
+ incomingEventId: optionalString(row.incoming_event_id, "incoming_event_id"),
153
+ eventId: optionalString(row.event_id, "event_id"),
154
+ boundLineageId: optionalString(row.bound_lineage_id, "bound_lineage_id"),
155
+ incomingLineageId: optionalString(row.incoming_lineage_id, "incoming_lineage_id"),
156
+ observedAt: asNumber(row.observed_at, "observed_at"),
157
+ };
158
+ }
159
+ function checkpointFrom(row) {
160
+ return {
161
+ serviceId: asString(row.service_id, "service_id"),
162
+ projectionId: asString(row.projection_id, "projection_id"),
163
+ lastSuid: asString(row.last_suid, "last_suid"),
164
+ stateJson: asString(row.state_json, "state_json"),
165
+ version: asNumber(row.version, "version"),
166
+ updatedAt: asNumber(row.updated_at, "updated_at"),
167
+ };
168
+ }
169
+ function collisionIdentity(serviceId, suid, existingEventId, incomingEventId) {
170
+ const [first, second] = [existingEventId, incomingEventId].sort(binaryCompare);
171
+ return `SUID_COLLISION|${serviceId}|${suid}|${first}|${second}`;
172
+ }
173
+ function lineageIdentity(serviceId, boundLineageId, incomingLineageId) {
174
+ return `LINEAGE_MISMATCH|${serviceId}|${boundLineageId}|${incomingLineageId}`;
175
+ }
176
+ function jsonArray(values) {
177
+ return JSON.stringify(sortedUnique(values));
178
+ }
179
+ /** Normal delivery has fixed serialized metadata; the fenced import lane may retain C# nulls. */
180
+ function metadataForDelivery(message, deliverySource) {
181
+ const serialized = serializedEventMetadata(message.eventId);
182
+ if (deliverySource !== "import") {
183
+ if (message.causationId !== serialized.causationId ||
184
+ message.correlationId !== serialized.correlationId ||
185
+ message.executedUser !== serialized.executedUser) {
186
+ throw new D1IdentityConflictError(`EventId ${message.eventId} metadata must use the serialized C# constants`);
187
+ }
188
+ return serialized;
189
+ }
190
+ const values = [message.causationId, message.correlationId, message.executedUser];
191
+ if (!values.every((value) => value === null || typeof value === "string")) {
192
+ throw new D1IdentityConflictError(`EventId ${message.eventId} import metadata must be string or null`);
193
+ }
194
+ const allNull = values.every((value) => value === null);
195
+ const allSerialized = message.causationId === serialized.causationId &&
196
+ message.correlationId === serialized.correlationId && message.executedUser === serialized.executedUser;
197
+ if (!allNull && !allSerialized) {
198
+ throw new D1IdentityConflictError(`EventId ${message.eventId} import metadata must be all null or serialized constants`);
199
+ }
200
+ return {
201
+ causationId: message.causationId,
202
+ correlationId: message.correlationId,
203
+ executedUser: message.executedUser,
204
+ };
205
+ }
206
+ /**
207
+ * Cloudflare D1 PipelineStore implementation.
208
+ *
209
+ * All schema changes live in migrations/d1. Writes that span several tables
210
+ * are one predeclared D1 batch; there is no interactive transaction or
211
+ * TypeScript read/branch between statements in the durable mutation.
212
+ */
213
+ export class D1EventStore {
214
+ database;
215
+ options;
216
+ initialized = false;
217
+ constructor(database, options = {}) {
218
+ this.database = database;
219
+ this.options = options;
220
+ }
221
+ async initialize() {
222
+ if (this.initialized)
223
+ return;
224
+ // A read verifies the binding without attempting runtime DDL. The
225
+ // versioned migration is the only schema authority.
226
+ await this.database.prepare("SELECT 1 AS migration_binding").all();
227
+ this.initialized = true;
228
+ }
229
+ async recordDelivery(message, arrivedAt, deliverySource = "queue", attemptContext) {
230
+ this.ready();
231
+ const mutationAttemptId = `admission-${crypto.randomUUID()}`;
232
+ let result;
233
+ let failure;
234
+ try {
235
+ result = await this.recordDeliveryCore(message, arrivedAt, deliverySource, mutationAttemptId);
236
+ }
237
+ catch (error) {
238
+ failure = { error };
239
+ }
240
+ const retryReason = failure === undefined ? null : errorText(failure.error);
241
+ // A canonical identity rejection is a complete fail-before-batch
242
+ // boundary: not even diagnostic-table INSERT/trim work may be scheduled
243
+ // for this invocation. Other outcomes retain the diagnostic-only receipt,
244
+ // but it remains outside the awaited delivery turn and invocation-owned.
245
+ const suppressDiagnostic = failure?.error instanceof D1IdentityConflictError && failure.error.beforeMutation;
246
+ if (!suppressDiagnostic) {
247
+ const diagnostic = Promise.resolve().then(() => this.bestEffortG69AdmissionAttempt(message, arrivedAt, deliverySource, attemptContext, mutationAttemptId, result, failure, retryReason));
248
+ if (attemptContext?.waitUntil !== undefined)
249
+ attemptContext.waitUntil(diagnostic);
250
+ else
251
+ void diagnostic;
252
+ }
253
+ if (failure !== undefined)
254
+ throw failure.error;
255
+ return result;
256
+ }
257
+ async recordDeliveryCore(message, arrivedAt, deliverySource = "queue", mutationAttemptId = `admission-${crypto.randomUUID()}`) {
258
+ this.ready();
259
+ // G44 source facts belong to a Tag outbox obligation. The fenced G22
260
+ // bootstrap import path has no Tag source partition to acknowledge, so it
261
+ // must not manufacture a receipt or turn an import record into scanner
262
+ // authority. Normal Queue/fast delivery remains fail-closed on the full
263
+ // source envelope.
264
+ const requiresGlobalReceipt = deliverySource !== "import";
265
+ if (requiresGlobalReceipt)
266
+ assertCompletenessEnvelope(message);
267
+ const identity = resolveDeliveryIdentity(message, deliverySource);
268
+ assertSortableUniqueId(message.suid);
269
+ const acceptsImportedId = deliverySource === "import";
270
+ if (!(acceptsImportedId ? isRfc4122Uuid(message.eventId) : isUuidV7(message.eventId))) {
271
+ throw new D1IdentityConflictError(`EventId ${message.eventId} is not an ${acceptsImportedId ? "RFC 4122 UUID" : "UUID v7"}`);
272
+ }
273
+ try {
274
+ JSON.parse(message.payload);
275
+ }
276
+ catch {
277
+ throw new D1IdentityConflictError(`EventId ${message.eventId} payload is not JSON text`);
278
+ }
279
+ const timestamp = message.timestamp ?? new Date(arrivedAt).toISOString();
280
+ // C# DateTimeOffset's round-trip representation carries seven fractional
281
+ // digits. Values authored by the TS commit worker use milliseconds, but
282
+ // imported C# records must retain their exact UTC representation too.
283
+ if (!CANONICAL_UTC_TIMESTAMP_PATTERN.test(timestamp)) {
284
+ throw new D1IdentityConflictError(`EventId ${message.eventId} timestamp is not canonical UTC ISO-8601`);
285
+ }
286
+ const metadata = metadataForDelivery(message, deliverySource);
287
+ // This is the pre-existing correctness preflight for canonical identity
288
+ // conflicts. It is not G69 diagnostic work: it must remain before the
289
+ // durable batch so a conflicting replay cannot write incidents, receipts,
290
+ // or event-arrival state.
291
+ const storedBefore = await this.eventById(message.serviceId, message.eventId);
292
+ const incomingEventType = identity.key;
293
+ const eventTags = [...message.eventTags];
294
+ const tagsJson = JSON.stringify(eventTags);
295
+ if (storedBefore !== undefined && (storedBefore.eventType !== incomingEventType ||
296
+ storedBefore.suid !== message.suid ||
297
+ storedBefore.payload !== message.payload ||
298
+ JSON.stringify(storedBefore.eventTags) !== tagsJson ||
299
+ (requiresGlobalReceipt && storedBefore.eventDigest !== undefined && storedBefore.eventDigest !== message.completeness.eventDigest) ||
300
+ storedBefore.timestamp !== timestamp ||
301
+ storedBefore.causationId !== metadata.causationId ||
302
+ storedBefore.correlationId !== metadata.correlationId ||
303
+ storedBefore.executedUser !== metadata.executedUser)) {
304
+ throw new D1IdentityConflictError(`EventId ${message.eventId} conflicts with its canonical event identity`, true);
305
+ }
306
+ const lagMs = Math.max(0, arrivedAt - message.enqueuedAt);
307
+ const statements = [
308
+ this.database.prepare(`INSERT INTO serialized_dcb_allocator_bindings (service_id, allocator_lineage_id, bound_at)
309
+ VALUES (?, ?, ?)
310
+ ON CONFLICT (service_id) DO NOTHING`).bind(message.serviceId, message.allocatorLineageId, arrivedAt),
311
+ // Binding mismatch is recorded in the same batch before any event path.
312
+ this.database.prepare(`INSERT INTO serialized_dcb_delivery_incidents
313
+ (service_id, identity_key, classification, bound_lineage_id, incoming_lineage_id, observed_at)
314
+ SELECT service_id,
315
+ 'LINEAGE_MISMATCH|' || service_id || '|' || allocator_lineage_id || '|' || ?,
316
+ 'LINEAGE_MISMATCH', allocator_lineage_id, ?, ?
317
+ FROM serialized_dcb_allocator_bindings
318
+ WHERE service_id = ? AND allocator_lineage_id <> ?
319
+ ON CONFLICT (service_id, identity_key) DO NOTHING`).bind(message.allocatorLineageId, message.allocatorLineageId, arrivedAt, message.serviceId, message.allocatorLineageId),
320
+ // G31 needs a target-SUID point gate even though a rejected lineage
321
+ // delivery has no dcb_events row. Keep this compact alias
322
+ // separate from the historical incident identity used by operators.
323
+ this.database.prepare(`INSERT INTO serialized_dcb_wait_target_incidents
324
+ (service_id, suid, classification, event_id, observed_at)
325
+ SELECT ?, ?, 'LINEAGE_MISMATCH', ?, ?
326
+ WHERE EXISTS (
327
+ SELECT 1 FROM serialized_dcb_allocator_bindings
328
+ WHERE service_id = ? AND allocator_lineage_id <> ?
329
+ )
330
+ ON CONFLICT (service_id, suid, classification) DO NOTHING`).bind(message.serviceId, message.suid, message.eventId, arrivedAt, message.serviceId, message.allocatorLineageId),
331
+ // SUID collision is a typed incident rather than a UNIQUE exception.
332
+ this.database.prepare(`INSERT INTO serialized_dcb_delivery_incidents
333
+ (service_id, identity_key, classification, suid, existing_event_id, incoming_event_id, observed_at)
334
+ SELECT "ServiceId",
335
+ 'SUID_COLLISION|' || "ServiceId" || '|' || "SortableUniqueId" || '|' ||
336
+ CASE WHEN "Id" < ? THEN "Id" ELSE ? END || '|' ||
337
+ CASE WHEN "Id" < ? THEN ? ELSE "Id" END,
338
+ 'SUID_COLLISION', "SortableUniqueId", "Id", ?, ?
339
+ FROM dcb_events
340
+ WHERE "ServiceId" = ? AND "SortableUniqueId" COLLATE BINARY = ? COLLATE BINARY AND "Id" <> ?
341
+ AND NOT EXISTS (
342
+ SELECT 1 FROM serialized_dcb_allocator_bindings
343
+ WHERE service_id = ? AND allocator_lineage_id <> ?
344
+ )
345
+ LIMIT 1
346
+ ON CONFLICT (service_id, identity_key) DO NOTHING`).bind(message.eventId, message.eventId, message.eventId, message.eventId, message.eventId, arrivedAt, message.serviceId, message.suid, message.eventId, message.serviceId, message.allocatorLineageId),
347
+ this.database.prepare(`INSERT INTO serialized_dcb_wait_target_incidents
348
+ (service_id, suid, classification, event_id, observed_at)
349
+ SELECT ?, ?, 'SUID_COLLISION', ?, ?
350
+ WHERE EXISTS (
351
+ SELECT 1 FROM dcb_events
352
+ WHERE "ServiceId" = ? AND "SortableUniqueId" COLLATE BINARY = ? COLLATE BINARY
353
+ AND "Id" <> ?
354
+ )
355
+ AND NOT EXISTS (
356
+ SELECT 1 FROM serialized_dcb_allocator_bindings
357
+ WHERE service_id = ? AND allocator_lineage_id <> ?
358
+ )
359
+ ON CONFLICT (service_id, suid, classification) DO NOTHING`).bind(message.serviceId, message.suid, message.eventId, arrivedAt, message.serviceId, message.suid, message.eventId, message.serviceId, message.allocatorLineageId),
360
+ this.database.prepare(`INSERT INTO dcb_events
361
+ ("ServiceId", "Id", "SortableUniqueId", "EventType", "Payload", "Tags", "Timestamp", "CausationId", "CorrelationId", "ExecutedUser", "EventDigest")
362
+ SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
363
+ WHERE NOT EXISTS (
364
+ SELECT 1 FROM serialized_dcb_allocator_bindings
365
+ WHERE service_id = ? AND allocator_lineage_id <> ?
366
+ )
367
+ AND NOT EXISTS (
368
+ SELECT 1 FROM dcb_events
369
+ WHERE "ServiceId" = ? AND "SortableUniqueId" COLLATE BINARY = ? COLLATE BINARY AND "Id" <> ?
370
+ )
371
+ ON CONFLICT ("ServiceId", "Id") DO NOTHING`).bind(message.serviceId, message.eventId, message.suid, incomingEventType, message.payload, tagsJson, timestamp, metadata.causationId, metadata.correlationId, metadata.executedUser, requiresGlobalReceipt ? message.completeness.eventDigest : null, message.serviceId, message.allocatorLineageId, message.serviceId, message.suid, message.eventId),
372
+ // Source discoverability is part of the same atomic global admission
373
+ // batch as the event, membership, and receipt. A local Tag commit can
374
+ // therefore return without a D1 registry probe; until this batch
375
+ // succeeds, G44 has no source authority for the obligation and remains
376
+ // fail-closed. Import delivery deliberately does not manufacture a
377
+ // Tag source partition.
378
+ this.database.prepare(`INSERT INTO serialized_dcb_source_partitions
379
+ (service_id, partition_tag, last_obligation_sequence, registered_at)
380
+ SELECT ?, ?, ?, ?
381
+ WHERE ? = 1 AND EXISTS (
382
+ SELECT 1 FROM dcb_events
383
+ WHERE "ServiceId" = ? AND "Id" = ? AND "EventDigest" = ?
384
+ )
385
+ ON CONFLICT (service_id, partition_tag) DO UPDATE
386
+ SET last_obligation_sequence = MAX(
387
+ serialized_dcb_source_partitions.last_obligation_sequence,
388
+ excluded.last_obligation_sequence
389
+ ),
390
+ registered_at = excluded.registered_at`).bind(message.serviceId, message.tag, message.completeness.obligationSequence, arrivedAt, requiresGlobalReceipt ? 1 : 0, message.serviceId, message.eventId, message.completeness.eventDigest),
391
+ // The global declared event, this tag-side committed membership, and
392
+ // the source-obligation receipt are one D1 atomic batch. A Queue ack or
393
+ // a receipt without this join never becomes source acknowledgement.
394
+ this.database.prepare(`INSERT INTO serialized_dcb_global_memberships
395
+ (service_id, event_id, partition_tag, event_digest, committed_at)
396
+ SELECT ?, ?, ?, ?, ?
397
+ WHERE ? = 1 AND EXISTS (
398
+ SELECT 1 FROM dcb_events
399
+ WHERE "ServiceId" = ? AND "Id" = ? AND "EventDigest" = ?
400
+ )
401
+ ON CONFLICT (service_id, event_id, partition_tag) DO NOTHING`).bind(message.serviceId, message.eventId, message.tag, message.completeness.eventDigest, arrivedAt, requiresGlobalReceipt ? 1 : 0, message.serviceId, message.eventId, message.completeness.eventDigest),
402
+ this.database.prepare(`INSERT INTO serialized_dcb_global_receipts
403
+ (service_id, partition_tag, obligation_sequence, event_id, event_digest, membership_tag, received_at)
404
+ SELECT ?, ?, ?, ?, ?, ?, ?
405
+ WHERE ? = 1 AND EXISTS (
406
+ SELECT 1 FROM serialized_dcb_global_memberships
407
+ WHERE service_id = ? AND event_id = ? AND partition_tag = ? AND event_digest = ?
408
+ )
409
+ ON CONFLICT (service_id, partition_tag, obligation_sequence) DO NOTHING`).bind(message.serviceId, message.tag, message.completeness.obligationSequence, message.eventId, message.completeness.eventDigest, message.tag, arrivedAt, requiresGlobalReceipt ? 1 : 0, message.serviceId, message.eventId, message.tag, message.completeness.eventDigest),
410
+ this.database.prepare(`INSERT INTO dcb_event_ops
411
+ ("ServiceId", "Id", "AttemptId", "FirstAdmissionAttemptId", "LastAdmissionAttemptId", "AllocatorLineageId", "FirstArrivedAt", "LastArrivedAt", "MaxDeliveryLagMs", "FirstArrivedSource", "LastArrivedSource")
412
+ SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
413
+ WHERE EXISTS (
414
+ SELECT 1 FROM dcb_events
415
+ WHERE "ServiceId" = ? AND "Id" = ?
416
+ AND "SortableUniqueId" COLLATE BINARY = ? COLLATE BINARY
417
+ AND "Payload" = ? AND "EventType" = ? AND "Tags" = ?
418
+ )
419
+ ON CONFLICT ("ServiceId", "Id") DO UPDATE
420
+ SET "FirstArrivedAt" = MIN(dcb_event_ops."FirstArrivedAt", excluded."FirstArrivedAt"),
421
+ "LastArrivedAt" = MAX(dcb_event_ops."LastArrivedAt", excluded."LastArrivedAt"),
422
+ "MaxDeliveryLagMs" = MAX(dcb_event_ops."MaxDeliveryLagMs", excluded."MaxDeliveryLagMs"),
423
+ "FirstAdmissionAttemptId" = COALESCE(dcb_event_ops."FirstAdmissionAttemptId", excluded."FirstAdmissionAttemptId"),
424
+ "LastAdmissionAttemptId" = excluded."LastAdmissionAttemptId",
425
+ "FirstArrivedSource" = dcb_event_ops."FirstArrivedSource",
426
+ "LastArrivedSource" = CASE
427
+ WHEN excluded."LastArrivedAt" > dcb_event_ops."LastArrivedAt" THEN excluded."LastArrivedSource"
428
+ ELSE dcb_event_ops."LastArrivedSource"
429
+ END`).bind(message.serviceId, message.eventId, message.attemptId, mutationAttemptId, mutationAttemptId, message.allocatorLineageId, arrivedAt, arrivedAt, lagMs, deliverySource, deliverySource, message.serviceId, message.eventId, message.suid, message.payload, incomingEventType, tagsJson),
430
+ this.database.prepare(`INSERT INTO serialized_dcb_event_arrivals
431
+ (service_id, event_id, tag, enqueued_at, arrived_at, lag_ms)
432
+ SELECT ?, ?, ?, ?, ?, ?
433
+ WHERE EXISTS (
434
+ SELECT 1 FROM dcb_events
435
+ WHERE "ServiceId" = ? AND "Id" = ? AND "SortableUniqueId" COLLATE BINARY = ? COLLATE BINARY
436
+ AND "Payload" = ? AND "EventType" = ? AND "Tags" = ?
437
+ )
438
+ AND NOT EXISTS (
439
+ SELECT 1 FROM serialized_dcb_allocator_bindings
440
+ WHERE service_id = ? AND allocator_lineage_id <> ?
441
+ )
442
+ AND NOT EXISTS (
443
+ SELECT 1 FROM dcb_events collision
444
+ WHERE collision."ServiceId" = ? AND collision."SortableUniqueId" COLLATE BINARY = ? COLLATE BINARY
445
+ AND collision."Id" <> ?
446
+ )
447
+ ON CONFLICT (service_id, event_id, tag) DO UPDATE
448
+ SET enqueued_at = MIN(serialized_dcb_event_arrivals.enqueued_at, excluded.enqueued_at),
449
+ arrived_at = MAX(serialized_dcb_event_arrivals.arrived_at, excluded.arrived_at),
450
+ lag_ms = MAX(serialized_dcb_event_arrivals.lag_ms, excluded.lag_ms)`).bind(message.serviceId, message.eventId, message.tag, message.enqueuedAt, arrivedAt, lagMs, message.serviceId, message.eventId, message.suid, message.payload, incomingEventType, tagsJson, message.serviceId, message.allocatorLineageId, message.serviceId, message.suid, message.eventId),
451
+ this.database.prepare(`INSERT INTO serialized_dcb_lag_estimates (service_id, estimate_ms, observed_at)
452
+ SELECT ?, ?, ?
453
+ WHERE ? = 'queue'
454
+ AND NOT EXISTS (
455
+ SELECT 1 FROM serialized_dcb_allocator_bindings
456
+ WHERE service_id = ? AND allocator_lineage_id <> ?
457
+ )
458
+ AND NOT EXISTS (
459
+ SELECT 1 FROM dcb_events collision
460
+ WHERE collision."ServiceId" = ? AND collision."SortableUniqueId" COLLATE BINARY = ? COLLATE BINARY
461
+ AND collision."Id" <> ?
462
+ )
463
+ AND NOT EXISTS (
464
+ SELECT 1 FROM dcb_events contradictory
465
+ WHERE contradictory."ServiceId" = ? AND contradictory."Id" = ?
466
+ AND (contradictory."SortableUniqueId" COLLATE BINARY <> ? COLLATE BINARY
467
+ OR contradictory."Payload" <> ? OR contradictory."EventType" <> ?
468
+ OR contradictory."Tags" <> ?)
469
+ )
470
+ AND NOT EXISTS (
471
+ SELECT 1 FROM dcb_events prior
472
+ WHERE prior."ServiceId" = ? AND prior."SortableUniqueId" COLLATE BINARY > ? COLLATE BINARY
473
+ )
474
+ ON CONFLICT (service_id) DO UPDATE
475
+ SET estimate_ms = MAX(
476
+ MAX(serialized_dcb_lag_estimates.estimate_ms -
477
+ MAX(0, excluded.observed_at - serialized_dcb_lag_estimates.observed_at), 0),
478
+ excluded.estimate_ms
479
+ ),
480
+ observed_at = excluded.observed_at`).bind(message.serviceId, lagMs, arrivedAt, deliverySource, message.serviceId, message.allocatorLineageId, message.serviceId, message.suid, message.eventId, message.serviceId, message.eventId, message.suid, message.payload, incomingEventType, tagsJson, message.serviceId, message.suid),
481
+ ];
482
+ await this.batch("recordDelivery", statements);
483
+ const binding = await this.lineageBinding(message.serviceId);
484
+ if (binding !== undefined && binding !== message.allocatorLineageId) {
485
+ const incident = await this.incidentByIdentity(message.serviceId, lineageIdentity(message.serviceId, binding, message.allocatorLineageId));
486
+ if (incident === undefined)
487
+ throw new Error("D1 lineage incident was not durable");
488
+ return { outcome: "lineage-mismatch", kind: "lineage-mismatch", incident };
489
+ }
490
+ const collision = await this.collisionEvent(message.serviceId, message.suid, message.eventId);
491
+ if (collision !== undefined) {
492
+ const identity = collisionIdentity(message.serviceId, message.suid, collision, message.eventId);
493
+ const incident = await this.incidentByIdentity(message.serviceId, identity);
494
+ if (incident === undefined)
495
+ throw new Error("D1 SUID collision incident was not durable");
496
+ return { outcome: "suid-collision", kind: "suid-collision", incident };
497
+ }
498
+ const stored = await this.eventById(message.serviceId, message.eventId);
499
+ if (stored === undefined)
500
+ throw new Error("D1 event delivery did not produce a durable row");
501
+ if (stored.suid !== message.suid ||
502
+ stored.payload !== message.payload ||
503
+ JSON.stringify(stored.eventTags) !== tagsJson ||
504
+ stored.eventType !== incomingEventType ||
505
+ (requiresGlobalReceipt && stored.eventDigest !== message.completeness.eventDigest) ||
506
+ stored.timestamp !== timestamp ||
507
+ stored.causationId !== metadata.causationId ||
508
+ stored.correlationId !== metadata.correlationId ||
509
+ stored.executedUser !== metadata.executedUser) {
510
+ throw new D1IdentityConflictError(`EventId ${message.eventId} conflicts with its durable D1 identity`);
511
+ }
512
+ if (requiresGlobalReceipt) {
513
+ const receipt = await this.readGlobalReceiptJoin(message);
514
+ if (receipt === undefined) {
515
+ throw new Error(`D1 event ${message.eventId} has no readable global receipt/membership join`);
516
+ }
517
+ }
518
+ // Mutation-owned admission evidence is diagnostic. It is read by the
519
+ // invocation-lifetime receipt after this core result is returned, never
520
+ // on the awaited delivery path. Without a pre-read, duplicate status is
521
+ // deliberately unverified here; the off-path receipt may classify it
522
+ // from mutation-owned attempt identities.
523
+ return {
524
+ outcome: "stored",
525
+ kind: "stored",
526
+ event: stored,
527
+ mutationEvidence: "unverified",
528
+ };
529
+ }
530
+ async readGlobalReceiptJoin(message) {
531
+ this.ready();
532
+ const row = await this.database.prepare(`SELECT receipt.service_id, receipt.partition_tag, receipt.obligation_sequence,
533
+ receipt.event_id, receipt.event_digest, receipt.membership_tag, receipt.received_at
534
+ FROM serialized_dcb_global_receipts AS receipt
535
+ JOIN serialized_dcb_global_memberships AS membership
536
+ ON membership.service_id = receipt.service_id
537
+ AND membership.event_id = receipt.event_id
538
+ AND membership.partition_tag = receipt.membership_tag
539
+ AND membership.event_digest = receipt.event_digest
540
+ JOIN dcb_events AS event
541
+ ON event."ServiceId" = receipt.service_id
542
+ AND event."Id" = receipt.event_id
543
+ AND event."EventDigest" = receipt.event_digest
544
+ WHERE receipt.service_id = ?
545
+ AND receipt.partition_tag = ?
546
+ AND receipt.obligation_sequence = ?
547
+ AND receipt.event_id = ?
548
+ AND receipt.event_digest = ?
549
+ AND receipt.membership_tag = ?`).bind(message.serviceId, message.tag, message.completeness.obligationSequence, message.eventId, message.completeness.eventDigest, message.tag).first();
550
+ if (row === null || row === undefined)
551
+ return undefined;
552
+ return {
553
+ serviceId: asString(row.service_id, "global_receipt.service_id"),
554
+ partitionTag: asString(row.partition_tag, "global_receipt.partition_tag"),
555
+ obligationSequence: asNumber(row.obligation_sequence, "global_receipt.obligation_sequence"),
556
+ eventId: asString(row.event_id, "global_receipt.event_id"),
557
+ eventDigest: asString(row.event_digest, "global_receipt.event_digest"),
558
+ membershipTag: asString(row.membership_tag, "global_receipt.membership_tag"),
559
+ receivedAt: asNumber(row.received_at, "global_receipt.received_at"),
560
+ };
561
+ }
562
+ async readAllEvents(serviceId, since) {
563
+ this.ready();
564
+ // The empty cursor is the internal "before the first event" sentinel.
565
+ // Every externally supplied cursor is a C#-shape SUID.
566
+ if (since.length !== 0)
567
+ assertSortableUniqueId(since);
568
+ const rows = await this.rows(`SELECT e."ServiceId" AS service_id, e."Id" AS event_id,
569
+ e."SortableUniqueId" AS suid, e."Payload" AS payload,
570
+ e."EventType" AS event_type, e."Tags" AS event_tags,
571
+ e."EventDigest" AS event_digest,
572
+ e."Timestamp" AS timestamp, e."CausationId" AS causation_id,
573
+ e."CorrelationId" AS correlation_id, e."ExecutedUser" AS executed_user,
574
+ COALESCE(o."FirstArrivedAt", 0) AS first_arrived_at,
575
+ COALESCE(o."LastArrivedAt", 0) AS last_arrived_at,
576
+ COALESCE(o."MaxDeliveryLagMs", 0) AS max_delivery_lag_ms
577
+ FROM dcb_events e LEFT JOIN dcb_event_ops o
578
+ ON o."ServiceId" = e."ServiceId" AND o."Id" = e."Id"
579
+ WHERE e."ServiceId" = ? AND e."SortableUniqueId" COLLATE BINARY > ? COLLATE BINARY
580
+ ORDER BY e."SortableUniqueId" COLLATE BINARY ASC, e."Id" COLLATE BINARY ASC`, serviceId, since);
581
+ return Promise.all(rows.map((row) => this.eventFromRow(row)));
582
+ }
583
+ /**
584
+ * G69 strict-order detector: find an event admitted after a projection
585
+ * checkpoint whose opaque SUID is lower than that checkpoint. A replay of
586
+ * an event already known before the checkpoint is deliberately excluded by
587
+ * FirstArrivedAt, so the detector reports new lower-SUID admission rather
588
+ * than ordinary Queue redelivery.
589
+ */
590
+ async findLateLowerSuid(serviceId, checkpointSuid, checkpointUpdatedAt) {
591
+ const evidence = await this.findLateLowerSuidEvidence(serviceId, checkpointSuid, checkpointUpdatedAt);
592
+ return evidence.kind === "late-lower-suid" ? evidence.event : undefined;
593
+ }
594
+ async findLateLowerSuidEvidence(serviceId, checkpointSuid, checkpointUpdatedAt, generation) {
595
+ const detectorStartedAt = Date.now();
596
+ const finish = (result) => ({ ...result, durationMs: Math.max(0, Date.now() - detectorStartedAt) });
597
+ this.ready();
598
+ // Generation ownership is enforced at the MV/quarantine boundary; the
599
+ // source evidence itself is generation-independent and must remain
600
+ // auditable across a generation transition. A malformed generation is
601
+ // still an untrusted clock/context observation, never a reason to open a
602
+ // quarantine.
603
+ if (generation !== undefined && (!Number.isSafeInteger(generation) || generation < 0)) {
604
+ return finish({ kind: "unknown", reason: "generation-invalid" });
605
+ }
606
+ if (checkpointSuid.length === 0)
607
+ return finish({ kind: "miss" });
608
+ if (!Number.isSafeInteger(checkpointUpdatedAt) || checkpointUpdatedAt < 0) {
609
+ return finish({ kind: "unknown", reason: "checkpoint-clock-invalid" });
610
+ }
611
+ try {
612
+ assertSortableUniqueId(checkpointSuid);
613
+ // The old detector materialized every lower-SUID row and then issued an
614
+ // event lookup for each candidate. Keep the proven-wins ordering, but
615
+ // make the normal path bounded: each classification is a single LIMIT 1
616
+ // indexed source/arrival probe, followed by at most one event read.
617
+ const proven = await this.database.prepare(`SELECT e."Id" AS event_id
618
+ FROM dcb_events e
619
+ JOIN dcb_event_ops o
620
+ ON o."ServiceId" = e."ServiceId" AND o."Id" = e."Id"
621
+ WHERE e."ServiceId" = ?
622
+ AND e."SortableUniqueId" COLLATE BINARY < ? COLLATE BINARY
623
+ AND o."FirstArrivedAt" > ?
624
+ AND o."FirstArrivedAt" >= 0
625
+ AND o."LastArrivedAt" >= o."FirstArrivedAt"
626
+ AND o."FirstArrivedSource" IN ('queue', 'fast')
627
+ ORDER BY e."SortableUniqueId" COLLATE BINARY ASC, e."Id" COLLATE BINARY ASC
628
+ LIMIT 1`).bind(serviceId, checkpointSuid, checkpointUpdatedAt).first();
629
+ if (proven !== null && proven !== undefined) {
630
+ const event = await this.eventById(serviceId, asString(proven.event_id, "late_lower_suid.event_id"));
631
+ return event === undefined
632
+ ? finish({ kind: "unknown", reason: "late-event-row-disappeared" })
633
+ : finish({ kind: "late-lower-suid", event });
634
+ }
635
+ // Unknown evidence is checked after proven evidence so an uncertain
636
+ // row cannot hide a later independently proven violation.
637
+ const uncertain = await this.database.prepare(`SELECT o."FirstArrivedAt" AS first_arrived_at,
638
+ o."LastArrivedAt" AS last_arrived_at,
639
+ o."FirstArrivedSource" AS first_arrived_source
640
+ FROM dcb_events e
641
+ JOIN dcb_event_ops o
642
+ ON o."ServiceId" = e."ServiceId" AND o."Id" = e."Id"
643
+ WHERE e."ServiceId" = ?
644
+ AND e."SortableUniqueId" COLLATE BINARY < ? COLLATE BINARY
645
+ AND (
646
+ o."FirstArrivedAt" < 0
647
+ OR o."LastArrivedAt" < o."FirstArrivedAt"
648
+ OR (
649
+ o."FirstArrivedAt" > ?
650
+ AND COALESCE(o."FirstArrivedSource", 'unknown') NOT IN ('queue', 'fast')
651
+ )
652
+ )
653
+ ORDER BY e."SortableUniqueId" COLLATE BINARY ASC, e."Id" COLLATE BINARY ASC
654
+ LIMIT 1`).bind(serviceId, checkpointSuid, checkpointUpdatedAt).first();
655
+ if (uncertain !== null && uncertain !== undefined) {
656
+ const first = asNumber(uncertain.first_arrived_at, "late_lower_suid.first_arrived_at");
657
+ const last = asNumber(uncertain.last_arrived_at, "late_lower_suid.last_arrived_at");
658
+ return finish({
659
+ kind: "unknown",
660
+ reason: first < 0 || last < first ? "arrival-clock-rollback" : "arrival-provenance-untrusted",
661
+ });
662
+ }
663
+ const replay = await this.database.prepare(`SELECT e."Id" AS event_id
664
+ FROM dcb_events e
665
+ JOIN dcb_event_ops o
666
+ ON o."ServiceId" = e."ServiceId" AND o."Id" = e."Id"
667
+ WHERE e."ServiceId" = ?
668
+ AND e."SortableUniqueId" COLLATE BINARY < ? COLLATE BINARY
669
+ AND o."FirstArrivedAt" <= ?
670
+ AND o."LastArrivedAt" > ?
671
+ AND o."LastArrivedSource" IN ('queue', 'fast')
672
+ ORDER BY o."LastArrivedAt" DESC,
673
+ e."SortableUniqueId" COLLATE BINARY ASC,
674
+ e."Id" COLLATE BINARY ASC
675
+ LIMIT 1`).bind(serviceId, checkpointSuid, checkpointUpdatedAt, checkpointUpdatedAt).first();
676
+ if (replay === null || replay === undefined)
677
+ return finish({ kind: "miss" });
678
+ const event = await this.eventById(serviceId, asString(replay.event_id, "replay.event_id"));
679
+ return event === undefined
680
+ ? finish({ kind: "unknown", reason: "replay-event-row-disappeared" })
681
+ : finish({ kind: "replay", event });
682
+ }
683
+ catch (error) {
684
+ return finish({ kind: "unknown", reason: errorText(error) });
685
+ }
686
+ }
687
+ /**
688
+ * G31 source proof: one indexed target-SUID lookup distinguishes pending,
689
+ * unique stored, and contradictory/incident states. It intentionally never
690
+ * delegates to readAllEvents, which would make waitFor history-size bound.
691
+ */
692
+ async readWaitForTarget(serviceId, suid) {
693
+ this.ready();
694
+ assertSortableUniqueId(suid);
695
+ const row = await this.database.prepare(`WITH target AS (
696
+ SELECT "Id" AS event_id
697
+ FROM dcb_events
698
+ WHERE "ServiceId" = ? AND "SortableUniqueId" COLLATE BINARY = ? COLLATE BINARY
699
+ ORDER BY "Id" COLLATE BINARY ASC
700
+ LIMIT 2
701
+ )
702
+ SELECT
703
+ (SELECT COUNT(*) FROM target) AS target_count,
704
+ (SELECT event_id FROM target ORDER BY event_id COLLATE BINARY ASC LIMIT 1) AS event_id,
705
+ CASE WHEN EXISTS (
706
+ SELECT 1
707
+ FROM serialized_dcb_wait_target_incidents incident
708
+ WHERE incident.service_id = ? AND incident.suid COLLATE BINARY = ? COLLATE BINARY
709
+ AND incident.classification IN ('SUID_COLLISION', 'LINEAGE_MISMATCH')
710
+ ) OR EXISTS (
711
+ SELECT 1
712
+ FROM serialized_dcb_delivery_incidents incident
713
+ WHERE incident.service_id = ? AND incident.suid COLLATE BINARY = ? COLLATE BINARY
714
+ AND incident.classification = 'SUID_COLLISION'
715
+ ) THEN 1 ELSE 0 END AS target_incident`).bind(serviceId, suid, serviceId, suid, serviceId, suid).first();
716
+ if (row === null || row === undefined)
717
+ throw new Error("D1 wait target lookup returned no row");
718
+ if (asNumber(row.target_incident, "target_incident") === 1) {
719
+ return { kind: "unavailable", reason: "incident" };
720
+ }
721
+ const count = asNumber(row.target_count, "target_count");
722
+ if (count === 0)
723
+ return { kind: "pending" };
724
+ if (count !== 1 || typeof row.event_id !== "string" || row.event_id.length === 0) {
725
+ return { kind: "unavailable", reason: "suid-contradiction" };
726
+ }
727
+ return { kind: "stored", eventId: row.event_id, suid };
728
+ }
729
+ async currentLagBound(serviceId, nowMs) {
730
+ this.ready();
731
+ const rows = await this.rows(`SELECT estimate_ms, observed_at FROM serialized_dcb_lag_estimates WHERE service_id = ?`, serviceId);
732
+ const row = rows[0];
733
+ if (row === undefined)
734
+ return 0;
735
+ const estimate = asNumber(row.estimate_ms, "estimate_ms");
736
+ const observedAt = asNumber(row.observed_at, "observed_at");
737
+ const decayNow = nowMs !== undefined && nowMs >= 100_000_000_000 && observedAt >= 100_000_000_000
738
+ ? nowMs
739
+ : observedAt;
740
+ return decayedLagEstimateMs(estimate, observedAt, decayNow);
741
+ }
742
+ async lagBoundDiagnostics(serviceId, nowMs) {
743
+ this.ready();
744
+ const rows = await this.rows(`SELECT estimate_ms, observed_at FROM serialized_dcb_lag_estimates WHERE service_id = ?`, serviceId);
745
+ const row = rows[0];
746
+ if (row === undefined) {
747
+ return { serviceIdUsed: serviceId, dynamicLagBoundMs: 0, rowFound: false, rawEstimateMs: null, rawObservedAt: null, nowMs };
748
+ }
749
+ const rawEstimateMs = asNumber(row.estimate_ms, "estimate_ms");
750
+ const rawObservedAt = asNumber(row.observed_at, "observed_at");
751
+ const decayNow = nowMs >= 100_000_000_000 && rawObservedAt >= 100_000_000_000 ? nowMs : rawObservedAt;
752
+ return {
753
+ serviceIdUsed: serviceId,
754
+ dynamicLagBoundMs: decayedLagEstimateMs(rawEstimateMs, rawObservedAt, decayNow),
755
+ rowFound: true,
756
+ rawEstimateMs,
757
+ rawObservedAt,
758
+ nowMs,
759
+ };
760
+ }
761
+ async listProjectionTags(serviceId) {
762
+ this.ready();
763
+ const rows = await this.rows(`SELECT DISTINCT membership.value AS tag
764
+ FROM dcb_events event, json_each(event."Tags") membership
765
+ WHERE event."ServiceId" = ?
766
+ ORDER BY tag COLLATE BINARY ASC`, serviceId);
767
+ return rows.map((row) => asString(row.tag, "tag"));
768
+ }
769
+ async readProjectionCheckpoint(serviceId, projectionId) {
770
+ this.ready();
771
+ const rows = await this.rows(`SELECT service_id, projection_id, last_suid, state_json, version, updated_at
772
+ FROM serialized_dcb_projection_checkpoints
773
+ WHERE service_id = ? AND projection_id = ?`, serviceId, projectionId);
774
+ return rows[0] === undefined ? undefined : checkpointFrom(rows[0]);
775
+ }
776
+ async advanceProjectionCheckpoint(input) {
777
+ this.ready();
778
+ const statement = this.database.prepare(`INSERT INTO serialized_dcb_projection_checkpoints
779
+ (service_id, projection_id, last_suid, state_json, version, updated_at)
780
+ SELECT ?, ?, ?, ?, ?, ?
781
+ WHERE ? IS NULL OR EXISTS (
782
+ SELECT 1 FROM serialized_dcb_projection_checkpoints current
783
+ WHERE current.service_id = ? AND current.projection_id = ?
784
+ AND current.last_suid COLLATE BINARY = ? COLLATE BINARY
785
+ )
786
+ ON CONFLICT (service_id, projection_id) DO UPDATE
787
+ SET last_suid = excluded.last_suid,
788
+ state_json = excluded.state_json,
789
+ version = excluded.version,
790
+ updated_at = excluded.updated_at
791
+ WHERE serialized_dcb_projection_checkpoints.last_suid COLLATE BINARY = ? COLLATE BINARY`).bind(input.serviceId, input.projectionId, input.lastSuid, input.stateJson, input.version, input.updatedAt, input.expectedLastSuid, input.serviceId, input.projectionId, input.expectedLastSuid, input.expectedLastSuid);
792
+ const result = await this.write("advanceProjectionCheckpoint", statement).run();
793
+ // D1's meta.changes is the CAS oracle; no read-then-write race exists.
794
+ return result.meta.changes === 1;
795
+ }
796
+ async projectionLag(serviceId, projectionId, tag) {
797
+ this.ready();
798
+ const checkpoint = await this.readProjectionCheckpoint(serviceId, projectionId);
799
+ const checkpointSuid = checkpoint?.lastSuid ?? "";
800
+ const rows = await this.rows(`SELECT
801
+ COALESCE((SELECT event."SortableUniqueId"
802
+ FROM dcb_events event, json_each(event."Tags") membership
803
+ WHERE event."ServiceId" = ? AND membership.value = ?
804
+ ORDER BY event."SortableUniqueId" COLLATE BINARY DESC LIMIT 1), '') AS head_suid,
805
+ (SELECT COUNT(*)
806
+ FROM dcb_events event, json_each(event."Tags") membership
807
+ WHERE event."ServiceId" = ? AND membership.value = ?
808
+ AND event."SortableUniqueId" COLLATE BINARY > ? COLLATE BINARY) AS behind_events`, serviceId, tag, serviceId, tag, checkpointSuid);
809
+ const row = rows[0] ?? {};
810
+ return {
811
+ serviceId,
812
+ projectionId,
813
+ tag,
814
+ checkpointSuid,
815
+ headSuid: asString(row.head_suid ?? "", "head_suid"),
816
+ behindEvents: asNumber(row.behind_events ?? 0, "behind_events"),
817
+ };
818
+ }
819
+ async upsertPending(message, firstObservedAt, lagBoundMs) {
820
+ this.ready();
821
+ const expectedJson = jsonArray(message.eventTags);
822
+ const observedJson = jsonArray([message.tag]);
823
+ const statements = [
824
+ this.database.prepare(`INSERT INTO serialized_dcb_pending_arrivals
825
+ (service_id, event_id, attempt_id, suid, expected_paths, observed_paths, first_observed_at, lag_bound_ms)
826
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
827
+ ON CONFLICT (service_id, event_id) DO NOTHING`).bind(message.serviceId, message.eventId, message.attemptId, message.suid, expectedJson, observedJson, firstObservedAt, lagBoundMs),
828
+ // JSON1 performs the set union inside the atomic UPDATE. There is no
829
+ // TypeScript read/merge/write window under concurrent deliveries.
830
+ this.database.prepare(`UPDATE serialized_dcb_pending_arrivals AS pending
831
+ SET expected_paths = COALESCE((
832
+ SELECT json_group_array(value) FROM (
833
+ SELECT value FROM json_each(pending.expected_paths)
834
+ UNION
835
+ SELECT value FROM json_each(?)
836
+ ORDER BY value COLLATE BINARY
837
+ )
838
+ ), '[]'),
839
+ observed_paths = COALESCE((
840
+ SELECT json_group_array(value) FROM (
841
+ SELECT value FROM json_each(pending.observed_paths)
842
+ UNION
843
+ SELECT value FROM json_each(?)
844
+ ORDER BY value COLLATE BINARY
845
+ )
846
+ ), '[]'),
847
+ lag_bound_ms = MAX(lag_bound_ms, ?)
848
+ WHERE service_id = ? AND event_id = ? AND attempt_id = ?
849
+ AND suid COLLATE BINARY = ? COLLATE BINARY`).bind(expectedJson, observedJson, lagBoundMs, message.serviceId, message.eventId, message.attemptId, message.suid),
850
+ ];
851
+ await this.batch("upsertPending", statements);
852
+ const pending = await this.pendingById(message.serviceId, message.eventId);
853
+ if (pending === undefined)
854
+ throw new Error("D1 pending arrival was not persisted");
855
+ if (pending.attemptId !== message.attemptId || pending.suid !== message.suid) {
856
+ throw new D1IdentityConflictError(`EventId ${message.eventId} has contradictory pending-arrival identity`);
857
+ }
858
+ return pending;
859
+ }
860
+ async listPending(serviceId) {
861
+ this.ready();
862
+ const rows = serviceId === undefined
863
+ ? await this.rows(`SELECT service_id, event_id, attempt_id, suid, expected_paths, observed_paths, first_observed_at, lag_bound_ms
864
+ FROM serialized_dcb_pending_arrivals
865
+ ORDER BY service_id COLLATE BINARY ASC, event_id COLLATE BINARY ASC`)
866
+ : await this.rows(`SELECT service_id, event_id, attempt_id, suid, expected_paths, observed_paths, first_observed_at, lag_bound_ms
867
+ FROM serialized_dcb_pending_arrivals
868
+ WHERE service_id = ?
869
+ ORDER BY event_id COLLATE BINARY ASC`, serviceId);
870
+ return rows.map(pendingFrom);
871
+ }
872
+ async appendFinding(finding) {
873
+ this.ready();
874
+ const statement = this.database.prepare(`INSERT INTO serialized_dcb_inconsistency_findings
875
+ (service_id, event_id, path, classification, first_observed_at, lag_bound_ms, observed_at)
876
+ VALUES (?, ?, ?, ?, ?, ?, ?)
877
+ ON CONFLICT (service_id, event_id, path, classification) DO NOTHING`).bind(finding.serviceId, finding.eventId, finding.path, finding.classification, finding.firstObservedAt, finding.lagBoundMs, finding.observedAt);
878
+ await this.write("appendFinding", statement).run();
879
+ }
880
+ async hasFinding(serviceId, eventId, path, classification) {
881
+ this.ready();
882
+ const rows = await this.rows(`SELECT 1 AS present FROM serialized_dcb_inconsistency_findings
883
+ WHERE service_id = ? AND event_id = ? AND path = ? AND classification = ?`, serviceId, eventId, path, classification);
884
+ return rows.length !== 0;
885
+ }
886
+ async listFindings(serviceId, eventId) {
887
+ this.ready();
888
+ const conditions = [];
889
+ const values = [];
890
+ if (serviceId !== undefined) {
891
+ conditions.push("service_id = ?");
892
+ values.push(serviceId);
893
+ }
894
+ if (eventId !== undefined) {
895
+ conditions.push("event_id = ?");
896
+ values.push(eventId);
897
+ }
898
+ const rows = await this.rows(`SELECT service_id, event_id, path, classification, first_observed_at, lag_bound_ms, observed_at
899
+ FROM serialized_dcb_inconsistency_findings
900
+ ${conditions.length === 0 ? "" : `WHERE ${conditions.join(" AND ")}`}
901
+ ORDER BY sequence ASC`, ...values);
902
+ return rows.map(findingFrom);
903
+ }
904
+ async appendDeliveryIncident(incident) {
905
+ this.ready();
906
+ const statement = this.database.prepare(`INSERT INTO serialized_dcb_delivery_incidents
907
+ (service_id, identity_key, classification, suid, existing_event_id, incoming_event_id, event_id,
908
+ bound_lineage_id, incoming_lineage_id, observed_at)
909
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
910
+ ON CONFLICT (service_id, identity_key) DO NOTHING`).bind(incident.serviceId, incident.identityKey, incident.classification, incident.suid ?? null, incident.existingEventId ?? null, incident.incomingEventId ?? null, incident.eventId ?? null, incident.boundLineageId ?? null, incident.incomingLineageId ?? null, incident.observedAt);
911
+ await this.write("appendDeliveryIncident", statement).run();
912
+ }
913
+ async hasDeliveryIncident(serviceId, identityKey) {
914
+ this.ready();
915
+ return (await this.incidentByIdentity(serviceId, identityKey)) !== undefined;
916
+ }
917
+ async listDeliveryIncidents(serviceId) {
918
+ this.ready();
919
+ const rows = serviceId === undefined
920
+ ? await this.rows(`SELECT service_id, identity_key, classification, suid, existing_event_id, incoming_event_id, event_id,
921
+ bound_lineage_id, incoming_lineage_id, observed_at
922
+ FROM serialized_dcb_delivery_incidents
923
+ ORDER BY sequence ASC`)
924
+ : await this.rows(`SELECT service_id, identity_key, classification, suid, existing_event_id, incoming_event_id, event_id,
925
+ bound_lineage_id, incoming_lineage_id, observed_at
926
+ FROM serialized_dcb_delivery_incidents
927
+ WHERE service_id = ?
928
+ ORDER BY sequence ASC`, serviceId);
929
+ return rows.map(incidentFrom);
930
+ }
931
+ async batch(operation, statements) {
932
+ const transformed = await this.options.beforeBatch?.(operation, statements, this.database) ?? statements;
933
+ await this.database.batch([...transformed]);
934
+ }
935
+ write(operation, statement) {
936
+ return this.options.beforeWrite?.(operation, statement, this.database) ?? statement;
937
+ }
938
+ async rows(statement, ...values) {
939
+ const result = await this.database.prepare(statement).bind(...values).all();
940
+ return result.results;
941
+ }
942
+ async eventById(serviceId, eventId) {
943
+ const rows = await this.rows(`SELECT e."ServiceId" AS service_id, e."Id" AS event_id,
944
+ e."SortableUniqueId" AS suid, e."Payload" AS payload,
945
+ e."EventType" AS event_type, e."Tags" AS event_tags,
946
+ e."EventDigest" AS event_digest,
947
+ e."Timestamp" AS timestamp, e."CausationId" AS causation_id,
948
+ e."CorrelationId" AS correlation_id, e."ExecutedUser" AS executed_user,
949
+ COALESCE(o."FirstArrivedAt", 0) AS first_arrived_at,
950
+ COALESCE(o."LastArrivedAt", 0) AS last_arrived_at,
951
+ COALESCE(o."MaxDeliveryLagMs", 0) AS max_delivery_lag_ms
952
+ FROM dcb_events e LEFT JOIN dcb_event_ops o
953
+ ON o."ServiceId" = e."ServiceId" AND o."Id" = e."Id"
954
+ WHERE e."ServiceId" = ? AND e."Id" = ?`, serviceId, eventId);
955
+ return rows[0] === undefined ? undefined : this.eventFromRow(rows[0]);
956
+ }
957
+ async eventFromRow(row) {
958
+ const arrivals = await this.rows(`SELECT service_id, event_id, tag, enqueued_at, arrived_at, lag_ms
959
+ FROM serialized_dcb_event_arrivals
960
+ WHERE service_id = ? AND event_id = ?
961
+ ORDER BY tag COLLATE BINARY ASC`, asString(row.service_id, "service_id"), asString(row.event_id, "event_id"));
962
+ return eventFrom(row, arrivals);
963
+ }
964
+ async pendingById(serviceId, eventId) {
965
+ const rows = await this.rows(`SELECT service_id, event_id, attempt_id, suid, expected_paths, observed_paths, first_observed_at, lag_bound_ms
966
+ FROM serialized_dcb_pending_arrivals WHERE service_id = ? AND event_id = ?`, serviceId, eventId);
967
+ return rows[0] === undefined ? undefined : pendingFrom(rows[0]);
968
+ }
969
+ async lineageBinding(serviceId) {
970
+ const rows = await this.rows(`SELECT allocator_lineage_id FROM serialized_dcb_allocator_bindings WHERE service_id = ?`, serviceId);
971
+ return rows[0] === undefined ? undefined : asString(rows[0].allocator_lineage_id, "allocator_lineage_id");
972
+ }
973
+ async collisionEvent(serviceId, suid, eventId) {
974
+ const rows = await this.rows(`SELECT "Id" AS event_id FROM dcb_events
975
+ WHERE "ServiceId" = ? AND "SortableUniqueId" COLLATE BINARY = ? COLLATE BINARY AND "Id" <> ?
976
+ ORDER BY "Id" COLLATE BINARY ASC LIMIT 1`, serviceId, suid, eventId);
977
+ return rows[0] === undefined ? undefined : asString(rows[0].event_id, "event_id");
978
+ }
979
+ async incidentByIdentity(serviceId, identityKey) {
980
+ const rows = await this.rows(`SELECT service_id, identity_key, classification, suid, existing_event_id, incoming_event_id, event_id,
981
+ bound_lineage_id, incoming_lineage_id, observed_at
982
+ FROM serialized_dcb_delivery_incidents
983
+ WHERE service_id = ? AND identity_key = ?`, serviceId, identityKey);
984
+ return rows[0] === undefined ? undefined : incidentFrom(rows[0]);
985
+ }
986
+ async eventArrivalOps(serviceId, eventId) {
987
+ const row = await this.database.prepare(`SELECT "FirstArrivedAt" AS first_arrived_at, "LastArrivedAt" AS last_arrived_at
988
+ FROM dcb_event_ops
989
+ WHERE "ServiceId" = ? AND "Id" = ?`).bind(serviceId, eventId).first();
990
+ if (row === null || row === undefined)
991
+ return undefined;
992
+ return {
993
+ firstArrivedAt: asNumber(row.first_arrived_at, "event_ops.first_arrived_at"),
994
+ lastArrivedAt: asNumber(row.last_arrived_at, "event_ops.last_arrived_at"),
995
+ };
996
+ }
997
+ async admissionMutationEvidence(serviceId, eventId, mutationAttemptId) {
998
+ const row = await this.database.prepare(`SELECT "FirstAdmissionAttemptId" AS first_admission_attempt_id,
999
+ "LastAdmissionAttemptId" AS last_admission_attempt_id
1000
+ FROM dcb_event_ops
1001
+ WHERE "ServiceId" = ? AND "Id" = ?`).bind(serviceId, eventId).first();
1002
+ const first = row?.first_admission_attempt_id === null || row?.first_admission_attempt_id === undefined
1003
+ ? undefined
1004
+ : asString(row.first_admission_attempt_id, "event_ops.first_admission_attempt_id");
1005
+ const last = row?.last_admission_attempt_id === null || row?.last_admission_attempt_id === undefined
1006
+ ? undefined
1007
+ : asString(row.last_admission_attempt_id, "event_ops.last_admission_attempt_id");
1008
+ if (first === mutationAttemptId)
1009
+ return "first-admission";
1010
+ if (last === mutationAttemptId)
1011
+ return "duplicate-admission";
1012
+ return "unverified";
1013
+ }
1014
+ async bestEffortG69AdmissionAttempt(message, arrivedAt, deliverySource, attemptContext, mutationAttemptId, result, failure, retryReason) {
1015
+ const diagnosticStartedAt = Date.now();
1016
+ const receiptCostMs = () => Math.max(0, Date.now() - diagnosticStartedAt);
1017
+ const appendFailedDiagnostic = async (reason) => {
1018
+ try {
1019
+ await appendG69AdmissionAttempt(this.database, {
1020
+ serviceId: message.serviceId,
1021
+ eventId: message.eventId,
1022
+ suid: message.suid,
1023
+ tag: message.tag,
1024
+ deliverySource,
1025
+ queueMessageId: attemptContext?.queueMessageId ?? null,
1026
+ attemptId: message.attemptId,
1027
+ allocatorLineageId: message.allocatorLineageId,
1028
+ obligationSequence: message.completeness.obligationSequence,
1029
+ enqueuedAt: message.enqueuedAt,
1030
+ observedAt: Date.now(),
1031
+ arrivedAt,
1032
+ firstArrivedAtBefore: null,
1033
+ lastArrivedAtBefore: null,
1034
+ firstArrivedAtAfter: null,
1035
+ lastArrivedAtAfter: null,
1036
+ beforeObservedAt: null,
1037
+ afterObservedAt: null,
1038
+ observationConsistency: "unverified",
1039
+ mutationEvidence: "unverified",
1040
+ diagnosticDurationMs: receiptCostMs(),
1041
+ status: "failed",
1042
+ retryReason: reason,
1043
+ });
1044
+ }
1045
+ catch {
1046
+ // The diagnostic table remains best effort even when its own failure
1047
+ // prevents a failed-observation row from being retained.
1048
+ }
1049
+ };
1050
+ try {
1051
+ try {
1052
+ await this.options.beforeG69AdmissionAttempt?.();
1053
+ }
1054
+ catch (error) {
1055
+ await appendFailedDiagnostic(errorText(error));
1056
+ return;
1057
+ }
1058
+ const afterObservedAt = Date.now();
1059
+ const afterResult = this.eventArrivalOps(message.serviceId, message.eventId)
1060
+ .then((after) => ({ after, failed: false }))
1061
+ .catch(() => ({ after: undefined, failed: true }));
1062
+ const mutationResult = this.admissionMutationEvidence(message.serviceId, message.eventId, mutationAttemptId)
1063
+ .then((evidence) => ({ evidence, failed: false }))
1064
+ .catch(() => ({ evidence: "unverified", failed: true }));
1065
+ const [{ after, failed: afterReadFailed }, { evidence: observedMutationEvidence }] = await Promise.all([afterResult, mutationResult]);
1066
+ const status = failure !== undefined
1067
+ ? "failed"
1068
+ : result?.outcome === "stored"
1069
+ ? observedMutationEvidence === "duplicate-admission" ? "duplicate" : "stored"
1070
+ : result?.outcome ?? "failed";
1071
+ const finalObservationConsistency = afterReadFailed
1072
+ ? "after-read-failed"
1073
+ : after === undefined
1074
+ ? "unverified"
1075
+ : "after-admission";
1076
+ await appendG69AdmissionAttempt(this.database, {
1077
+ serviceId: message.serviceId,
1078
+ eventId: message.eventId,
1079
+ suid: message.suid,
1080
+ tag: message.tag,
1081
+ deliverySource,
1082
+ queueMessageId: attemptContext?.queueMessageId ?? null,
1083
+ attemptId: message.attemptId,
1084
+ allocatorLineageId: message.allocatorLineageId,
1085
+ obligationSequence: message.completeness.obligationSequence,
1086
+ enqueuedAt: message.enqueuedAt,
1087
+ observedAt: afterObservedAt,
1088
+ arrivedAt,
1089
+ firstArrivedAtBefore: null,
1090
+ lastArrivedAtBefore: null,
1091
+ firstArrivedAtAfter: after?.firstArrivedAt ?? null,
1092
+ lastArrivedAtAfter: after?.lastArrivedAt ?? null,
1093
+ beforeObservedAt: null,
1094
+ afterObservedAt,
1095
+ observationConsistency: finalObservationConsistency,
1096
+ mutationEvidence: observedMutationEvidence,
1097
+ diagnosticDurationMs: receiptCostMs(),
1098
+ status,
1099
+ retryReason,
1100
+ });
1101
+ }
1102
+ catch {
1103
+ // Diagnostic evidence is strictly best effort. It must not alter the
1104
+ // durable event, Queue disposition, G44 receipt, or safe frontier.
1105
+ }
1106
+ }
1107
+ ready() {
1108
+ if (!this.initialized)
1109
+ throw new Error("D1EventStore.initialize() must complete before use");
1110
+ }
1111
+ }