@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.
- package/LICENSE +86 -0
- package/README.md +13 -0
- package/dist/allocator/AllocatorDurableObject.d.ts +36 -0
- package/dist/allocator/AllocatorDurableObject.js +658 -0
- package/dist/allocator/IssuanceLedger.d.ts +188 -0
- package/dist/allocator/IssuanceLedger.js +608 -0
- package/dist/allocator/IssuanceReconciler.d.ts +41 -0
- package/dist/allocator/IssuanceReconciler.js +191 -0
- package/dist/allocator/OrderClock.d.ts +41 -0
- package/dist/allocator/OrderClock.js +92 -0
- package/dist/allocator/SortableUniqueId.d.ts +46 -0
- package/dist/allocator/SortableUniqueId.js +156 -0
- package/dist/allocator/types.d.ts +52 -0
- package/dist/allocator/types.js +1 -0
- package/dist/bootstrap/BootstrapCoordinatorDurableObject.d.ts +28 -0
- package/dist/bootstrap/BootstrapCoordinatorDurableObject.js +357 -0
- package/dist/bootstrap/BootstrapStoreAdapter.d.ts +51 -0
- package/dist/bootstrap/BootstrapStoreAdapter.js +162 -0
- package/dist/bootstrap/OperatorBootstrap.d.ts +20 -0
- package/dist/bootstrap/OperatorBootstrap.js +104 -0
- package/dist/bootstrap/manifest.d.ts +9 -0
- package/dist/bootstrap/manifest.js +115 -0
- package/dist/bootstrap/types.d.ts +75 -0
- package/dist/bootstrap/types.js +1 -0
- package/dist/cli/OperatorRepairCli.d.ts +11 -0
- package/dist/cli/OperatorRepairCli.js +108 -0
- package/dist/cloudflare.d.ts +164 -0
- package/dist/cloudflare.js +19487 -0
- package/dist/commit/CommitWorker.d.ts +101 -0
- package/dist/commit/CommitWorker.js +921 -0
- package/dist/commit/types.d.ts +25 -0
- package/dist/commit/types.js +13 -0
- package/dist/completeness/GlobalCompletenessReconciler.d.ts +77 -0
- package/dist/completeness/GlobalCompletenessReconciler.js +572 -0
- package/dist/completeness/types.d.ts +85 -0
- package/dist/completeness/types.js +6 -0
- package/dist/composition.d.ts +123 -0
- package/dist/composition.js +237 -0
- package/dist/cosmos.d.ts +11 -0
- package/dist/cosmos.js +1411 -0
- package/dist/d1-mv.d.ts +8 -0
- package/dist/d1-mv.js +2001 -0
- package/dist/d1.d.ts +16 -0
- package/dist/d1.js +3418 -0
- package/dist/diagnostics/G60DurableHop.d.ts +99 -0
- package/dist/diagnostics/G60DurableHop.js +248 -0
- package/dist/diagnostics/G65Admission.d.ts +23 -0
- package/dist/diagnostics/G65Admission.js +43 -0
- package/dist/diagnostics/G65DirectRing.d.ts +33 -0
- package/dist/diagnostics/G65DirectRing.js +85 -0
- package/dist/diagnostics/G69AdmissionAttempt.d.ts +43 -0
- package/dist/diagnostics/G69AdmissionAttempt.js +22 -0
- package/dist/downstream/DeliveryCore.d.ts +127 -0
- package/dist/downstream/DeliveryCore.js +304 -0
- package/dist/downstream/Doorbell.d.ts +43 -0
- package/dist/downstream/Doorbell.js +137 -0
- package/dist/downstream/DownstreamAdapter.d.ts +52 -0
- package/dist/downstream/DownstreamAdapter.js +299 -0
- package/dist/downstream/ExclusionLookup.d.ts +36 -0
- package/dist/downstream/ExclusionLookup.js +50 -0
- package/dist/downstream/InconsistencyDetector.d.ts +27 -0
- package/dist/downstream/InconsistencyDetector.js +82 -0
- package/dist/downstream/OutboxDrain.d.ts +35 -0
- package/dist/downstream/OutboxDrain.js +126 -0
- package/dist/downstream/types.d.ts +77 -0
- package/dist/downstream/types.js +64 -0
- package/dist/eventIdentity.d.ts +30 -0
- package/dist/eventIdentity.js +49 -0
- package/dist/eventRecord.d.ts +26 -0
- package/dist/eventRecord.js +53 -0
- package/dist/g42-probe.d.ts +9 -0
- package/dist/g42-probe.js +9 -0
- package/dist/http/SerializedQueryWorker.d.ts +58 -0
- package/dist/http/SerializedQueryWorker.js +497 -0
- package/dist/http/commitResponse.d.ts +46 -0
- package/dist/http/commitResponse.js +80 -0
- package/dist/http/testServiceId.d.ts +17 -0
- package/dist/http/testServiceId.js +14 -0
- package/dist/index.d.ts +112 -0
- package/dist/index.js +19926 -0
- package/dist/journal/JournalDurableObject.d.ts +30 -0
- package/dist/journal/JournalDurableObject.js +481 -0
- package/dist/journal/JournalFirstTouchProbe.d.ts +190 -0
- package/dist/journal/JournalFirstTouchProbe.js +375 -0
- package/dist/journal/types.d.ts +116 -0
- package/dist/journal/types.js +52 -0
- package/dist/mv/MaterializedViewCatchUp.d.ts +60 -0
- package/dist/mv/MaterializedViewCatchUp.js +353 -0
- package/dist/mv/MaterializedViewRuntime.d.ts +80 -0
- package/dist/mv/MaterializedViewRuntime.js +328 -0
- package/dist/mv/MaterializedViewStore.d.ts +257 -0
- package/dist/mv/MaterializedViewStore.js +875 -0
- package/dist/mv/UnsafeWindowMaterializedView.d.ts +112 -0
- package/dist/mv/UnsafeWindowMaterializedView.js +495 -0
- package/dist/mv/index.d.ts +8 -0
- package/dist/mv/index.js +2357 -0
- package/dist/projection/LiveProjectionWorker.d.ts +86 -0
- package/dist/projection/LiveProjectionWorker.js +240 -0
- package/dist/projection/ProjectionRuntime.d.ts +113 -0
- package/dist/projection/ProjectionRuntime.js +346 -0
- package/dist/projection/ProjectorRegistry.d.ts +51 -0
- package/dist/projection/ProjectorRegistry.js +116 -0
- package/dist/query/ProjectionQueryStore.d.ts +102 -0
- package/dist/query/ProjectionQueryStore.js +185 -0
- package/dist/query/QueryRegistry.d.ts +22 -0
- package/dist/query/QueryRegistry.js +36 -0
- package/dist/read/SerializedReadWorker.d.ts +36 -0
- package/dist/read/SerializedReadWorker.js +225 -0
- package/dist/repair/RepairWorker.d.ts +72 -0
- package/dist/repair/RepairWorker.js +461 -0
- package/dist/safeWindow.d.ts +20 -0
- package/dist/safeWindow.js +36 -0
- package/dist/scope/ControlRouteScope.d.ts +22 -0
- package/dist/scope/ControlRouteScope.js +36 -0
- package/dist/scope/ScopeName.d.ts +34 -0
- package/dist/scope/ScopeName.js +67 -0
- package/dist/service/ServiceIdentityProvider.d.ts +35 -0
- package/dist/service/ServiceIdentityProvider.js +56 -0
- package/dist/store/CosmosEventStore.d.ts +126 -0
- package/dist/store/CosmosEventStore.js +950 -0
- package/dist/store/D1EventStore.d.ts +111 -0
- package/dist/store/D1EventStore.js +1111 -0
- package/dist/store/PostgresEventStore.d.ts +35 -0
- package/dist/store/PostgresEventStore.js +811 -0
- package/dist/store/provider.d.ts +25 -0
- package/dist/store/provider.js +32 -0
- package/dist/store/types.d.ts +205 -0
- package/dist/store/types.js +18 -0
- package/dist/tag/EventDigest.d.ts +40 -0
- package/dist/tag/EventDigest.js +158 -0
- package/dist/tag/TagDurableObject.d.ts +286 -0
- package/dist/tag/TagDurableObject.js +4071 -0
- package/dist/tag/TagSqlMeasurement.d.ts +26 -0
- package/dist/tag/TagSqlMeasurement.js +44 -0
- package/dist/tag/TagSqlSchema.d.ts +29 -0
- package/dist/tag/TagSqlSchema.js +211 -0
- package/dist/tag/types.d.ts +173 -0
- package/dist/tag/types.js +3 -0
- package/dist/tagstate/TagStateDurableObject.d.ts +57 -0
- package/dist/tagstate/TagStateDurableObject.js +499 -0
- package/dist/trace/AttributionRatio.d.ts +12 -0
- package/dist/trace/AttributionRatio.js +68 -0
- package/dist/trace/CloudflareTracing.d.ts +7 -0
- package/dist/trace/CloudflareTracing.js +9 -0
- package/dist/trace/CommitTrace.d.ts +405 -0
- package/dist/trace/CommitTrace.js +1008 -0
- package/dist/trace/CommitTraceConsoleSink.d.ts +68 -0
- package/dist/trace/CommitTraceConsoleSink.js +114 -0
- package/dist/trace/CommitTraceVerifier.d.ts +22 -0
- package/dist/trace/CommitTraceVerifier.js +295 -0
- package/dist/trace/ObservationStream.d.ts +118 -0
- package/dist/trace/ObservationStream.js +148 -0
- package/package.json +71 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
export declare const G42_JOURNAL_PROBE_SCHEMA: "sdt.g42.journal-first-touch/v1";
|
|
2
|
+
export declare const G42_JOURNAL_PROBE_PATH: "/conformance/v1/g42/journal-first-touch";
|
|
3
|
+
export declare const G42_JOURNAL_PROBE_INTERNAL_PREFIX: "/__sdt_g42/journal-first-touch";
|
|
4
|
+
/** These fixed-length prefixes are reserved exclusively for the G42 probe. */
|
|
5
|
+
export declare const G42_JOURNAL_PROBE_IDENTITY_PREFIX: "sdt-g42-p1-identity-";
|
|
6
|
+
export declare const G42_JOURNAL_PROBE_LOGICAL_KEY_PREFIX: "sdt-g42-p1-key-";
|
|
7
|
+
export declare const G42_JOURNAL_PROBE_STORAGE_PREFIX: "__sdt_g42_p1_record__:";
|
|
8
|
+
export declare const G42_JOURNAL_PROBE_INDEX_KEY: "__sdt_g42_p1_index__";
|
|
9
|
+
export declare const G42_JOURNAL_PROBE_ALARM_KEY: "__sdt_g42_p1_alarm__";
|
|
10
|
+
export type G42ProbeCell = "A" | "B" | "C" | "D";
|
|
11
|
+
export type G42ProbeAlarmMode = "on" | "off";
|
|
12
|
+
export type G42ProbeMediator = "none" | "handler-ping" | "state-404";
|
|
13
|
+
export interface G42JournalProbeTrial {
|
|
14
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
15
|
+
readonly action: "trial";
|
|
16
|
+
readonly trialId: string;
|
|
17
|
+
readonly blockId: string;
|
|
18
|
+
readonly cell: G42ProbeCell;
|
|
19
|
+
readonly physicalIdentity: string;
|
|
20
|
+
readonly logicalKey: string;
|
|
21
|
+
/**
|
|
22
|
+
* Fixed-width public-envelope key. D uses a distinct key for its warm-up;
|
|
23
|
+
* A/B/C repeat logicalKey as padding so the caller-side A/D request shape
|
|
24
|
+
* cannot itself create a byte-count contrast.
|
|
25
|
+
*/
|
|
26
|
+
readonly warmupLogicalKey: string;
|
|
27
|
+
readonly payload: string;
|
|
28
|
+
readonly alarmMode: G42ProbeAlarmMode;
|
|
29
|
+
readonly mediator: G42ProbeMediator;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* An idle-regime D trial is deliberately split across two authenticated
|
|
33
|
+
* requests. The warm-up must finish *before* the requested idle gap, while
|
|
34
|
+
* the later measurement remains the first handler after that gap. Holding a
|
|
35
|
+
* Worker request open for 180 seconds would itself prevent the observation
|
|
36
|
+
* this probe is intended to make.
|
|
37
|
+
*/
|
|
38
|
+
export interface G42JournalProbePreparation {
|
|
39
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
40
|
+
readonly action: "prepare";
|
|
41
|
+
readonly trialId: string;
|
|
42
|
+
readonly blockId: string;
|
|
43
|
+
readonly cell: "D";
|
|
44
|
+
readonly physicalIdentity: string;
|
|
45
|
+
readonly logicalKey: string;
|
|
46
|
+
readonly warmupLogicalKey: string;
|
|
47
|
+
readonly payload: string;
|
|
48
|
+
readonly alarmMode: G42ProbeAlarmMode;
|
|
49
|
+
readonly mediator: "none";
|
|
50
|
+
}
|
|
51
|
+
export interface G42JournalProbeMeasurement {
|
|
52
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
53
|
+
readonly action: "measure";
|
|
54
|
+
readonly trialId: string;
|
|
55
|
+
readonly blockId: string;
|
|
56
|
+
readonly cell: "D";
|
|
57
|
+
readonly physicalIdentity: string;
|
|
58
|
+
readonly logicalKey: string;
|
|
59
|
+
readonly warmupLogicalKey: string;
|
|
60
|
+
readonly payload: string;
|
|
61
|
+
readonly alarmMode: G42ProbeAlarmMode;
|
|
62
|
+
readonly mediator: "none";
|
|
63
|
+
}
|
|
64
|
+
export interface G42JournalProbeCleanup {
|
|
65
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
66
|
+
readonly action: "cleanup";
|
|
67
|
+
readonly trialId: string;
|
|
68
|
+
readonly physicalIdentity: string;
|
|
69
|
+
}
|
|
70
|
+
export interface G42JournalProbeInventory {
|
|
71
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
72
|
+
readonly action: "inventory";
|
|
73
|
+
readonly trialId: string;
|
|
74
|
+
readonly physicalIdentity: string;
|
|
75
|
+
}
|
|
76
|
+
export type G42JournalProbeRequest = G42JournalProbeTrial | G42JournalProbePreparation | G42JournalProbeMeasurement | G42JournalProbeCleanup | G42JournalProbeInventory;
|
|
77
|
+
export interface G42ProbeActivationFact {
|
|
78
|
+
readonly activationId: string;
|
|
79
|
+
readonly activationFirst: boolean;
|
|
80
|
+
readonly constructorToHandlerMs: number;
|
|
81
|
+
readonly firstStorageReadMs: number | null;
|
|
82
|
+
}
|
|
83
|
+
export interface G42ProbeOperationResult {
|
|
84
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
85
|
+
readonly action: "ping" | "state" | "write" | "cleanup" | "inventory";
|
|
86
|
+
readonly status: number;
|
|
87
|
+
readonly activation: G42ProbeActivationFact;
|
|
88
|
+
readonly handlerWallMs: number;
|
|
89
|
+
readonly transactionWallMs: number | null;
|
|
90
|
+
readonly requestBytes: number;
|
|
91
|
+
readonly recordBytes: number | null;
|
|
92
|
+
readonly alarmStateBefore: number | null;
|
|
93
|
+
readonly alarmDueAt: number | null;
|
|
94
|
+
readonly setAlarmCalls: number;
|
|
95
|
+
readonly logicalKey?: string;
|
|
96
|
+
readonly expectedWarmupKeyPresent?: boolean;
|
|
97
|
+
readonly keyPresent?: boolean;
|
|
98
|
+
readonly inventory?: readonly string[];
|
|
99
|
+
readonly productionJournalPresent?: boolean;
|
|
100
|
+
}
|
|
101
|
+
export interface G42JournalProbeTrialReceipt {
|
|
102
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
103
|
+
readonly trialId: string;
|
|
104
|
+
readonly blockId: string;
|
|
105
|
+
readonly cell: G42ProbeCell;
|
|
106
|
+
readonly physicalIdentity: string;
|
|
107
|
+
readonly logicalKey: string;
|
|
108
|
+
readonly warmupLogicalKey?: string;
|
|
109
|
+
readonly callerWallMs: number;
|
|
110
|
+
readonly callerColo: string | null;
|
|
111
|
+
readonly measured: G42ProbeOperationResult;
|
|
112
|
+
readonly preceding?: G42ProbeOperationResult;
|
|
113
|
+
readonly treatmentCompliance: Readonly<{
|
|
114
|
+
readonly measuredActivationFirst: boolean;
|
|
115
|
+
readonly sharedActivationId: boolean | null;
|
|
116
|
+
readonly mediatorCompletedBeforeMeasurement: boolean | null;
|
|
117
|
+
readonly distinctLogicalKey: boolean | null;
|
|
118
|
+
readonly alarmStateEqualized: boolean;
|
|
119
|
+
readonly alarmSetExactlyOnce: boolean;
|
|
120
|
+
}>;
|
|
121
|
+
}
|
|
122
|
+
export interface G42JournalProbeCleanupReceipt {
|
|
123
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
124
|
+
readonly trialId: string;
|
|
125
|
+
readonly physicalIdentity: string;
|
|
126
|
+
readonly cleanupWallMs: number;
|
|
127
|
+
readonly first: G42ProbeOperationResult;
|
|
128
|
+
readonly repeat: G42ProbeOperationResult;
|
|
129
|
+
readonly inventoryEmpty: boolean;
|
|
130
|
+
}
|
|
131
|
+
export interface G42JournalProbeInventoryReceipt {
|
|
132
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
133
|
+
readonly trialId: string;
|
|
134
|
+
readonly physicalIdentity: string;
|
|
135
|
+
readonly inventory: G42ProbeOperationResult;
|
|
136
|
+
}
|
|
137
|
+
export interface G42JournalProbePreparationReceipt {
|
|
138
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
139
|
+
readonly trialId: string;
|
|
140
|
+
readonly blockId: string;
|
|
141
|
+
readonly cell: "D";
|
|
142
|
+
readonly physicalIdentity: string;
|
|
143
|
+
readonly logicalKey: string;
|
|
144
|
+
readonly warmupLogicalKey: string;
|
|
145
|
+
readonly preparation: G42ProbeOperationResult;
|
|
146
|
+
}
|
|
147
|
+
export interface G42JournalProbeMeasurementReceipt {
|
|
148
|
+
readonly schema: typeof G42_JOURNAL_PROBE_SCHEMA;
|
|
149
|
+
readonly trialId: string;
|
|
150
|
+
readonly blockId: string;
|
|
151
|
+
readonly cell: "D";
|
|
152
|
+
readonly physicalIdentity: string;
|
|
153
|
+
readonly logicalKey: string;
|
|
154
|
+
readonly warmupLogicalKey: string;
|
|
155
|
+
readonly callerWallMs: number;
|
|
156
|
+
readonly callerColo: string | null;
|
|
157
|
+
readonly measured: G42ProbeOperationResult;
|
|
158
|
+
readonly treatmentCompliance: Readonly<{
|
|
159
|
+
readonly measuredActivationFirst: boolean;
|
|
160
|
+
readonly alarmStateEqualized: boolean;
|
|
161
|
+
readonly alarmSetExactlyOnce: boolean;
|
|
162
|
+
}>;
|
|
163
|
+
}
|
|
164
|
+
type ParseResult<T> = Readonly<{
|
|
165
|
+
value: T;
|
|
166
|
+
}> | Readonly<{
|
|
167
|
+
error: string;
|
|
168
|
+
}>;
|
|
169
|
+
export declare function isG42ProbeIdentity(value: unknown): value is string;
|
|
170
|
+
export declare function isG42ProbeLogicalKey(value: unknown): value is string;
|
|
171
|
+
export declare function g42ProbeStorageKey(logicalKey: string): string;
|
|
172
|
+
export declare function parseG42JournalProbeRequest(value: unknown): ParseResult<G42JournalProbeRequest>;
|
|
173
|
+
/**
|
|
174
|
+
* Runs one scheduled logical trial. The caller starts its timer around only
|
|
175
|
+
* the measured write; warm-up/mediator/cleanup are deliberately separate.
|
|
176
|
+
*/
|
|
177
|
+
export declare function runG42JournalProbeTrial(namespace: DurableObjectNamespace, serviceId: string, trial: G42JournalProbeTrial, callerColo?: string | null): Promise<G42JournalProbeTrialReceipt>;
|
|
178
|
+
/**
|
|
179
|
+
* Starts the D-only precondition for an idle-regime trial. The caller must
|
|
180
|
+
* wait outside the Worker, then invoke `measureG42JournalProbeTrial`; this
|
|
181
|
+
* preserves a real opportunity for Durable Object activation continuity to
|
|
182
|
+
* change during the requested idle interval.
|
|
183
|
+
*/
|
|
184
|
+
export declare function prepareG42JournalProbeTrial(namespace: DurableObjectNamespace, serviceId: string, request: G42JournalProbePreparation): Promise<G42JournalProbePreparationReceipt>;
|
|
185
|
+
/** Completes the D-only post-idle measurement without re-running warm-up. */
|
|
186
|
+
export declare function measureG42JournalProbeTrial(namespace: DurableObjectNamespace, serviceId: string, request: G42JournalProbeMeasurement, callerColo?: string | null): Promise<G42JournalProbeMeasurementReceipt>;
|
|
187
|
+
/** Cleanup is intentionally timed outside a trial's measured write. */
|
|
188
|
+
export declare function cleanupG42JournalProbeTrial(namespace: DurableObjectNamespace, serviceId: string, request: G42JournalProbeCleanup): Promise<G42JournalProbeCleanupReceipt>;
|
|
189
|
+
export declare function inventoryG42JournalProbeTrial(namespace: DurableObjectNamespace, serviceId: string, request: G42JournalProbeInventory): Promise<G42JournalProbeInventoryReceipt>;
|
|
190
|
+
export {};
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDT-G42's conformance-only Journal first-touch probe protocol.
|
|
3
|
+
*
|
|
4
|
+
* This is deliberately separate from the Serialized DCB commit protocol and
|
|
5
|
+
* from sdt.commit/v1 / sdt.observe/v1. It gives the primary Worker a small,
|
|
6
|
+
* authenticated control surface for exercising the already-deployed JOURNAL
|
|
7
|
+
* namespace without changing normal commit routing or Journal recovery.
|
|
8
|
+
*/
|
|
9
|
+
import { scopeIdFor } from "../scope/ScopeName";
|
|
10
|
+
export const G42_JOURNAL_PROBE_SCHEMA = "sdt.g42.journal-first-touch/v1";
|
|
11
|
+
export const G42_JOURNAL_PROBE_PATH = "/conformance/v1/g42/journal-first-touch";
|
|
12
|
+
export const G42_JOURNAL_PROBE_INTERNAL_PREFIX = "/__sdt_g42/journal-first-touch";
|
|
13
|
+
/** These fixed-length prefixes are reserved exclusively for the G42 probe. */
|
|
14
|
+
export const G42_JOURNAL_PROBE_IDENTITY_PREFIX = "sdt-g42-p1-identity-";
|
|
15
|
+
export const G42_JOURNAL_PROBE_LOGICAL_KEY_PREFIX = "sdt-g42-p1-key-";
|
|
16
|
+
export const G42_JOURNAL_PROBE_STORAGE_PREFIX = "__sdt_g42_p1_record__:";
|
|
17
|
+
export const G42_JOURNAL_PROBE_INDEX_KEY = "__sdt_g42_p1_index__";
|
|
18
|
+
export const G42_JOURNAL_PROBE_ALARM_KEY = "__sdt_g42_p1_alarm__";
|
|
19
|
+
const HEX_32 = /^[a-f0-9]{32}$/;
|
|
20
|
+
const MAX_PAYLOAD_BYTES = 8_192;
|
|
21
|
+
const MIN_PAYLOAD_BYTES = 16;
|
|
22
|
+
const TRIAL_ID = /^g42-p1-[a-z0-9-]{8,96}$/;
|
|
23
|
+
function object(value) {
|
|
24
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
25
|
+
? value
|
|
26
|
+
: undefined;
|
|
27
|
+
}
|
|
28
|
+
function exactKeys(value, allowed) {
|
|
29
|
+
const expected = new Set(allowed);
|
|
30
|
+
return Object.keys(value).every((key) => expected.has(key)) && allowed.every((key) => key in value);
|
|
31
|
+
}
|
|
32
|
+
function nonEmptyString(value) {
|
|
33
|
+
return typeof value === "string" && value.length > 0;
|
|
34
|
+
}
|
|
35
|
+
export function isG42ProbeIdentity(value) {
|
|
36
|
+
return typeof value === "string" && value.startsWith(G42_JOURNAL_PROBE_IDENTITY_PREFIX)
|
|
37
|
+
&& HEX_32.test(value.slice(G42_JOURNAL_PROBE_IDENTITY_PREFIX.length));
|
|
38
|
+
}
|
|
39
|
+
export function isG42ProbeLogicalKey(value) {
|
|
40
|
+
return typeof value === "string" && value.startsWith(G42_JOURNAL_PROBE_LOGICAL_KEY_PREFIX)
|
|
41
|
+
&& HEX_32.test(value.slice(G42_JOURNAL_PROBE_LOGICAL_KEY_PREFIX.length));
|
|
42
|
+
}
|
|
43
|
+
export function g42ProbeStorageKey(logicalKey) {
|
|
44
|
+
if (!isG42ProbeLogicalKey(logicalKey))
|
|
45
|
+
throw new Error("G42 probe logical key lacks the reserved fixed-length prefix");
|
|
46
|
+
return `${G42_JOURNAL_PROBE_STORAGE_PREFIX}${logicalKey}`;
|
|
47
|
+
}
|
|
48
|
+
export function parseG42JournalProbeRequest(value) {
|
|
49
|
+
const candidate = object(value);
|
|
50
|
+
if (candidate === undefined)
|
|
51
|
+
return { error: "G42 probe body must be a JSON object" };
|
|
52
|
+
if (candidate.schema !== G42_JOURNAL_PROBE_SCHEMA)
|
|
53
|
+
return { error: "G42 probe schema is not recognized" };
|
|
54
|
+
if (!nonEmptyString(candidate.trialId) || !TRIAL_ID.test(candidate.trialId))
|
|
55
|
+
return { error: "G42 probe trialId is not a reserved scheduled identity" };
|
|
56
|
+
if (!isG42ProbeIdentity(candidate.physicalIdentity))
|
|
57
|
+
return { error: "G42 probe physicalIdentity lacks the reserved fixed-length prefix" };
|
|
58
|
+
if (candidate.action === "cleanup" || candidate.action === "inventory") {
|
|
59
|
+
if (!exactKeys(candidate, ["schema", "action", "trialId", "physicalIdentity"]))
|
|
60
|
+
return { error: "G42 probe cleanup/inventory body has an unexpected field" };
|
|
61
|
+
return { value: candidate };
|
|
62
|
+
}
|
|
63
|
+
if (candidate.action !== "trial" && candidate.action !== "prepare" && candidate.action !== "measure") {
|
|
64
|
+
return { error: "G42 probe action is not recognized" };
|
|
65
|
+
}
|
|
66
|
+
if (!exactKeys(candidate, [
|
|
67
|
+
"schema", "action", "trialId", "blockId", "cell", "physicalIdentity", "logicalKey",
|
|
68
|
+
"warmupLogicalKey", "payload", "alarmMode", "mediator",
|
|
69
|
+
]))
|
|
70
|
+
return { error: "G42 probe trial body has an unexpected or missing field" };
|
|
71
|
+
if (!nonEmptyString(candidate.blockId) || !/^g42-p1-block-[0-9]{2}$/.test(candidate.blockId))
|
|
72
|
+
return { error: "G42 probe blockId is not scheduled" };
|
|
73
|
+
if (!["A", "B", "C", "D"].includes(String(candidate.cell)))
|
|
74
|
+
return { error: "G42 probe cell is not recognized" };
|
|
75
|
+
if (!isG42ProbeLogicalKey(candidate.logicalKey))
|
|
76
|
+
return { error: "G42 probe logicalKey lacks the reserved fixed-length prefix" };
|
|
77
|
+
if (!nonEmptyString(candidate.payload))
|
|
78
|
+
return { error: "G42 probe payload is required" };
|
|
79
|
+
const payloadBytes = new TextEncoder().encode(candidate.payload).byteLength;
|
|
80
|
+
if (payloadBytes < MIN_PAYLOAD_BYTES || payloadBytes > MAX_PAYLOAD_BYTES)
|
|
81
|
+
return { error: "G42 probe payload byte length is outside the scheduled range" };
|
|
82
|
+
if (candidate.alarmMode !== "on" && candidate.alarmMode !== "off")
|
|
83
|
+
return { error: "G42 probe alarmMode is not recognized" };
|
|
84
|
+
if (!["none", "handler-ping", "state-404"].includes(String(candidate.mediator)))
|
|
85
|
+
return { error: "G42 probe mediator is not recognized" };
|
|
86
|
+
if ((candidate.action === "prepare" || candidate.action === "measure") && candidate.cell !== "D") {
|
|
87
|
+
return { error: "G42 probe preparation/measurement is D-only" };
|
|
88
|
+
}
|
|
89
|
+
if (candidate.cell === "A" && candidate.mediator !== "none")
|
|
90
|
+
return { error: "G42 probe A cell cannot use a diagnostic mediator" };
|
|
91
|
+
if (candidate.cell === "B" && candidate.mediator !== "handler-ping")
|
|
92
|
+
return { error: "G42 probe B cell requires the handler-ping mediator" };
|
|
93
|
+
if (candidate.cell === "C" && candidate.mediator !== "state-404")
|
|
94
|
+
return { error: "G42 probe C cell requires the state-404 mediator" };
|
|
95
|
+
if (candidate.cell === "D") {
|
|
96
|
+
if (!isG42ProbeLogicalKey(candidate.warmupLogicalKey) || candidate.warmupLogicalKey === candidate.logicalKey) {
|
|
97
|
+
return { error: "G42 probe D cell requires a distinct reserved warmupLogicalKey" };
|
|
98
|
+
}
|
|
99
|
+
if (candidate.mediator !== "none")
|
|
100
|
+
return { error: "G42 probe D cell cannot use a diagnostic mediator" };
|
|
101
|
+
}
|
|
102
|
+
else if (!isG42ProbeLogicalKey(candidate.warmupLogicalKey) || candidate.warmupLogicalKey !== candidate.logicalKey) {
|
|
103
|
+
return { error: "G42 probe non-D envelope padding must equal logicalKey" };
|
|
104
|
+
}
|
|
105
|
+
return { value: candidate };
|
|
106
|
+
}
|
|
107
|
+
function responseBody(response) {
|
|
108
|
+
return response.json().then((value) => {
|
|
109
|
+
const parsed = object(value);
|
|
110
|
+
if (parsed === undefined)
|
|
111
|
+
throw new Error("G42 Journal probe returned a non-object response");
|
|
112
|
+
return parsed;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function finite(value, label) {
|
|
116
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
|
|
117
|
+
throw new Error(`G42 Journal probe ${label} is not a non-negative finite number`);
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
function activation(value) {
|
|
121
|
+
const candidate = object(value);
|
|
122
|
+
if (candidate === undefined || !nonEmptyString(candidate.activationId) || typeof candidate.activationFirst !== "boolean") {
|
|
123
|
+
throw new Error("G42 Journal probe activation fact is malformed");
|
|
124
|
+
}
|
|
125
|
+
const firstStorageReadMs = candidate.firstStorageReadMs;
|
|
126
|
+
if (firstStorageReadMs !== null)
|
|
127
|
+
finite(firstStorageReadMs, "activation.firstStorageReadMs");
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
activationId: candidate.activationId,
|
|
130
|
+
activationFirst: candidate.activationFirst,
|
|
131
|
+
constructorToHandlerMs: finite(candidate.constructorToHandlerMs, "activation.constructorToHandlerMs"),
|
|
132
|
+
firstStorageReadMs: firstStorageReadMs === null ? null : firstStorageReadMs,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function operation(result, status) {
|
|
136
|
+
if (result.schema !== G42_JOURNAL_PROBE_SCHEMA || !["ping", "state", "write", "cleanup", "inventory"].includes(String(result.action))) {
|
|
137
|
+
throw new Error("G42 Journal probe response schema/action is not recognized");
|
|
138
|
+
}
|
|
139
|
+
const maybeInventory = result.inventory;
|
|
140
|
+
if (maybeInventory !== undefined && (!Array.isArray(maybeInventory) || maybeInventory.some((key) => !isG42ProbeLogicalKey(key)))) {
|
|
141
|
+
throw new Error("G42 Journal probe inventory is malformed");
|
|
142
|
+
}
|
|
143
|
+
return Object.freeze({
|
|
144
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
145
|
+
action: result.action,
|
|
146
|
+
status,
|
|
147
|
+
activation: activation(result.activation),
|
|
148
|
+
handlerWallMs: finite(result.handlerWallMs, "handlerWallMs"),
|
|
149
|
+
transactionWallMs: result.transactionWallMs === null ? null : finite(result.transactionWallMs, "transactionWallMs"),
|
|
150
|
+
requestBytes: finite(result.requestBytes, "requestBytes"),
|
|
151
|
+
recordBytes: result.recordBytes === null ? null : finite(result.recordBytes, "recordBytes"),
|
|
152
|
+
alarmStateBefore: result.alarmStateBefore === null ? null : finite(result.alarmStateBefore, "alarmStateBefore"),
|
|
153
|
+
alarmDueAt: result.alarmDueAt === null ? null : finite(result.alarmDueAt, "alarmDueAt"),
|
|
154
|
+
setAlarmCalls: finite(result.setAlarmCalls, "setAlarmCalls"),
|
|
155
|
+
...(isG42ProbeLogicalKey(result.logicalKey) ? { logicalKey: result.logicalKey } : {}),
|
|
156
|
+
...(typeof result.expectedWarmupKeyPresent === "boolean" ? { expectedWarmupKeyPresent: result.expectedWarmupKeyPresent } : {}),
|
|
157
|
+
...(typeof result.keyPresent === "boolean" ? { keyPresent: result.keyPresent } : {}),
|
|
158
|
+
...(maybeInventory === undefined ? {} : { inventory: Object.freeze([...maybeInventory]) }),
|
|
159
|
+
...(typeof result.productionJournalPresent === "boolean" ? { productionJournalPresent: result.productionJournalPresent } : {}),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async function call(stub, path, body, acceptedStatuses = [200, 201, 202, 204]) {
|
|
163
|
+
const response = await stub.fetch(new Request(`https://g42-probe.internal${path}`, {
|
|
164
|
+
method: "POST",
|
|
165
|
+
headers: { "content-type": "application/json" },
|
|
166
|
+
body: JSON.stringify(body),
|
|
167
|
+
}));
|
|
168
|
+
const parsed = await responseBody(response);
|
|
169
|
+
if (!acceptedStatuses.includes(response.status)) {
|
|
170
|
+
const code = typeof parsed.code === "string" ? parsed.code : "unknown";
|
|
171
|
+
throw new Error(`G42 Journal probe ${path} failed HTTP ${response.status} (${code})`);
|
|
172
|
+
}
|
|
173
|
+
return operation(parsed, response.status);
|
|
174
|
+
}
|
|
175
|
+
function internalBody(trial, action, extra = {}) {
|
|
176
|
+
return {
|
|
177
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
178
|
+
action,
|
|
179
|
+
trialId: trial.trialId,
|
|
180
|
+
// Fixed-width cell values keep the measured write shape identical across
|
|
181
|
+
// A/B/C/D; only its scheduled value changes.
|
|
182
|
+
cell: trial.cell,
|
|
183
|
+
logicalKey: trial.logicalKey,
|
|
184
|
+
...extra,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function stubFor(namespace, serviceId, physicalIdentity) {
|
|
188
|
+
if (!isG42ProbeIdentity(physicalIdentity))
|
|
189
|
+
throw new Error("G42 Journal probe attempted an unreserved physical identity");
|
|
190
|
+
return namespace.get(scopeIdFor(namespace, { serviceId, doClass: "journal", identity: physicalIdentity }));
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Runs one scheduled logical trial. The caller starts its timer around only
|
|
194
|
+
* the measured write; warm-up/mediator/cleanup are deliberately separate.
|
|
195
|
+
*/
|
|
196
|
+
export async function runG42JournalProbeTrial(namespace, serviceId, trial, callerColo = null) {
|
|
197
|
+
const stub = stubFor(namespace, serviceId, trial.physicalIdentity);
|
|
198
|
+
let preceding;
|
|
199
|
+
let mediatorCompletedBeforeMeasurement = null;
|
|
200
|
+
if (trial.cell === "D") {
|
|
201
|
+
preceding = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/write`, internalBody(trial, "write", {
|
|
202
|
+
logicalKey: trial.warmupLogicalKey,
|
|
203
|
+
recordKind: "warmup",
|
|
204
|
+
payload: trial.payload,
|
|
205
|
+
alarmMode: "off",
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
else if (trial.mediator === "handler-ping") {
|
|
209
|
+
preceding = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/ping`, internalBody(trial, "ping"));
|
|
210
|
+
mediatorCompletedBeforeMeasurement = preceding.status >= 200 && preceding.status < 300;
|
|
211
|
+
}
|
|
212
|
+
else if (trial.mediator === "state-404") {
|
|
213
|
+
preceding = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/state`, internalBody(trial, "state", {
|
|
214
|
+
logicalKey: trial.logicalKey,
|
|
215
|
+
}), [404]);
|
|
216
|
+
mediatorCompletedBeforeMeasurement = preceding.status === 404 && preceding.keyPresent === false;
|
|
217
|
+
}
|
|
218
|
+
const startedAtMs = Date.now();
|
|
219
|
+
const measured = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/write`, internalBody(trial, "write", {
|
|
220
|
+
recordKind: "measure",
|
|
221
|
+
payload: trial.payload,
|
|
222
|
+
alarmMode: trial.alarmMode,
|
|
223
|
+
}));
|
|
224
|
+
const callerWallMs = Math.max(0, Date.now() - startedAtMs);
|
|
225
|
+
const sharedActivationId = preceding === undefined
|
|
226
|
+
? null
|
|
227
|
+
: preceding.activation.activationId === measured.activation.activationId;
|
|
228
|
+
const alarmStateEqualized = trial.alarmMode === "on"
|
|
229
|
+
? measured.alarmStateBefore === null
|
|
230
|
+
: measured.alarmStateBefore === null && measured.alarmDueAt === null;
|
|
231
|
+
const alarmSetExactlyOnce = trial.alarmMode === "on"
|
|
232
|
+
? measured.setAlarmCalls === 1 && measured.alarmDueAt !== null
|
|
233
|
+
: measured.setAlarmCalls === 0 && measured.alarmDueAt === null;
|
|
234
|
+
const distinctLogicalKey = trial.cell === "D" && preceding !== undefined
|
|
235
|
+
? preceding.logicalKey === trial.warmupLogicalKey
|
|
236
|
+
&& measured.logicalKey === trial.logicalKey
|
|
237
|
+
&& preceding.logicalKey !== measured.logicalKey
|
|
238
|
+
: null;
|
|
239
|
+
return Object.freeze({
|
|
240
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
241
|
+
trialId: trial.trialId,
|
|
242
|
+
blockId: trial.blockId,
|
|
243
|
+
cell: trial.cell,
|
|
244
|
+
physicalIdentity: trial.physicalIdentity,
|
|
245
|
+
logicalKey: trial.logicalKey,
|
|
246
|
+
warmupLogicalKey: trial.warmupLogicalKey,
|
|
247
|
+
callerWallMs,
|
|
248
|
+
callerColo,
|
|
249
|
+
measured,
|
|
250
|
+
...(preceding === undefined ? {} : { preceding }),
|
|
251
|
+
treatmentCompliance: Object.freeze({
|
|
252
|
+
measuredActivationFirst: measured.activation.activationFirst,
|
|
253
|
+
sharedActivationId,
|
|
254
|
+
mediatorCompletedBeforeMeasurement,
|
|
255
|
+
distinctLogicalKey,
|
|
256
|
+
alarmStateEqualized,
|
|
257
|
+
alarmSetExactlyOnce,
|
|
258
|
+
}),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Starts the D-only precondition for an idle-regime trial. The caller must
|
|
263
|
+
* wait outside the Worker, then invoke `measureG42JournalProbeTrial`; this
|
|
264
|
+
* preserves a real opportunity for Durable Object activation continuity to
|
|
265
|
+
* change during the requested idle interval.
|
|
266
|
+
*/
|
|
267
|
+
export async function prepareG42JournalProbeTrial(namespace, serviceId, request) {
|
|
268
|
+
const stub = stubFor(namespace, serviceId, request.physicalIdentity);
|
|
269
|
+
const preparation = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/write`, {
|
|
270
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
271
|
+
action: "write",
|
|
272
|
+
trialId: request.trialId,
|
|
273
|
+
cell: "D",
|
|
274
|
+
logicalKey: request.warmupLogicalKey,
|
|
275
|
+
recordKind: "warmup",
|
|
276
|
+
payload: request.payload,
|
|
277
|
+
alarmMode: "off",
|
|
278
|
+
});
|
|
279
|
+
if (preparation.logicalKey !== request.warmupLogicalKey || preparation.setAlarmCalls !== 0 || preparation.alarmDueAt !== null) {
|
|
280
|
+
throw new Error("G42 Journal probe D preparation did not create the alarm-free scheduled warmup record");
|
|
281
|
+
}
|
|
282
|
+
return Object.freeze({
|
|
283
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
284
|
+
trialId: request.trialId,
|
|
285
|
+
blockId: request.blockId,
|
|
286
|
+
cell: "D",
|
|
287
|
+
physicalIdentity: request.physicalIdentity,
|
|
288
|
+
logicalKey: request.logicalKey,
|
|
289
|
+
warmupLogicalKey: request.warmupLogicalKey,
|
|
290
|
+
preparation,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
/** Completes the D-only post-idle measurement without re-running warm-up. */
|
|
294
|
+
export async function measureG42JournalProbeTrial(namespace, serviceId, request, callerColo = null) {
|
|
295
|
+
const stub = stubFor(namespace, serviceId, request.physicalIdentity);
|
|
296
|
+
const startedAtMs = Date.now();
|
|
297
|
+
const measured = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/write`, {
|
|
298
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
299
|
+
action: "write",
|
|
300
|
+
trialId: request.trialId,
|
|
301
|
+
cell: "D",
|
|
302
|
+
logicalKey: request.logicalKey,
|
|
303
|
+
recordKind: "measure",
|
|
304
|
+
payload: request.payload,
|
|
305
|
+
alarmMode: request.alarmMode,
|
|
306
|
+
});
|
|
307
|
+
const callerWallMs = Math.max(0, Date.now() - startedAtMs);
|
|
308
|
+
const alarmStateEqualized = request.alarmMode === "on"
|
|
309
|
+
? measured.alarmStateBefore === null
|
|
310
|
+
: measured.alarmStateBefore === null && measured.alarmDueAt === null;
|
|
311
|
+
const alarmSetExactlyOnce = request.alarmMode === "on"
|
|
312
|
+
? measured.setAlarmCalls === 1 && measured.alarmDueAt !== null
|
|
313
|
+
: measured.setAlarmCalls === 0 && measured.alarmDueAt === null;
|
|
314
|
+
if (measured.logicalKey !== request.logicalKey || measured.expectedWarmupKeyPresent !== true) {
|
|
315
|
+
throw new Error("G42 Journal probe D measurement did not prove the scheduled warmup record remained present");
|
|
316
|
+
}
|
|
317
|
+
return Object.freeze({
|
|
318
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
319
|
+
trialId: request.trialId,
|
|
320
|
+
blockId: request.blockId,
|
|
321
|
+
cell: "D",
|
|
322
|
+
physicalIdentity: request.physicalIdentity,
|
|
323
|
+
logicalKey: request.logicalKey,
|
|
324
|
+
warmupLogicalKey: request.warmupLogicalKey,
|
|
325
|
+
callerWallMs,
|
|
326
|
+
callerColo,
|
|
327
|
+
measured,
|
|
328
|
+
treatmentCompliance: Object.freeze({
|
|
329
|
+
measuredActivationFirst: measured.activation.activationFirst,
|
|
330
|
+
alarmStateEqualized,
|
|
331
|
+
alarmSetExactlyOnce,
|
|
332
|
+
}),
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
/** Cleanup is intentionally timed outside a trial's measured write. */
|
|
336
|
+
export async function cleanupG42JournalProbeTrial(namespace, serviceId, request) {
|
|
337
|
+
const stub = stubFor(namespace, serviceId, request.physicalIdentity);
|
|
338
|
+
const body = {
|
|
339
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
340
|
+
action: "cleanup",
|
|
341
|
+
trialId: request.trialId,
|
|
342
|
+
};
|
|
343
|
+
const startedAtMs = Date.now();
|
|
344
|
+
const first = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/cleanup`, body);
|
|
345
|
+
const repeat = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/cleanup`, body);
|
|
346
|
+
const inventory = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/inventory`, {
|
|
347
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
348
|
+
action: "inventory",
|
|
349
|
+
trialId: request.trialId,
|
|
350
|
+
});
|
|
351
|
+
const inventoryEmpty = inventory.inventory?.length === 0 && inventory.alarmDueAt === null && inventory.productionJournalPresent === false;
|
|
352
|
+
return Object.freeze({
|
|
353
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
354
|
+
trialId: request.trialId,
|
|
355
|
+
physicalIdentity: request.physicalIdentity,
|
|
356
|
+
cleanupWallMs: Math.max(0, Date.now() - startedAtMs),
|
|
357
|
+
first,
|
|
358
|
+
repeat,
|
|
359
|
+
inventoryEmpty,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
export async function inventoryG42JournalProbeTrial(namespace, serviceId, request) {
|
|
363
|
+
const stub = stubFor(namespace, serviceId, request.physicalIdentity);
|
|
364
|
+
const inventory = await call(stub, `${G42_JOURNAL_PROBE_INTERNAL_PREFIX}/inventory`, {
|
|
365
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
366
|
+
action: "inventory",
|
|
367
|
+
trialId: request.trialId,
|
|
368
|
+
});
|
|
369
|
+
return Object.freeze({
|
|
370
|
+
schema: G42_JOURNAL_PROBE_SCHEMA,
|
|
371
|
+
trialId: request.trialId,
|
|
372
|
+
physicalIdentity: request.physicalIdentity,
|
|
373
|
+
inventory,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export declare const JOURNAL_STATES: readonly ["ADMITTED", "RESERVED", "ALLOCATED", "WRITING", "SEALING", "COMPLETE", "REFUSED", "FAILED", "PARTIAL", "ABANDONED"];
|
|
2
|
+
export type JournalState = (typeof JOURNAL_STATES)[number];
|
|
3
|
+
export declare const TERMINAL_JOURNAL_STATES: Set<"FAILED" | "ADMITTED" | "RESERVED" | "ALLOCATED" | "WRITING" | "SEALING" | "COMPLETE" | "REFUSED" | "PARTIAL" | "ABANDONED">;
|
|
4
|
+
export type JournalTerminalState = Extract<JournalState, "COMPLETE" | "REFUSED" | "FAILED" | "PARTIAL" | "ABANDONED">;
|
|
5
|
+
export interface JournalCandidate {
|
|
6
|
+
eventId: string;
|
|
7
|
+
payload: string;
|
|
8
|
+
eventType: string;
|
|
9
|
+
timestamp: string;
|
|
10
|
+
tags: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface ConsistencyTag {
|
|
13
|
+
tag: string;
|
|
14
|
+
lastSortableUniqueId: string;
|
|
15
|
+
}
|
|
16
|
+
export interface AlarmSchedule {
|
|
17
|
+
attempt: number;
|
|
18
|
+
dueAt: number;
|
|
19
|
+
delayMs: number;
|
|
20
|
+
/** Durable generation for the next scheduled fire, not a retry counter. */
|
|
21
|
+
scheduledGenerationId?: string;
|
|
22
|
+
}
|
|
23
|
+
export type ReconciliationFailureCause = "reservation-conflict" | "allocator-failure" | "write-failure" | "reservation-timeout" | "guard-rejection";
|
|
24
|
+
/**
|
|
25
|
+
* Persisted fault points used by the commit-worker recovery tests. They are
|
|
26
|
+
* consumed one at a time, so an at-least-once alarm can resume safely.
|
|
27
|
+
*/
|
|
28
|
+
export declare const ALARM_FAULT_POINTS: readonly ["handler-entry", "after-rearm-before-seal", "after-partial-seal", "after-full-seal-before-requery", "after-fence-before-cancel", "before-outcome-cas"];
|
|
29
|
+
export type AlarmFaultPoint = (typeof ALARM_FAULT_POINTS)[number];
|
|
30
|
+
/** Information the Journal needs to recover an externally visible commit. */
|
|
31
|
+
export interface CommitAttemptContext {
|
|
32
|
+
attemptId: string;
|
|
33
|
+
serviceId: string;
|
|
34
|
+
allocatorLineageId?: string;
|
|
35
|
+
/** Private commit.test hook that makes partial-write fence installation unavailable. */
|
|
36
|
+
testFenceNotDurable?: boolean;
|
|
37
|
+
/** Private commit.test hook that interrupts immediately after one durable fence install. */
|
|
38
|
+
testFenceInstallFaultOnce?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface RequeriedRecord {
|
|
41
|
+
eventId: string;
|
|
42
|
+
payload: string;
|
|
43
|
+
present: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface ReconciliationInput {
|
|
46
|
+
allocatorVector?: string[];
|
|
47
|
+
records: RequeriedRecord[];
|
|
48
|
+
failureCause: ReconciliationFailureCause;
|
|
49
|
+
/** Tags for which at least one requested candidate is still absent. */
|
|
50
|
+
missingTags?: string[];
|
|
51
|
+
}
|
|
52
|
+
export interface TakeoverProgress {
|
|
53
|
+
active: true;
|
|
54
|
+
sealedTags: string[];
|
|
55
|
+
/** Missing tags whose partial-write fence is durably installed. */
|
|
56
|
+
fencedTags: string[];
|
|
57
|
+
}
|
|
58
|
+
export interface TerminalResponse {
|
|
59
|
+
outcome: JournalTerminalState;
|
|
60
|
+
ownerEpoch: number;
|
|
61
|
+
stateVersion: number;
|
|
62
|
+
reason: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* SDT-G6 repair progress is an observation log only. It is never consulted
|
|
66
|
+
* to authorize a Tag mutation, clear a fence, or change the original outcome.
|
|
67
|
+
*/
|
|
68
|
+
export type RepairObservationPhase = "PREPARED" | "VERIFIED" | "CLEARED";
|
|
69
|
+
export interface RepairObservation {
|
|
70
|
+
owner: string;
|
|
71
|
+
epoch: number;
|
|
72
|
+
tag: string;
|
|
73
|
+
attemptId: string;
|
|
74
|
+
eventId: string;
|
|
75
|
+
suid: string;
|
|
76
|
+
phase: RepairObservationPhase;
|
|
77
|
+
branch?: "ROLLED_FORWARD" | "EXCLUDED_AUDITED" | "FAILED_CLOSED";
|
|
78
|
+
observedAt: string;
|
|
79
|
+
}
|
|
80
|
+
/** Durable classification recorded before the reservation cancel barrier. */
|
|
81
|
+
export interface ReservationFailure {
|
|
82
|
+
outcome: Extract<JournalTerminalState, "REFUSED" | "FAILED">;
|
|
83
|
+
reason: string;
|
|
84
|
+
failureCause: ReconciliationFailureCause;
|
|
85
|
+
}
|
|
86
|
+
export interface JournalRecord {
|
|
87
|
+
schemaVersion: 1;
|
|
88
|
+
candidates: JournalCandidate[];
|
|
89
|
+
consistencyTags: ConsistencyTag[];
|
|
90
|
+
allTags: string[];
|
|
91
|
+
/** Present only for the V1 commit worker; legacy Journal controls omit it. */
|
|
92
|
+
commitContext?: CommitAttemptContext;
|
|
93
|
+
ownerEpoch: number;
|
|
94
|
+
state: JournalState;
|
|
95
|
+
version: number;
|
|
96
|
+
alarm: AlarmSchedule | null;
|
|
97
|
+
/** Generation that fired the current/most recent reconciliation handler. */
|
|
98
|
+
firedGenerationId?: string;
|
|
99
|
+
reconciliation: ReconciliationInput | null;
|
|
100
|
+
reservationFailure: ReservationFailure | null;
|
|
101
|
+
takeover: TakeoverProgress | null;
|
|
102
|
+
faultsRemaining: number;
|
|
103
|
+
alarmFaults: AlarmFaultPoint[];
|
|
104
|
+
terminalResponse: TerminalResponse | null;
|
|
105
|
+
repairObservations: RepairObservation[];
|
|
106
|
+
createdAt: string;
|
|
107
|
+
updatedAt: string;
|
|
108
|
+
}
|
|
109
|
+
export interface CasExpectation {
|
|
110
|
+
expectedState: JournalState;
|
|
111
|
+
expectedVersion: number;
|
|
112
|
+
expectedOwnerEpoch: number;
|
|
113
|
+
}
|
|
114
|
+
export declare function isTerminalState(state: JournalState): state is JournalTerminalState;
|
|
115
|
+
export declare function isJournalState(value: unknown): value is JournalState;
|
|
116
|
+
export declare function isAllowedTransition(from: JournalState, to: JournalState): boolean;
|