@tiangong-lca/cli 0.1.0 → 0.1.2
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/README.md +58 -6
- package/dist/src/batch.d.ts +6 -0
- package/dist/src/batch.js +6 -0
- package/dist/src/batch.js.map +1 -0
- package/dist/src/cli.d.ts +161 -0
- package/dist/src/cli.js +130 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/command-spec.d.ts +90 -0
- package/dist/src/command-spec.js +400 -0
- package/dist/src/command-spec.js.map +1 -0
- package/dist/src/lib/artifacts.d.ts +9 -0
- package/dist/src/lib/auth-identity-receipt.d.ts +160 -0
- package/dist/src/lib/auth-identity-receipt.js +634 -0
- package/dist/src/lib/auth-identity-receipt.js.map +1 -0
- package/dist/src/lib/batch/attempt-recovery.d.ts +12 -0
- package/dist/src/lib/batch/attempt-recovery.js +183 -0
- package/dist/src/lib/batch/attempt-recovery.js.map +1 -0
- package/dist/src/lib/batch/canonical-contracts.d.ts +21 -0
- package/dist/src/lib/batch/canonical-contracts.js +180 -0
- package/dist/src/lib/batch/canonical-contracts.js.map +1 -0
- package/dist/src/lib/batch/engine.d.ts +2 -0
- package/dist/src/lib/batch/engine.js +294 -0
- package/dist/src/lib/batch/engine.js.map +1 -0
- package/dist/src/lib/batch/errors.d.ts +51 -0
- package/dist/src/lib/batch/errors.js +81 -0
- package/dist/src/lib/batch/errors.js.map +1 -0
- package/dist/src/lib/batch/item-projection.d.ts +10 -0
- package/dist/src/lib/batch/item-projection.js +118 -0
- package/dist/src/lib/batch/item-projection.js.map +1 -0
- package/dist/src/lib/batch/run-lock.d.ts +4 -0
- package/dist/src/lib/batch/run-lock.js +155 -0
- package/dist/src/lib/batch/run-lock.js.map +1 -0
- package/dist/src/lib/batch/scheduler-runtime.d.ts +29 -0
- package/dist/src/lib/batch/scheduler-runtime.js +114 -0
- package/dist/src/lib/batch/scheduler-runtime.js.map +1 -0
- package/dist/src/lib/batch/types.d.ts +183 -0
- package/dist/src/lib/batch/types.js +2 -0
- package/dist/src/lib/batch/types.js.map +1 -0
- package/dist/src/lib/dataset-author.d.ts +39 -0
- package/dist/src/lib/dataset-bilingual.d.ts +165 -0
- package/dist/src/lib/dataset-classification.d.ts +279 -0
- package/dist/src/lib/dataset-command.d.ts +53 -0
- package/dist/src/lib/dataset-contract.d.ts +82 -0
- package/dist/src/lib/dataset-curation-queue.d.ts +219 -0
- package/dist/src/lib/dataset-evidence-search.d.ts +134 -0
- package/dist/src/lib/dataset-import-lca.d.ts +136 -0
- package/dist/src/lib/dataset-local.d.ts +23 -0
- package/dist/src/lib/dataset-maintenance-alias-request.d.ts +12 -0
- package/dist/src/lib/dataset-maintenance-alias-rewrite.d.ts +111 -0
- package/dist/src/lib/dataset-maintenance-apply.d.ts +458 -0
- package/dist/src/lib/dataset-maintenance-clear-account.d.ts +161 -0
- package/dist/src/lib/dataset-maintenance-contract.d.ts +317 -0
- package/dist/src/lib/dataset-maintenance-derivatives.d.ts +48 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-approval-claim.d.ts +50 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-capture.d.ts +110 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-command.d.ts +15 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-contract.d.ts +387 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-execution-contract.d.ts +456 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-freeze.d.ts +99 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-plan.d.ts +85 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-recovery.d.ts +268 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-run.d.ts +123 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-seal.d.ts +50 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-verify.d.ts +85 -0
- package/dist/src/lib/dataset-maintenance-flow-identity-wire.d.ts +32 -0
- package/dist/src/lib/dataset-maintenance-pagination.d.ts +58 -0
- package/dist/src/lib/dataset-maintenance-plan.d.ts +40 -0
- package/dist/src/lib/dataset-maintenance-protected-artifacts.d.ts +16 -0
- package/dist/src/lib/dataset-maintenance-protected-before.d.ts +71 -0
- package/dist/src/lib/dataset-maintenance-protected-contract.d.ts +385 -0
- package/dist/src/lib/dataset-maintenance-protected-freeze.d.ts +148 -0
- package/dist/src/lib/dataset-maintenance-protected-preparation.d.ts +76 -0
- package/dist/src/lib/dataset-maintenance-protected-run.d.ts +252 -0
- package/dist/src/lib/dataset-maintenance-protected-seal.d.ts +93 -0
- package/dist/src/lib/dataset-maintenance-protected-toolchain.d.ts +31 -0
- package/dist/src/lib/dataset-maintenance-protected-verify.d.ts +99 -0
- package/dist/src/lib/dataset-maintenance-remote.d.ts +212 -0
- package/dist/src/lib/dataset-maintenance-support-validation.d.ts +18 -0
- package/dist/src/lib/dataset-maintenance-verify.d.ts +102 -0
- package/dist/src/lib/dataset-patch.d.ts +193 -0
- package/dist/src/lib/dataset-references-rewrite.d.ts +77 -0
- package/dist/src/lib/dataset-remote-refresh.d.ts +64 -0
- package/dist/src/lib/dataset-remote-verify.d.ts +168 -0
- package/dist/src/lib/dataset-save-draft-run.d.ts +283 -0
- package/dist/src/lib/dataset-save-draft-run.js +54 -21
- package/dist/src/lib/dataset-save-draft-run.js.map +1 -1
- package/dist/src/lib/dataset-source-upload-attachments.d.ts +109 -0
- package/dist/src/lib/dataset-validate.d.ts +62 -0
- package/dist/src/lib/dotenv.d.ts +6 -0
- package/dist/src/lib/env.d.ts +59 -0
- package/dist/src/lib/errors.d.ts +18 -0
- package/dist/src/lib/flow-build-alias-map.d.ts +80 -0
- package/dist/src/lib/flow-fetch-rows.d.ts +70 -0
- package/dist/src/lib/flow-get.d.ts +35 -0
- package/dist/src/lib/flow-governance.d.ts +22 -0
- package/dist/src/lib/flow-list.d.ts +57 -0
- package/dist/src/lib/flow-materialize-decisions.d.ts +67 -0
- package/dist/src/lib/flow-payload-validation.d.ts +32 -0
- package/dist/src/lib/flow-publish-reviewed-data.d.ts +262 -0
- package/dist/src/lib/flow-publish-version.d.ts +193 -0
- package/dist/src/lib/flow-qa.d.ts +289 -0
- package/dist/src/lib/flow-read.d.ts +70 -0
- package/dist/src/lib/flow-regen-product.d.ts +431 -0
- package/dist/src/lib/flow-remediate.d.ts +133 -0
- package/dist/src/lib/http.d.ts +25 -0
- package/dist/src/lib/http.js.map +1 -1
- package/dist/src/lib/identity-preflight.d.ts +235 -0
- package/dist/src/lib/io.d.ts +2 -0
- package/dist/src/lib/kb-search.d.ts +23 -0
- package/dist/src/lib/lca-release.d.ts +105 -0
- package/dist/src/lib/lifecyclemodel-auto-build.d.ts +223 -0
- package/dist/src/lib/lifecyclemodel-bundle-save.d.ts +71 -0
- package/dist/src/lib/lifecyclemodel-graph.d.ts +91 -0
- package/dist/src/lib/lifecyclemodel-orchestrate.d.ts +349 -0
- package/dist/src/lib/lifecyclemodel-publish-build.d.ts +77 -0
- package/dist/src/lib/lifecyclemodel-publish-resulting-process.d.ts +27 -0
- package/dist/src/lib/lifecyclemodel-qa.d.ts +206 -0
- package/dist/src/lib/lifecyclemodel-resulting-process.d.ts +204 -0
- package/dist/src/lib/lifecyclemodel-save-draft-run.d.ts +96 -0
- package/dist/src/lib/lifecyclemodel-validate-build.d.ts +78 -0
- package/dist/src/lib/llm.d.ts +51 -0
- package/dist/src/lib/package-version.d.ts +3 -0
- package/dist/src/lib/process-auto-build.d.ts +168 -0
- package/dist/src/lib/process-batch-build.d.ts +97 -0
- package/dist/src/lib/process-dedup-review.d.ts +253 -0
- package/dist/src/lib/process-flow-build-plan.d.ts +108 -0
- package/dist/src/lib/process-get.d.ts +26 -0
- package/dist/src/lib/process-list.d.ts +87 -0
- package/dist/src/lib/process-payload-validation.d.ts +27 -0
- package/dist/src/lib/process-publish-build.d.ts +148 -0
- package/dist/src/lib/process-qa.d.ts +243 -0
- package/dist/src/lib/process-refresh-references.d.ts +179 -0
- package/dist/src/lib/process-required-fields.d.ts +142 -0
- package/dist/src/lib/process-resume-build.d.ts +77 -0
- package/dist/src/lib/process-save-draft-run.d.ts +72 -0
- package/dist/src/lib/process-save-draft.d.ts +53 -0
- package/dist/src/lib/process-scope-statistics.d.ts +210 -0
- package/dist/src/lib/process-verify-rows.d.ts +71 -0
- package/dist/src/lib/publish.d.ts +219 -0
- package/dist/src/lib/remote.d.ts +19 -0
- package/dist/src/lib/run.d.ts +60 -0
- package/dist/src/lib/runtime-rulesets.d.ts +29 -0
- package/dist/src/lib/state-lock.d.ts +25 -0
- package/dist/src/lib/state-lock.js +23 -1
- package/dist/src/lib/state-lock.js.map +1 -1
- package/dist/src/lib/supabase-client.d.ts +47 -0
- package/dist/src/lib/supabase-data-api-contract.d.ts +304 -0
- package/dist/src/lib/supabase-data-api-replay.d.ts +74 -0
- package/dist/src/lib/supabase-json-ordered-write.d.ts +65 -0
- package/dist/src/lib/supabase-rest.d.ts +26 -0
- package/dist/src/lib/supabase-session.d.ts +115 -0
- package/dist/src/lib/tidas-languages.d.ts +3 -0
- package/dist/src/lib/tidas-sdk-package-validator.d.ts +111 -0
- package/dist/src/lib/tidas-sdk-validation.d.ts +42 -0
- package/dist/src/lib/unstructured.d.ts +32 -0
- package/dist/src/lib/user-api-key.d.ts +16 -0
- package/dist/src/lib/validation.d.ts +82 -0
- package/dist/src/main.d.ts +4 -0
- package/package.json +17 -3
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { executeClaim } from './attempt-recovery.js';
|
|
2
|
+
import { assertUniqueItemIds, batchItemContractsMatch, parseBatchContract, parseItemIdentity, } from './canonical-contracts.js';
|
|
3
|
+
import { identityDriftFailure, itemIdentityMatches, projectBatchItemContract, projectExclusiveKey, projectionDriftFailure, resourceDriftFailure, resumeContractFailure, validateBatchOptions, validateResume, } from './item-projection.js';
|
|
4
|
+
import { MinReadyIndexHeap, createAsyncLock, createEventEmitter, defaultBatchSleep, freezeResult, inputOrderedResults, readClock, } from './scheduler-runtime.js';
|
|
5
|
+
export async function runBoundedBatch(options) {
|
|
6
|
+
const contract = parseBatchContract(options.contract);
|
|
7
|
+
validateBatchOptions(options);
|
|
8
|
+
const itemIds = options.items.map((item, index) => parseItemIdentity(options.getItemIdentity(item, index), index));
|
|
9
|
+
assertUniqueItemIds(itemIds);
|
|
10
|
+
const preflightItemContracts = options.items.map((item, index) => projectBatchItemContract(options, item, index, itemIds[index]));
|
|
11
|
+
const preflightExclusiveKeys = options.items.map((item, index) => projectExclusiveKey(options, item, index, preflightItemContracts[index]));
|
|
12
|
+
const resumeItems = validateResume(options.resume, contract, itemIds);
|
|
13
|
+
const clock = options.clock ?? { now: Date.now };
|
|
14
|
+
const sleep = options.sleep ?? defaultBatchSleep;
|
|
15
|
+
const emitter = createEventEmitter(clock, options.eventSink);
|
|
16
|
+
const resultsByIndex = new Map();
|
|
17
|
+
const completionResults = [];
|
|
18
|
+
const claimOrder = [];
|
|
19
|
+
const pendingIndexes = [];
|
|
20
|
+
let paused = false;
|
|
21
|
+
let stopped = false;
|
|
22
|
+
let hasInfrastructureFailure = false;
|
|
23
|
+
let firstInfrastructureFailure;
|
|
24
|
+
const recordInfrastructureFailure = (error) => {
|
|
25
|
+
if (!hasInfrastructureFailure) {
|
|
26
|
+
hasInfrastructureFailure = true;
|
|
27
|
+
firstInfrastructureFailure = error;
|
|
28
|
+
}
|
|
29
|
+
stopped = true;
|
|
30
|
+
};
|
|
31
|
+
const schedulerLock = createAsyncLock(recordInfrastructureFailure);
|
|
32
|
+
await emitter.emit('batch_started');
|
|
33
|
+
const recordPreclaimResult = async (result, eventType) => {
|
|
34
|
+
resultsByIndex.set(result.input_index, result);
|
|
35
|
+
completionResults.push(result);
|
|
36
|
+
await emitter.emit(eventType, {
|
|
37
|
+
item_id: result.item_id,
|
|
38
|
+
input_index: result.input_index,
|
|
39
|
+
status: result.status,
|
|
40
|
+
});
|
|
41
|
+
if (!stopped &&
|
|
42
|
+
(await shouldStopAfter(options, result, claimOrder, resultsByIndex, completionResults))) {
|
|
43
|
+
stopped = true;
|
|
44
|
+
await emitter.emit('batch_stopped', {
|
|
45
|
+
item_id: result.item_id,
|
|
46
|
+
input_index: result.input_index,
|
|
47
|
+
status: 'stopped',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
for (const [index, item] of options.items.entries()) {
|
|
52
|
+
const resumeItem = resumeItems.get(itemIds[index]);
|
|
53
|
+
if (resumeItem?.state === 'completed') {
|
|
54
|
+
const itemContract = preflightItemContracts[index];
|
|
55
|
+
const exclusiveKey = preflightExclusiveKeys[index];
|
|
56
|
+
if (!itemIdentityMatches(options, item, index, itemContract.item_id)) {
|
|
57
|
+
const result = identityDriftFailure(item, itemContract, exclusiveKey, index, resumeItem, clock);
|
|
58
|
+
await recordPreclaimResult(result, 'item_identity_drift');
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const currentItemContract = projectBatchItemContract(options, item, index, itemIds[index]);
|
|
62
|
+
if (!batchItemContractsMatch(itemContract, currentItemContract)) {
|
|
63
|
+
const result = projectionDriftFailure(item, itemContract, exclusiveKey, index, resumeItem, clock);
|
|
64
|
+
await recordPreclaimResult(result, 'item_projection_drift');
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const currentExclusiveKey = projectExclusiveKey(options, item, index, currentItemContract);
|
|
68
|
+
if (exclusiveKey !== currentExclusiveKey) {
|
|
69
|
+
const result = resourceDriftFailure(item, itemContract, exclusiveKey, index, resumeItem, clock);
|
|
70
|
+
await recordPreclaimResult(result, 'item_resource_drift');
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!batchItemContractsMatch(itemContract, resumeItem)) {
|
|
74
|
+
const result = resumeContractFailure(item, itemContract, exclusiveKey, index, resumeItem.attempts, clock);
|
|
75
|
+
await recordPreclaimResult(result, 'item_resume_rejected');
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const resumedResult = freezeResult({
|
|
79
|
+
item,
|
|
80
|
+
item_id: itemContract.item_id,
|
|
81
|
+
item_contract: itemContract,
|
|
82
|
+
exclusive_key: exclusiveKey,
|
|
83
|
+
input_index: index,
|
|
84
|
+
attempts: resumeItem.attempts,
|
|
85
|
+
attempt_consumed: true,
|
|
86
|
+
resumed: true,
|
|
87
|
+
completed_at_ms: readClock(clock),
|
|
88
|
+
status: resumeItem.outcome,
|
|
89
|
+
value: resumeItem.value,
|
|
90
|
+
});
|
|
91
|
+
await recordPreclaimResult(resumedResult, 'item_resumed');
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
pendingIndexes.push(index);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const unclaimedIndexes = new Set(pendingIndexes);
|
|
98
|
+
const resourceKeysByIndex = new Map();
|
|
99
|
+
const resourceQueues = new Map();
|
|
100
|
+
for (const index of pendingIndexes) {
|
|
101
|
+
const exclusiveKey = preflightExclusiveKeys[index];
|
|
102
|
+
const resourceKey = exclusiveKey ?? index;
|
|
103
|
+
resourceKeysByIndex.set(index, resourceKey);
|
|
104
|
+
const queue = resourceQueues.get(resourceKey) ?? { indexes: [], cursor: 0 };
|
|
105
|
+
queue.indexes.push(index);
|
|
106
|
+
resourceQueues.set(resourceKey, queue);
|
|
107
|
+
}
|
|
108
|
+
const readyIndexes = new MinReadyIndexHeap();
|
|
109
|
+
for (const queue of resourceQueues.values())
|
|
110
|
+
readyIndexes.push(queue.indexes[0]);
|
|
111
|
+
const claimNext = async () => schedulerLock(async () => {
|
|
112
|
+
if (paused || stopped || unclaimedIndexes.size === 0)
|
|
113
|
+
return null;
|
|
114
|
+
const index = readyIndexes.pop();
|
|
115
|
+
if (index === undefined)
|
|
116
|
+
return null;
|
|
117
|
+
const resourceKey = resourceKeysByIndex.get(index);
|
|
118
|
+
const item = options.items[index];
|
|
119
|
+
const itemId = itemIds[index];
|
|
120
|
+
const itemContract = preflightItemContracts[index];
|
|
121
|
+
const exclusiveKey = preflightExclusiveKeys[index];
|
|
122
|
+
if (!itemIdentityMatches(options, item, index, itemId)) {
|
|
123
|
+
unclaimedIndexes.delete(index);
|
|
124
|
+
const result = identityDriftFailure(item, itemContract, exclusiveKey, index, resumeItems.get(itemId), clock);
|
|
125
|
+
await emitter.emit('item_identity_drift', {
|
|
126
|
+
item_id: result.item_id,
|
|
127
|
+
input_index: result.input_index,
|
|
128
|
+
status: result.status,
|
|
129
|
+
});
|
|
130
|
+
return { kind: 'rejected', resourceKey, result };
|
|
131
|
+
}
|
|
132
|
+
const currentItemContract = projectBatchItemContract(options, item, index, itemId);
|
|
133
|
+
const resumeItem = resumeItems.get(itemId);
|
|
134
|
+
if (!batchItemContractsMatch(itemContract, currentItemContract)) {
|
|
135
|
+
unclaimedIndexes.delete(index);
|
|
136
|
+
const result = projectionDriftFailure(item, itemContract, exclusiveKey, index, resumeItem, clock);
|
|
137
|
+
await emitter.emit('item_projection_drift', {
|
|
138
|
+
item_id: result.item_id,
|
|
139
|
+
input_index: result.input_index,
|
|
140
|
+
status: result.status,
|
|
141
|
+
});
|
|
142
|
+
return { kind: 'rejected', resourceKey, result };
|
|
143
|
+
}
|
|
144
|
+
const currentExclusiveKey = projectExclusiveKey(options, item, index, currentItemContract);
|
|
145
|
+
if (exclusiveKey !== currentExclusiveKey) {
|
|
146
|
+
unclaimedIndexes.delete(index);
|
|
147
|
+
const result = resourceDriftFailure(item, itemContract, exclusiveKey, index, resumeItem, clock);
|
|
148
|
+
await emitter.emit('item_resource_drift', {
|
|
149
|
+
item_id: result.item_id,
|
|
150
|
+
input_index: result.input_index,
|
|
151
|
+
status: result.status,
|
|
152
|
+
});
|
|
153
|
+
return { kind: 'rejected', resourceKey, result };
|
|
154
|
+
}
|
|
155
|
+
if (resumeItem && !batchItemContractsMatch(itemContract, resumeItem)) {
|
|
156
|
+
unclaimedIndexes.delete(index);
|
|
157
|
+
const result = resumeContractFailure(item, itemContract, exclusiveKey, index, resumeItem.attempts, clock);
|
|
158
|
+
await emitter.emit('item_resume_rejected', {
|
|
159
|
+
item_id: result.item_id,
|
|
160
|
+
input_index: result.input_index,
|
|
161
|
+
status: result.status,
|
|
162
|
+
});
|
|
163
|
+
return { kind: 'rejected', resourceKey, result };
|
|
164
|
+
}
|
|
165
|
+
if (options.shouldPauseBeforeClaim &&
|
|
166
|
+
(await options.shouldPauseBeforeClaim({
|
|
167
|
+
item,
|
|
168
|
+
item_id: itemId,
|
|
169
|
+
item_contract: itemContract,
|
|
170
|
+
exclusive_key: exclusiveKey,
|
|
171
|
+
input_index: index,
|
|
172
|
+
claimed_count: claimOrder.length,
|
|
173
|
+
results_input_order: inputOrderedResults(resultsByIndex),
|
|
174
|
+
}))) {
|
|
175
|
+
paused = true;
|
|
176
|
+
await emitter.emit('batch_paused', {
|
|
177
|
+
item_id: itemId,
|
|
178
|
+
input_index: index,
|
|
179
|
+
status: 'paused',
|
|
180
|
+
});
|
|
181
|
+
readyIndexes.push(index);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
unclaimedIndexes.delete(index);
|
|
185
|
+
claimOrder.push(itemId);
|
|
186
|
+
await emitter.emit('item_claimed', { item_id: itemId, input_index: index });
|
|
187
|
+
if (exclusiveKey !== null) {
|
|
188
|
+
await emitter.emit('item_resource_queued', {
|
|
189
|
+
item_id: itemId,
|
|
190
|
+
input_index: index,
|
|
191
|
+
exclusive_key: exclusiveKey,
|
|
192
|
+
});
|
|
193
|
+
await emitter.emit('item_resource_acquired', {
|
|
194
|
+
item_id: itemId,
|
|
195
|
+
input_index: index,
|
|
196
|
+
exclusive_key: exclusiveKey,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return { kind: 'claimed', index, itemContract, exclusiveKey, resourceKey, resumeItem };
|
|
200
|
+
});
|
|
201
|
+
const complete = async (result, exclusiveKey, resourceKey) => schedulerLock(async () => {
|
|
202
|
+
if (exclusiveKey !== null) {
|
|
203
|
+
await emitter.emit('item_resource_released', {
|
|
204
|
+
item_id: result.item_id,
|
|
205
|
+
input_index: result.input_index,
|
|
206
|
+
exclusive_key: exclusiveKey,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
resultsByIndex.set(result.input_index, result);
|
|
210
|
+
completionResults.push(result);
|
|
211
|
+
await emitter.emit('item_completed', {
|
|
212
|
+
item_id: result.item_id,
|
|
213
|
+
input_index: result.input_index,
|
|
214
|
+
status: result.status,
|
|
215
|
+
});
|
|
216
|
+
if (!stopped &&
|
|
217
|
+
(await shouldStopAfter(options, result, claimOrder, resultsByIndex, completionResults))) {
|
|
218
|
+
stopped = true;
|
|
219
|
+
await emitter.emit('batch_stopped', {
|
|
220
|
+
item_id: result.item_id,
|
|
221
|
+
input_index: result.input_index,
|
|
222
|
+
status: 'stopped',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const resourceQueue = resourceQueues.get(resourceKey);
|
|
226
|
+
resourceQueue.cursor += 1;
|
|
227
|
+
const nextIndex = resourceQueue.indexes[resourceQueue.cursor];
|
|
228
|
+
if (nextIndex !== undefined && !paused && !stopped)
|
|
229
|
+
readyIndexes.push(nextIndex);
|
|
230
|
+
});
|
|
231
|
+
const worker = async () => {
|
|
232
|
+
try {
|
|
233
|
+
while (true) {
|
|
234
|
+
const claim = await claimNext();
|
|
235
|
+
if (!claim)
|
|
236
|
+
return;
|
|
237
|
+
if (claim.kind === 'rejected') {
|
|
238
|
+
await complete(claim.result, null, claim.resourceKey);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const execute = () => executeClaim({
|
|
242
|
+
options,
|
|
243
|
+
index: claim.index,
|
|
244
|
+
itemContract: claim.itemContract,
|
|
245
|
+
exclusiveKey: claim.exclusiveKey,
|
|
246
|
+
resumeItem: claim.resumeItem,
|
|
247
|
+
clock,
|
|
248
|
+
sleep,
|
|
249
|
+
emitter,
|
|
250
|
+
});
|
|
251
|
+
const result = await execute();
|
|
252
|
+
await complete(result, claim.exclusiveKey, claim.resourceKey);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
recordInfrastructureFailure(error);
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
if (!stopped) {
|
|
261
|
+
const workerCount = Math.min(options.maxConcurrency, pendingIndexes.length);
|
|
262
|
+
await Promise.allSettled(Array.from({ length: workerCount }, worker));
|
|
263
|
+
}
|
|
264
|
+
if (hasInfrastructureFailure)
|
|
265
|
+
throw firstInfrastructureFailure;
|
|
266
|
+
const status = paused ? 'paused' : stopped ? 'stopped' : 'completed';
|
|
267
|
+
await emitter.emit('batch_completed', { status });
|
|
268
|
+
await emitter.settled();
|
|
269
|
+
const resultsInputOrder = inputOrderedResults(resultsByIndex);
|
|
270
|
+
const unclaimedItemIds = pendingIndexes
|
|
271
|
+
.filter((index) => unclaimedIndexes.has(index))
|
|
272
|
+
.map((index) => itemIds[index]);
|
|
273
|
+
return Object.freeze({
|
|
274
|
+
contract,
|
|
275
|
+
status,
|
|
276
|
+
claim_order: Object.freeze([...claimOrder]),
|
|
277
|
+
completion_order: Object.freeze(completionResults.map((result) => result.item_id)),
|
|
278
|
+
results_input_order: Object.freeze(resultsInputOrder),
|
|
279
|
+
results_completion_order: Object.freeze([...completionResults]),
|
|
280
|
+
unclaimed_item_ids: Object.freeze(unclaimedItemIds),
|
|
281
|
+
events: Object.freeze([...emitter.events]),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async function shouldStopAfter(options, lastResult, claimOrder, resultsByIndex, completionResults) {
|
|
285
|
+
if (!options.shouldStop)
|
|
286
|
+
return false;
|
|
287
|
+
return options.shouldStop({
|
|
288
|
+
last_result: lastResult,
|
|
289
|
+
claimed_count: claimOrder.length,
|
|
290
|
+
results_input_order: inputOrderedResults(resultsByIndex),
|
|
291
|
+
results_completion_order: [...completionResults],
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../../src/lib/batch/engine.ts"],"names":[],"mappings":"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","sourcesContent":["import { executeClaim } from './attempt-recovery.js';\nimport {\n assertUniqueItemIds,\n batchItemContractsMatch,\n parseBatchContract,\n parseItemIdentity,\n} from './canonical-contracts.js';\nimport {\n identityDriftFailure,\n itemIdentityMatches,\n projectBatchItemContract,\n projectExclusiveKey,\n projectionDriftFailure,\n resourceDriftFailure,\n resumeContractFailure,\n validateBatchOptions,\n validateResume,\n} from './item-projection.js';\nimport {\n MinReadyIndexHeap,\n createAsyncLock,\n createEventEmitter,\n defaultBatchSleep,\n freezeResult,\n inputOrderedResults,\n readClock,\n} from './scheduler-runtime.js';\nimport type {\n BatchItemContract,\n BatchItemFailedResult,\n BatchItemResult,\n BatchJsonValue,\n BatchResumeItem,\n BatchRunResult,\n BatchStatus,\n RunBoundedBatchOptions,\n} from './types.js';\n\nexport async function runBoundedBatch<\n TInput,\n TOutput,\n TIdentity extends BatchJsonValue,\n TExclusiveKey extends string = string,\n>(\n options: RunBoundedBatchOptions<TInput, TOutput, TIdentity, TExclusiveKey>,\n): Promise<BatchRunResult<TInput, TOutput, TIdentity>> {\n const contract = parseBatchContract<TIdentity>(options.contract);\n validateBatchOptions(options);\n const itemIds = options.items.map((item, index) =>\n parseItemIdentity(options.getItemIdentity(item, index), index),\n );\n assertUniqueItemIds(itemIds);\n const preflightItemContracts = options.items.map((item, index) =>\n projectBatchItemContract(options, item, index, itemIds[index]!),\n );\n const preflightExclusiveKeys = options.items.map((item, index) =>\n projectExclusiveKey(options, item, index, preflightItemContracts[index]!),\n );\n const resumeItems = validateResume(options.resume, contract, itemIds);\n const clock = options.clock ?? { now: Date.now };\n const sleep = options.sleep ?? defaultBatchSleep;\n const emitter = createEventEmitter(clock, options.eventSink);\n const resultsByIndex = new Map<number, BatchItemResult<TInput, TOutput>>();\n const completionResults: BatchItemResult<TInput, TOutput>[] = [];\n const claimOrder: string[] = [];\n const pendingIndexes: number[] = [];\n let paused = false;\n let stopped = false;\n let hasInfrastructureFailure = false;\n let firstInfrastructureFailure: unknown;\n const recordInfrastructureFailure = (error: unknown): void => {\n if (!hasInfrastructureFailure) {\n hasInfrastructureFailure = true;\n firstInfrastructureFailure = error;\n }\n stopped = true;\n };\n const schedulerLock = createAsyncLock(recordInfrastructureFailure);\n\n await emitter.emit('batch_started');\n\n const recordPreclaimResult = async (\n result: BatchItemResult<TInput, TOutput>,\n eventType:\n | 'item_identity_drift'\n | 'item_projection_drift'\n | 'item_resource_drift'\n | 'item_resume_rejected'\n | 'item_resumed',\n ): Promise<void> => {\n resultsByIndex.set(result.input_index, result);\n completionResults.push(result);\n await emitter.emit(eventType, {\n item_id: result.item_id,\n input_index: result.input_index,\n status: result.status,\n });\n if (\n !stopped &&\n (await shouldStopAfter(options, result, claimOrder, resultsByIndex, completionResults))\n ) {\n stopped = true;\n await emitter.emit('batch_stopped', {\n item_id: result.item_id,\n input_index: result.input_index,\n status: 'stopped',\n });\n }\n };\n\n for (const [index, item] of options.items.entries()) {\n const resumeItem = resumeItems.get(itemIds[index]!);\n if (resumeItem?.state === 'completed') {\n const itemContract = preflightItemContracts[index]!;\n const exclusiveKey = preflightExclusiveKeys[index]!;\n if (!itemIdentityMatches(options, item, index, itemContract.item_id)) {\n const result = identityDriftFailure(\n item,\n itemContract,\n exclusiveKey,\n index,\n resumeItem,\n clock,\n );\n await recordPreclaimResult(result, 'item_identity_drift');\n continue;\n }\n const currentItemContract = projectBatchItemContract(options, item, index, itemIds[index]!);\n if (!batchItemContractsMatch(itemContract, currentItemContract)) {\n const result = projectionDriftFailure(\n item,\n itemContract,\n exclusiveKey,\n index,\n resumeItem,\n clock,\n );\n await recordPreclaimResult(result, 'item_projection_drift');\n continue;\n }\n const currentExclusiveKey = projectExclusiveKey(options, item, index, currentItemContract);\n if (exclusiveKey !== currentExclusiveKey) {\n const result = resourceDriftFailure(\n item,\n itemContract,\n exclusiveKey,\n index,\n resumeItem,\n clock,\n );\n await recordPreclaimResult(result, 'item_resource_drift');\n continue;\n }\n if (!batchItemContractsMatch(itemContract, resumeItem)) {\n const result = resumeContractFailure(\n item,\n itemContract,\n exclusiveKey,\n index,\n resumeItem.attempts,\n clock,\n );\n await recordPreclaimResult(result, 'item_resume_rejected');\n continue;\n }\n const resumedResult = freezeResult({\n item,\n item_id: itemContract.item_id,\n item_contract: itemContract,\n exclusive_key: exclusiveKey,\n input_index: index,\n attempts: resumeItem.attempts,\n attempt_consumed: true,\n resumed: true,\n completed_at_ms: readClock(clock),\n status: resumeItem.outcome,\n value: resumeItem.value,\n });\n await recordPreclaimResult(resumedResult, 'item_resumed');\n } else {\n pendingIndexes.push(index);\n }\n }\n const unclaimedIndexes = new Set(pendingIndexes);\n type PendingResourceKey = number | string;\n type PendingResourceQueue = { indexes: number[]; cursor: number };\n const resourceKeysByIndex = new Map<number, PendingResourceKey>();\n const resourceQueues = new Map<PendingResourceKey, PendingResourceQueue>();\n for (const index of pendingIndexes) {\n const exclusiveKey = preflightExclusiveKeys[index]!;\n const resourceKey: PendingResourceKey = exclusiveKey ?? index;\n resourceKeysByIndex.set(index, resourceKey);\n const queue = resourceQueues.get(resourceKey) ?? { indexes: [], cursor: 0 };\n queue.indexes.push(index);\n resourceQueues.set(resourceKey, queue);\n }\n const readyIndexes = new MinReadyIndexHeap();\n for (const queue of resourceQueues.values()) readyIndexes.push(queue.indexes[0]!);\n\n type Claim =\n | {\n kind: 'claimed';\n index: number;\n itemContract: BatchItemContract;\n exclusiveKey: TExclusiveKey | null;\n resourceKey: PendingResourceKey;\n resumeItem: BatchResumeItem<TOutput> | undefined;\n }\n | {\n kind: 'rejected';\n resourceKey: PendingResourceKey;\n result: BatchItemFailedResult<TInput>;\n };\n\n const claimNext = async (): Promise<Claim | null> =>\n schedulerLock(async () => {\n if (paused || stopped || unclaimedIndexes.size === 0) return null;\n const index = readyIndexes.pop();\n if (index === undefined) return null;\n const resourceKey = resourceKeysByIndex.get(index)!;\n const item = options.items[index]!;\n const itemId = itemIds[index]!;\n const itemContract = preflightItemContracts[index]!;\n const exclusiveKey = preflightExclusiveKeys[index]!;\n if (!itemIdentityMatches(options, item, index, itemId)) {\n unclaimedIndexes.delete(index);\n const result = identityDriftFailure(\n item,\n itemContract,\n exclusiveKey,\n index,\n resumeItems.get(itemId),\n clock,\n );\n await emitter.emit('item_identity_drift', {\n item_id: result.item_id,\n input_index: result.input_index,\n status: result.status,\n });\n return { kind: 'rejected', resourceKey, result };\n }\n const currentItemContract = projectBatchItemContract(options, item, index, itemId);\n const resumeItem = resumeItems.get(itemId);\n if (!batchItemContractsMatch(itemContract, currentItemContract)) {\n unclaimedIndexes.delete(index);\n const result = projectionDriftFailure(\n item,\n itemContract,\n exclusiveKey,\n index,\n resumeItem,\n clock,\n );\n await emitter.emit('item_projection_drift', {\n item_id: result.item_id,\n input_index: result.input_index,\n status: result.status,\n });\n return { kind: 'rejected', resourceKey, result };\n }\n const currentExclusiveKey = projectExclusiveKey(options, item, index, currentItemContract);\n if (exclusiveKey !== currentExclusiveKey) {\n unclaimedIndexes.delete(index);\n const result = resourceDriftFailure(\n item,\n itemContract,\n exclusiveKey,\n index,\n resumeItem,\n clock,\n );\n await emitter.emit('item_resource_drift', {\n item_id: result.item_id,\n input_index: result.input_index,\n status: result.status,\n });\n return { kind: 'rejected', resourceKey, result };\n }\n if (resumeItem && !batchItemContractsMatch(itemContract, resumeItem)) {\n unclaimedIndexes.delete(index);\n const result = resumeContractFailure(\n item,\n itemContract,\n exclusiveKey,\n index,\n resumeItem.attempts,\n clock,\n );\n await emitter.emit('item_resume_rejected', {\n item_id: result.item_id,\n input_index: result.input_index,\n status: result.status,\n });\n return { kind: 'rejected', resourceKey, result };\n }\n if (\n options.shouldPauseBeforeClaim &&\n (await options.shouldPauseBeforeClaim({\n item,\n item_id: itemId,\n item_contract: itemContract,\n exclusive_key: exclusiveKey,\n input_index: index,\n claimed_count: claimOrder.length,\n results_input_order: inputOrderedResults(resultsByIndex),\n }))\n ) {\n paused = true;\n await emitter.emit('batch_paused', {\n item_id: itemId,\n input_index: index,\n status: 'paused',\n });\n readyIndexes.push(index);\n return null;\n }\n unclaimedIndexes.delete(index);\n claimOrder.push(itemId);\n await emitter.emit('item_claimed', { item_id: itemId, input_index: index });\n if (exclusiveKey !== null) {\n await emitter.emit('item_resource_queued', {\n item_id: itemId,\n input_index: index,\n exclusive_key: exclusiveKey,\n });\n await emitter.emit('item_resource_acquired', {\n item_id: itemId,\n input_index: index,\n exclusive_key: exclusiveKey,\n });\n }\n return { kind: 'claimed', index, itemContract, exclusiveKey, resourceKey, resumeItem };\n });\n\n const complete = async (\n result: BatchItemResult<TInput, TOutput>,\n exclusiveKey: string | null,\n resourceKey: PendingResourceKey,\n ): Promise<void> =>\n schedulerLock(async () => {\n if (exclusiveKey !== null) {\n await emitter.emit('item_resource_released', {\n item_id: result.item_id,\n input_index: result.input_index,\n exclusive_key: exclusiveKey,\n });\n }\n resultsByIndex.set(result.input_index, result);\n completionResults.push(result);\n await emitter.emit('item_completed', {\n item_id: result.item_id,\n input_index: result.input_index,\n status: result.status,\n });\n if (\n !stopped &&\n (await shouldStopAfter(options, result, claimOrder, resultsByIndex, completionResults))\n ) {\n stopped = true;\n await emitter.emit('batch_stopped', {\n item_id: result.item_id,\n input_index: result.input_index,\n status: 'stopped',\n });\n }\n const resourceQueue = resourceQueues.get(resourceKey)!;\n resourceQueue.cursor += 1;\n const nextIndex = resourceQueue.indexes[resourceQueue.cursor];\n if (nextIndex !== undefined && !paused && !stopped) readyIndexes.push(nextIndex);\n });\n\n const worker = async () => {\n try {\n while (true) {\n const claim = await claimNext();\n if (!claim) return;\n if (claim.kind === 'rejected') {\n await complete(claim.result, null, claim.resourceKey);\n continue;\n }\n const execute = () =>\n executeClaim({\n options,\n index: claim.index,\n itemContract: claim.itemContract,\n exclusiveKey: claim.exclusiveKey,\n resumeItem: claim.resumeItem,\n clock,\n sleep,\n emitter,\n });\n const result = await execute();\n await complete(result, claim.exclusiveKey, claim.resourceKey);\n }\n } catch (error) {\n recordInfrastructureFailure(error);\n throw error;\n }\n };\n\n if (!stopped) {\n const workerCount = Math.min(options.maxConcurrency, pendingIndexes.length);\n await Promise.allSettled(Array.from({ length: workerCount }, worker));\n }\n if (hasInfrastructureFailure) throw firstInfrastructureFailure;\n\n const status: BatchStatus = paused ? 'paused' : stopped ? 'stopped' : 'completed';\n await emitter.emit('batch_completed', { status });\n await emitter.settled();\n const resultsInputOrder = inputOrderedResults(resultsByIndex);\n const unclaimedItemIds = pendingIndexes\n .filter((index) => unclaimedIndexes.has(index))\n .map((index) => itemIds[index]!);\n return Object.freeze({\n contract,\n status,\n claim_order: Object.freeze([...claimOrder]),\n completion_order: Object.freeze(completionResults.map((result) => result.item_id)),\n results_input_order: Object.freeze(resultsInputOrder),\n results_completion_order: Object.freeze([...completionResults]),\n unclaimed_item_ids: Object.freeze(unclaimedItemIds),\n events: Object.freeze([...emitter.events]),\n });\n}\n\nasync function shouldStopAfter<\n TInput,\n TOutput,\n TIdentity extends BatchJsonValue,\n TExclusiveKey extends string,\n>(\n options: RunBoundedBatchOptions<TInput, TOutput, TIdentity, TExclusiveKey>,\n lastResult: BatchItemResult<TInput, TOutput>,\n claimOrder: readonly string[],\n resultsByIndex: Map<number, BatchItemResult<TInput, TOutput>>,\n completionResults: readonly BatchItemResult<TInput, TOutput>[],\n): Promise<boolean> {\n if (!options.shouldStop) return false;\n return options.shouldStop({\n last_result: lastResult,\n claimed_count: claimOrder.length,\n results_input_order: inputOrderedResults(resultsByIndex),\n results_completion_order: [...completionResults],\n });\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export declare class BatchContractError extends Error {
|
|
2
|
+
constructor(message: string, options?: ErrorOptions);
|
|
3
|
+
}
|
|
4
|
+
export declare class BatchMutationRetryError extends BatchContractError {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export declare class BatchMutationReplayError extends Error {
|
|
8
|
+
constructor(itemId: string);
|
|
9
|
+
}
|
|
10
|
+
export declare class BatchItemResumeContractError extends BatchContractError {
|
|
11
|
+
readonly itemId: string;
|
|
12
|
+
constructor(itemId: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class BatchItemProjectionDriftError extends BatchContractError {
|
|
15
|
+
readonly itemId: string;
|
|
16
|
+
constructor(itemId: string);
|
|
17
|
+
}
|
|
18
|
+
export declare class BatchItemIdentityDriftError extends BatchContractError {
|
|
19
|
+
readonly itemId: string;
|
|
20
|
+
constructor(itemId: string);
|
|
21
|
+
}
|
|
22
|
+
export declare class BatchItemResourceDriftError extends BatchContractError {
|
|
23
|
+
readonly itemId: string;
|
|
24
|
+
constructor(itemId: string);
|
|
25
|
+
}
|
|
26
|
+
export declare class BatchRunLockTimeoutError extends Error {
|
|
27
|
+
readonly code: 'BATCH_RUN_LOCK_TIMEOUT';
|
|
28
|
+
readonly runPath: string;
|
|
29
|
+
readonly lockPath: string;
|
|
30
|
+
readonly identitySha256: string;
|
|
31
|
+
readonly waitedMs: number;
|
|
32
|
+
readonly owner: Readonly<Record<string, unknown>> | null;
|
|
33
|
+
constructor(options: {
|
|
34
|
+
runPath: string;
|
|
35
|
+
lockPath: string;
|
|
36
|
+
identitySha256: string;
|
|
37
|
+
waitedMs: number;
|
|
38
|
+
owner: Readonly<Record<string, unknown>> | null;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export declare class BatchRunLockIdentityConflictError extends Error {
|
|
42
|
+
readonly code: 'BATCH_RUN_LOCK_IDENTITY_CONFLICT';
|
|
43
|
+
readonly runPath: string;
|
|
44
|
+
readonly activeIdentitySha256: string;
|
|
45
|
+
readonly requestedIdentitySha256: string;
|
|
46
|
+
constructor(options: {
|
|
47
|
+
runPath: string;
|
|
48
|
+
activeIdentitySha256: string;
|
|
49
|
+
requestedIdentitySha256: string;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export class BatchContractError extends Error {
|
|
2
|
+
constructor(message, options) {
|
|
3
|
+
super(message, options);
|
|
4
|
+
this.name = 'BatchContractError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class BatchMutationRetryError extends BatchContractError {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('Mutation batches reject automatic retry; use explicit readback recovery instead.');
|
|
10
|
+
this.name = 'BatchMutationRetryError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class BatchMutationReplayError extends Error {
|
|
14
|
+
constructor(itemId) {
|
|
15
|
+
super(`Mutation item ${itemId} has a consumed incomplete attempt and cannot be replayed without explicit readback recovery.`);
|
|
16
|
+
this.name = 'BatchMutationReplayError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class BatchItemResumeContractError extends BatchContractError {
|
|
20
|
+
itemId;
|
|
21
|
+
constructor(itemId) {
|
|
22
|
+
super(`Batch resume item ${itemId} does not match the current item identity, content SHA-256, and policy SHA-256 triple.`);
|
|
23
|
+
this.name = 'BatchItemResumeContractError';
|
|
24
|
+
this.itemId = itemId;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class BatchItemProjectionDriftError extends BatchContractError {
|
|
28
|
+
itemId;
|
|
29
|
+
constructor(itemId) {
|
|
30
|
+
super(`Batch item ${itemId} content or policy projection drifted before claim.`);
|
|
31
|
+
this.name = 'BatchItemProjectionDriftError';
|
|
32
|
+
this.itemId = itemId;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class BatchItemIdentityDriftError extends BatchContractError {
|
|
36
|
+
itemId;
|
|
37
|
+
constructor(itemId) {
|
|
38
|
+
super(`Batch item ${itemId} identity drifted before claim.`);
|
|
39
|
+
this.name = 'BatchItemIdentityDriftError';
|
|
40
|
+
this.itemId = itemId;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export class BatchItemResourceDriftError extends BatchContractError {
|
|
44
|
+
itemId;
|
|
45
|
+
constructor(itemId) {
|
|
46
|
+
super(`Batch item ${itemId} exclusive resource key drifted before claim.`);
|
|
47
|
+
this.name = 'BatchItemResourceDriftError';
|
|
48
|
+
this.itemId = itemId;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export class BatchRunLockTimeoutError extends Error {
|
|
52
|
+
code = 'BATCH_RUN_LOCK_TIMEOUT';
|
|
53
|
+
runPath;
|
|
54
|
+
lockPath;
|
|
55
|
+
identitySha256;
|
|
56
|
+
waitedMs;
|
|
57
|
+
owner;
|
|
58
|
+
constructor(options) {
|
|
59
|
+
super(`Timed out after ${options.waitedMs}ms acquiring batch run lock: ${options.lockPath}`);
|
|
60
|
+
this.name = 'BatchRunLockTimeoutError';
|
|
61
|
+
this.runPath = options.runPath;
|
|
62
|
+
this.lockPath = options.lockPath;
|
|
63
|
+
this.identitySha256 = options.identitySha256;
|
|
64
|
+
this.waitedMs = options.waitedMs;
|
|
65
|
+
this.owner = options.owner;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export class BatchRunLockIdentityConflictError extends Error {
|
|
69
|
+
code = 'BATCH_RUN_LOCK_IDENTITY_CONFLICT';
|
|
70
|
+
runPath;
|
|
71
|
+
activeIdentitySha256;
|
|
72
|
+
requestedIdentitySha256;
|
|
73
|
+
constructor(options) {
|
|
74
|
+
super(`Batch run path is already locked by another identity in this process: ${options.runPath}`);
|
|
75
|
+
this.name = 'BatchRunLockIdentityConflictError';
|
|
76
|
+
this.runPath = options.runPath;
|
|
77
|
+
this.activeIdentitySha256 = options.activeIdentitySha256;
|
|
78
|
+
this.requestedIdentitySha256 = options.requestedIdentitySha256;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/lib/batch/errors.ts"],"names":[],"mappings":"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","sourcesContent":["export class BatchContractError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'BatchContractError';\n }\n}\n\nexport class BatchMutationRetryError extends BatchContractError {\n constructor() {\n super('Mutation batches reject automatic retry; use explicit readback recovery instead.');\n this.name = 'BatchMutationRetryError';\n }\n}\n\nexport class BatchMutationReplayError extends Error {\n constructor(itemId: string) {\n super(\n `Mutation item ${itemId} has a consumed incomplete attempt and cannot be replayed without explicit readback recovery.`,\n );\n this.name = 'BatchMutationReplayError';\n }\n}\n\nexport class BatchItemResumeContractError extends BatchContractError {\n readonly itemId: string;\n\n constructor(itemId: string) {\n super(\n `Batch resume item ${itemId} does not match the current item identity, content SHA-256, and policy SHA-256 triple.`,\n );\n this.name = 'BatchItemResumeContractError';\n this.itemId = itemId;\n }\n}\n\nexport class BatchItemProjectionDriftError extends BatchContractError {\n readonly itemId: string;\n\n constructor(itemId: string) {\n super(`Batch item ${itemId} content or policy projection drifted before claim.`);\n this.name = 'BatchItemProjectionDriftError';\n this.itemId = itemId;\n }\n}\n\nexport class BatchItemIdentityDriftError extends BatchContractError {\n readonly itemId: string;\n\n constructor(itemId: string) {\n super(`Batch item ${itemId} identity drifted before claim.`);\n this.name = 'BatchItemIdentityDriftError';\n this.itemId = itemId;\n }\n}\n\nexport class BatchItemResourceDriftError extends BatchContractError {\n readonly itemId: string;\n\n constructor(itemId: string) {\n super(`Batch item ${itemId} exclusive resource key drifted before claim.`);\n this.name = 'BatchItemResourceDriftError';\n this.itemId = itemId;\n }\n}\n\nexport class BatchRunLockTimeoutError extends Error {\n readonly code = 'BATCH_RUN_LOCK_TIMEOUT' as const;\n readonly runPath: string;\n readonly lockPath: string;\n readonly identitySha256: string;\n readonly waitedMs: number;\n readonly owner: Readonly<Record<string, unknown>> | null;\n\n constructor(options: {\n runPath: string;\n lockPath: string;\n identitySha256: string;\n waitedMs: number;\n owner: Readonly<Record<string, unknown>> | null;\n }) {\n super(`Timed out after ${options.waitedMs}ms acquiring batch run lock: ${options.lockPath}`);\n this.name = 'BatchRunLockTimeoutError';\n this.runPath = options.runPath;\n this.lockPath = options.lockPath;\n this.identitySha256 = options.identitySha256;\n this.waitedMs = options.waitedMs;\n this.owner = options.owner;\n }\n}\n\nexport class BatchRunLockIdentityConflictError extends Error {\n readonly code = 'BATCH_RUN_LOCK_IDENTITY_CONFLICT' as const;\n readonly runPath: string;\n readonly activeIdentitySha256: string;\n readonly requestedIdentitySha256: string;\n\n constructor(options: {\n runPath: string;\n activeIdentitySha256: string;\n requestedIdentitySha256: string;\n }) {\n super(\n `Batch run path is already locked by another identity in this process: ${options.runPath}`,\n );\n this.name = 'BatchRunLockIdentityConflictError';\n this.runPath = options.runPath;\n this.activeIdentitySha256 = options.activeIdentitySha256;\n this.requestedIdentitySha256 = options.requestedIdentitySha256;\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BatchClock, BatchContract, BatchItemContract, BatchItemFailedResult, BatchJsonValue, BatchResumeItem, BatchResumeState, RunBoundedBatchOptions } from './types.js';
|
|
2
|
+
export declare function validateBatchOptions<TInput, TOutput, TIdentity extends BatchJsonValue, TExclusiveKey extends string>(options: RunBoundedBatchOptions<TInput, TOutput, TIdentity, TExclusiveKey>): void;
|
|
3
|
+
export declare function validateResume<TOutput, TIdentity extends BatchJsonValue>(resume: BatchResumeState<TOutput, TIdentity> | undefined, contract: BatchContract<TIdentity>, itemIds: readonly string[]): Map<string, BatchResumeItem<TOutput>>;
|
|
4
|
+
export declare function projectBatchItemContract<TInput, TOutput, TIdentity extends BatchJsonValue, TExclusiveKey extends string>(options: RunBoundedBatchOptions<TInput, TOutput, TIdentity, TExclusiveKey>, item: TInput, index: number, itemId: string): BatchItemContract;
|
|
5
|
+
export declare function itemIdentityMatches<TInput, TOutput, TIdentity extends BatchJsonValue, TExclusiveKey extends string>(options: RunBoundedBatchOptions<TInput, TOutput, TIdentity, TExclusiveKey>, item: TInput, index: number, expectedItemId: string): boolean;
|
|
6
|
+
export declare function identityDriftFailure<TInput, TOutput>(item: TInput, itemContract: BatchItemContract, exclusiveKey: string | null, index: number, resumeItem: BatchResumeItem<TOutput> | undefined, clock: BatchClock): BatchItemFailedResult<TInput>;
|
|
7
|
+
export declare function projectExclusiveKey<TInput, TOutput, TIdentity extends BatchJsonValue, TExclusiveKey extends string>(options: RunBoundedBatchOptions<TInput, TOutput, TIdentity, TExclusiveKey>, item: TInput, index: number, itemContract: BatchItemContract): TExclusiveKey | null;
|
|
8
|
+
export declare function projectionDriftFailure<TInput, TOutput>(item: TInput, itemContract: BatchItemContract, exclusiveKey: string | null, index: number, resumeItem: BatchResumeItem<TOutput> | undefined, clock: BatchClock): BatchItemFailedResult<TInput>;
|
|
9
|
+
export declare function resourceDriftFailure<TInput, TOutput>(item: TInput, itemContract: BatchItemContract, exclusiveKey: string | null, index: number, resumeItem: BatchResumeItem<TOutput> | undefined, clock: BatchClock): BatchItemFailedResult<TInput>;
|
|
10
|
+
export declare function resumeContractFailure<TInput>(item: TInput, itemContract: BatchItemContract, exclusiveKey: string | null, index: number, attempts: number, clock: BatchClock): BatchItemFailedResult<TInput>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { MAX_NODE_TIMER_DELAY_MS, assertBatchContractMatches, createBatchItemContract, isRecord, parseBatchItemContract, parseItemIdentity, } from './canonical-contracts.js';
|
|
2
|
+
import { BatchContractError, BatchItemIdentityDriftError, BatchItemProjectionDriftError, BatchItemResourceDriftError, BatchItemResumeContractError, BatchMutationRetryError, } from './errors.js';
|
|
3
|
+
import { failedResult } from './scheduler-runtime.js';
|
|
4
|
+
import { MAX_BATCH_CONCURRENCY } from './types.js';
|
|
5
|
+
export function validateBatchOptions(options) {
|
|
6
|
+
if (!Number.isSafeInteger(options.maxConcurrency) ||
|
|
7
|
+
options.maxConcurrency < 1 ||
|
|
8
|
+
options.maxConcurrency > MAX_BATCH_CONCURRENCY) {
|
|
9
|
+
throw new BatchContractError(`Batch maxConcurrency must be a safe integer from 1 to ${MAX_BATCH_CONCURRENCY}.`);
|
|
10
|
+
}
|
|
11
|
+
if (options.mode !== 'read' && options.mode !== 'mutation') {
|
|
12
|
+
throw new BatchContractError('Batch mode must be read or mutation.');
|
|
13
|
+
}
|
|
14
|
+
if (options.mode === 'mutation' && options.retry) {
|
|
15
|
+
throw new BatchMutationRetryError();
|
|
16
|
+
}
|
|
17
|
+
if (options.retry &&
|
|
18
|
+
(!Number.isSafeInteger(options.retry.maxAttempts) ||
|
|
19
|
+
options.retry.maxAttempts < 1 ||
|
|
20
|
+
!Number.isSafeInteger(options.retry.maxDelayMs) ||
|
|
21
|
+
options.retry.maxDelayMs < 0 ||
|
|
22
|
+
options.retry.maxDelayMs > MAX_NODE_TIMER_DELAY_MS)) {
|
|
23
|
+
throw new BatchContractError(`Batch retry maxAttempts must be positive and maxDelayMs must be a non-negative safe integer no greater than the maximum supported timer delay (${MAX_NODE_TIMER_DELAY_MS} ms).`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function validateResume(resume, contract, itemIds) {
|
|
27
|
+
const result = new Map();
|
|
28
|
+
if (!resume)
|
|
29
|
+
return result;
|
|
30
|
+
assertBatchContractMatches(contract, resume.contract);
|
|
31
|
+
const currentIds = new Set(itemIds);
|
|
32
|
+
for (const item of resume.items) {
|
|
33
|
+
let itemContract;
|
|
34
|
+
try {
|
|
35
|
+
itemContract = parseBatchItemContract(item);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw new BatchContractError('Batch resume contains an invalid item contract.', {
|
|
39
|
+
cause: error,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (!isRecord(item) ||
|
|
43
|
+
!currentIds.has(itemContract.item_id) ||
|
|
44
|
+
!Number.isSafeInteger(item.attempts) ||
|
|
45
|
+
Number(item.attempts) < 1 ||
|
|
46
|
+
!['attempted', 'completed'].includes(String(item.state))) {
|
|
47
|
+
throw new BatchContractError('Batch resume contains an invalid or foreign item.');
|
|
48
|
+
}
|
|
49
|
+
if (result.has(itemContract.item_id)) {
|
|
50
|
+
throw new BatchContractError('Batch resume item identities must be unique.');
|
|
51
|
+
}
|
|
52
|
+
if (item.state === 'completed') {
|
|
53
|
+
if (!['succeeded', 'recovered'].includes(String(item.outcome))) {
|
|
54
|
+
throw new BatchContractError('Completed batch resume items require a successful outcome.');
|
|
55
|
+
}
|
|
56
|
+
result.set(itemContract.item_id, Object.freeze({
|
|
57
|
+
...itemContract,
|
|
58
|
+
state: 'completed',
|
|
59
|
+
outcome: item.outcome,
|
|
60
|
+
value: item.value,
|
|
61
|
+
attempts: Number(item.attempts),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
result.set(itemContract.item_id, Object.freeze({
|
|
66
|
+
...itemContract,
|
|
67
|
+
state: 'attempted',
|
|
68
|
+
attempts: Number(item.attempts),
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
export function projectBatchItemContract(options, item, index, itemId) {
|
|
75
|
+
return createBatchItemContract({
|
|
76
|
+
item_id: itemId,
|
|
77
|
+
content: options.projectItemContent(item, index),
|
|
78
|
+
policy: options.projectItemPolicy(item, index),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function itemIdentityMatches(options, item, index, expectedItemId) {
|
|
82
|
+
try {
|
|
83
|
+
const projected = options.getItemIdentity(item, index);
|
|
84
|
+
return parseItemIdentity(projected, index) === expectedItemId;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function identityDriftFailure(item, itemContract, exclusiveKey, index, resumeItem, clock) {
|
|
91
|
+
return failedResult(item, itemContract, index, resumeItem?.attempts ?? 0, resumeItem !== undefined, resumeItem !== undefined, clock, new BatchItemIdentityDriftError(itemContract.item_id), exclusiveKey);
|
|
92
|
+
}
|
|
93
|
+
export function projectExclusiveKey(options, item, index, itemContract) {
|
|
94
|
+
if (!options.getExclusiveKey)
|
|
95
|
+
return null;
|
|
96
|
+
const value = options.getExclusiveKey({
|
|
97
|
+
item,
|
|
98
|
+
item_id: itemContract.item_id,
|
|
99
|
+
item_contract: itemContract,
|
|
100
|
+
input_index: index,
|
|
101
|
+
});
|
|
102
|
+
if (value === null || value === undefined)
|
|
103
|
+
return null;
|
|
104
|
+
if (typeof value !== 'string' || value.length === 0 || /[\0\r\n]/u.test(value)) {
|
|
105
|
+
throw new BatchContractError('Batch exclusive resource keys must be non-empty single-line strings.');
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
export function projectionDriftFailure(item, itemContract, exclusiveKey, index, resumeItem, clock) {
|
|
110
|
+
return failedResult(item, itemContract, index, resumeItem?.attempts ?? 0, resumeItem !== undefined, resumeItem !== undefined, clock, new BatchItemProjectionDriftError(itemContract.item_id), exclusiveKey);
|
|
111
|
+
}
|
|
112
|
+
export function resourceDriftFailure(item, itemContract, exclusiveKey, index, resumeItem, clock) {
|
|
113
|
+
return failedResult(item, itemContract, index, resumeItem?.attempts ?? 0, resumeItem !== undefined, resumeItem !== undefined, clock, new BatchItemResourceDriftError(itemContract.item_id), exclusiveKey);
|
|
114
|
+
}
|
|
115
|
+
export function resumeContractFailure(item, itemContract, exclusiveKey, index, attempts, clock) {
|
|
116
|
+
return failedResult(item, itemContract, index, attempts, true, true, clock, new BatchItemResumeContractError(itemContract.item_id), exclusiveKey);
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=item-projection.js.map
|