@aldus-runtime/release 0.1.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.
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Release execution, resumption, and reconciliation (architecture contract §17, §19.1).
3
+ *
4
+ * Three properties drive the design, and each rules out the obvious implementation:
5
+ *
6
+ * 1. **§17: every operation is independently idempotent and resumable.** So execution is never a
7
+ * transaction over a bundle. Each operation carries its own derived idempotency key and its
8
+ * own receipt, and a half-executed bundle is resumed by skipping what already succeeded.
9
+ *
10
+ * 2. **§17: pre-release hard gates and post-upload best-effort operations are distinguished.**
11
+ * The first failure among required operations stops the bundle — a failed media upload must
12
+ * not be followed by a visibility transition making nothing public. A best-effort failure is
13
+ * recorded and execution continues.
14
+ *
15
+ * 3. **§17: external state must be reconciled, not guessed.** A response lost after the
16
+ * destination accepted a request leaves local records saying nothing happened while something
17
+ * did. Retrying then publishes twice. So an operation whose outcome was recorded as `pending`
18
+ * is never retried; it is looked up, and if the destination cannot be queried the executor
19
+ * refuses rather than risking the duplicate.
20
+ *
21
+ * Nothing about a bundle's progress is stored beyond its receipts. State is derived on every
22
+ * call, for the reason ADR-0009 derives gate state: a stored progress flag survives a crash that
23
+ * the work it describes did not.
24
+ */
25
+ import { SCHEMA_VERSION, newEventId, newReleaseId } from "@aldus-runtime/core";
26
+ import { assertBundleValid, deriveIdempotencyKey } from "./bundle.js";
27
+ import { ReleaseErrorCodes, releaseError } from "./errors.js";
28
+ import { latestByKey } from "./ports.js";
29
+ import { permitAllAuthorizer } from "./authorization.js";
30
+ /** Where one operation stands. */
31
+ export const OPERATION_STATES = [
32
+ /** No receipt exists; it has not been attempted. */
33
+ "not_started",
34
+ /** Attempted, outcome unconfirmed. MUST be reconciled, never retried (contract §17). */
35
+ "pending",
36
+ /** Completed at the destination. */
37
+ "succeeded",
38
+ /** Attempted and rejected by the destination. */
39
+ "failed",
40
+ /** Deliberately not attempted — no authority, or nothing to do. */
41
+ "skipped",
42
+ ];
43
+ /** Executes release bundles, resumably. */
44
+ export class ReleaseExecutor {
45
+ #adapters;
46
+ #receipts;
47
+ #events;
48
+ #authorizer;
49
+ #now;
50
+ #nextReleaseId;
51
+ #nextEventId;
52
+ constructor(options) {
53
+ this.#adapters = options.adapters;
54
+ this.#receipts = options.receipts;
55
+ this.#events = options.events;
56
+ this.#authorizer = options.authorizer ?? permitAllAuthorizer();
57
+ this.#now = options.now ?? (() => new Date());
58
+ this.#nextReleaseId = options.nextReleaseId ?? newReleaseId;
59
+ this.#nextEventId = options.nextEventId ?? newEventId;
60
+ }
61
+ /**
62
+ * Derive where a bundle stands from its receipts (contract §19.1).
63
+ *
64
+ * Reads only; safe to call at any time, including on a bundle that has never run.
65
+ */
66
+ async status(bundle) {
67
+ assertBundleValid(bundle);
68
+ const latest = latestByKey(await this.#receipts.list(bundle.runId));
69
+ const operations = this.#plan(bundle).map(({ operation, criticality, idempotencyKey }) => toStatus(operation, criticality, idempotencyKey, latest.get(idempotencyKey)));
70
+ const required = operations.filter((entry) => entry.criticality === "required");
71
+ const remaining = operations
72
+ .filter((entry) => entry.state !== "succeeded" && entry.state !== "skipped")
73
+ .map((entry) => entry.operationId);
74
+ return {
75
+ bundleId: bundle.bundleId,
76
+ runId: bundle.runId,
77
+ state: bundleState(required),
78
+ operations,
79
+ remaining,
80
+ };
81
+ }
82
+ /**
83
+ * Repair the local record against the destinations (contract §17).
84
+ *
85
+ * Only operations without a terminal local receipt are looked up: an operation already
86
+ * recorded as succeeded or failed has an answer, and asking again would turn reconciliation
87
+ * into polling.
88
+ */
89
+ async reconcile(bundle, options) {
90
+ assertBundleValid(bundle);
91
+ const latest = latestByKey(await this.#receipts.list(bundle.runId));
92
+ const findings = [];
93
+ const repaired = [];
94
+ for (const { operation, idempotencyKey } of this.#plan(bundle)) {
95
+ const receipt = latest.get(idempotencyKey);
96
+ if (receipt !== undefined && receipt.status !== "pending") {
97
+ findings.push({
98
+ operationId: operation.operationId,
99
+ idempotencyKey,
100
+ action: "already_recorded",
101
+ });
102
+ continue;
103
+ }
104
+ const adapter = this.#adapters.require(operation.destination);
105
+ if (adapter.lookup === undefined) {
106
+ findings.push({
107
+ operationId: operation.operationId,
108
+ idempotencyKey,
109
+ action: "unavailable",
110
+ explanation: `Destination "${operation.destination}" cannot be queried, so whether ` +
111
+ `"${operation.operationId}" already happened is unknowable from here.`,
112
+ });
113
+ continue;
114
+ }
115
+ const request = {
116
+ operation,
117
+ idempotencyKey,
118
+ runId: bundle.runId,
119
+ };
120
+ const remote = await adapter.lookup(request);
121
+ if (!remote.exists) {
122
+ findings.push({
123
+ operationId: operation.operationId,
124
+ idempotencyKey,
125
+ action: "confirmed_absent",
126
+ });
127
+ continue;
128
+ }
129
+ const written = await this.#record(bundle, operation, idempotencyKey, {
130
+ status: "succeeded",
131
+ ...(remote.remoteId === undefined ? {} : { remoteId: remote.remoteId }),
132
+ ...(remote.remoteUrl === undefined ? {} : { remoteUrl: remote.remoteUrl }),
133
+ }, options.actor);
134
+ repaired.push(written);
135
+ findings.push({
136
+ operationId: operation.operationId,
137
+ idempotencyKey,
138
+ action: "repaired",
139
+ explanation: `The destination already holds "${operation.operationId}". The local record was ` +
140
+ "repaired rather than the operation repeated.",
141
+ });
142
+ }
143
+ return { bundleId: bundle.bundleId, runId: bundle.runId, findings, repaired };
144
+ }
145
+ /**
146
+ * Execute a bundle, resuming whatever has already succeeded (contract §17, §19.1).
147
+ *
148
+ * Reconciles first by default, so the safe path is the one a caller gets without asking.
149
+ */
150
+ async execute(bundle, options) {
151
+ assertBundleValid(bundle);
152
+ const warnings = [];
153
+ const written = [];
154
+ if (options.reconcile !== false) {
155
+ const report = await this.reconcile(bundle, { actor: options.actor });
156
+ written.push(...report.repaired);
157
+ for (const finding of report.findings) {
158
+ if (finding.explanation !== undefined && finding.action !== "confirmed_absent") {
159
+ warnings.push(finding.explanation);
160
+ }
161
+ }
162
+ }
163
+ let latest = latestByKey(await this.#receipts.list(bundle.runId));
164
+ let halted = false;
165
+ let haltState;
166
+ for (const { operation, criticality, idempotencyKey } of this.#plan(bundle)) {
167
+ // §17 calls the best-effort operations "post-upload": they run only once every required
168
+ // one has succeeded. Attempting a notification about a release that failed would announce
169
+ // something that does not exist.
170
+ if (halted) {
171
+ warnings.push(`"${operation.operationId}" was not attempted because a required operation did not ` +
172
+ "succeed.");
173
+ continue;
174
+ }
175
+ const existing = latest.get(idempotencyKey);
176
+ if (existing?.status === "succeeded" || existing?.status === "skipped")
177
+ continue;
178
+ // An operation whose outcome was never confirmed must not be retried (contract §17). If
179
+ // reconciliation could have resolved it, it already ran above and this receipt would be
180
+ // terminal; reaching here means the destination cannot be queried.
181
+ if (existing?.status === "pending") {
182
+ throw releaseError(ReleaseErrorCodes.RECONCILIATION_UNAVAILABLE, `"${operation.operationId}" was attempted and its outcome was never confirmed, and ` +
183
+ `destination "${operation.destination}" cannot be queried to find out. Retrying ` +
184
+ "would risk performing it twice, so it is refused until the outcome is established.", {
185
+ category: "conflict",
186
+ retryable: false,
187
+ details: {
188
+ runId: bundle.runId,
189
+ operationId: operation.operationId,
190
+ destination: operation.destination,
191
+ },
192
+ });
193
+ }
194
+ const verdict = operation.requiresAuthority === undefined
195
+ ? { authorized: true }
196
+ : await this.#authorizer.check(bundle.runId, operation.requiresAuthority);
197
+ if (!verdict.authorized) {
198
+ const explanation = `"${operation.operationId}" requires authority "${operation.requiresAuthority}", ` +
199
+ `which is not held. ${verdict.explanation ?? ""}`.trim();
200
+ // A required operation without authority is a refusal, not a warning: §13.4 binds
201
+ // release approval to exact inputs, and continuing past it would publish unapproved.
202
+ if (criticality === "required") {
203
+ throw releaseError(ReleaseErrorCodes.RELEASE_NOT_AUTHORIZED, explanation, {
204
+ category: "policy",
205
+ retryable: false,
206
+ details: {
207
+ runId: bundle.runId,
208
+ operationId: operation.operationId,
209
+ authority: operation.requiresAuthority,
210
+ },
211
+ });
212
+ }
213
+ // A best-effort operation is recorded as skipped rather than failed. It was never
214
+ // attempted, and a `failed` receipt would claim the destination rejected it.
215
+ written.push(await this.#record(bundle, operation, idempotencyKey, { status: "skipped", message: explanation }, options.actor));
216
+ warnings.push(explanation);
217
+ continue;
218
+ }
219
+ const adapter = this.#adapters.require(operation.destination);
220
+ const outcome = await adapter.execute({ operation, idempotencyKey, runId: bundle.runId });
221
+ written.push(await this.#record(bundle, operation, idempotencyKey, outcome, options.actor));
222
+ if (outcome.status === "succeeded")
223
+ continue;
224
+ if (criticality === "best_effort") {
225
+ warnings.push(`Best-effort operation "${operation.operationId}" did not succeed ` +
226
+ `(${outcome.status}). The release is unaffected.`);
227
+ continue;
228
+ }
229
+ halted = true;
230
+ haltState = outcome.status === "pending" ? "pending" : "failed";
231
+ }
232
+ latest = latestByKey(await this.#receipts.list(bundle.runId));
233
+ const status = await this.status(bundle);
234
+ const state = haltState ?? (status.state === "succeeded" ? "succeeded" : "pending");
235
+ return { bundleId: bundle.bundleId, runId: bundle.runId, state, status, written, warnings };
236
+ }
237
+ /** Operations paired with their category and derived key, in execution order. */
238
+ #plan(bundle) {
239
+ const entries = [
240
+ ...bundle.required.map((operation) => ({
241
+ operation: operation,
242
+ criticality: "required",
243
+ })),
244
+ ...bundle.bestEffort.map((operation) => ({
245
+ operation: operation,
246
+ criticality: "best_effort",
247
+ })),
248
+ ];
249
+ return entries.map((entry) => ({
250
+ ...entry,
251
+ idempotencyKey: deriveIdempotencyKey(bundle.bundleId, entry.operation),
252
+ }));
253
+ }
254
+ /** Write a receipt and emit its event (contract §6.4, §17). */
255
+ async #record(bundle, operation, idempotencyKey, outcome, actor) {
256
+ const at = this.#now().toISOString();
257
+ const receipt = {
258
+ schemaVersion: SCHEMA_VERSION,
259
+ releaseId: this.#nextReleaseId(),
260
+ runId: bundle.runId,
261
+ destination: operation.destination,
262
+ operation: operation.kind,
263
+ idempotencyKey,
264
+ status: outcome.status,
265
+ inputHashes: [...operation.inputHashes],
266
+ ...(outcome.status === "succeeded" && outcome.remoteId !== undefined
267
+ ? { remoteId: outcome.remoteId }
268
+ : {}),
269
+ ...(outcome.status === "succeeded" && outcome.remoteUrl !== undefined
270
+ ? { remoteUrl: outcome.remoteUrl }
271
+ : {}),
272
+ // `pending` is not terminal, so it carries no completion time (contract §17).
273
+ ...(outcome.status === "pending" ? {} : { completedAt: at }),
274
+ ...(outcome.status === "failed"
275
+ ? {
276
+ error: {
277
+ code: "ALDUS_RELEASE_OPERATION_FAILED",
278
+ category: "provider",
279
+ message: outcome.message,
280
+ retryable: outcome.retryable ?? true,
281
+ occurredAt: at,
282
+ },
283
+ }
284
+ : {}),
285
+ };
286
+ await this.#receipts.append(bundle.runId, receipt);
287
+ await this.#events.emit(this.#event(bundle, operation, receipt, at, outcome, actor));
288
+ return receipt;
289
+ }
290
+ /** The §6.4 event for one recorded outcome. */
291
+ #event(bundle, operation, receipt, at, outcome, actor) {
292
+ return {
293
+ schemaVersion: SCHEMA_VERSION,
294
+ eventId: this.#nextEventId(),
295
+ occurredAt: at,
296
+ episodeId: bundle.episodeId,
297
+ runId: bundle.runId,
298
+ action: `release.operation.${receipt.status}`,
299
+ actor,
300
+ inputRefs: [],
301
+ outputRefs: [],
302
+ idempotencyKey: receipt.idempotencyKey,
303
+ details: {
304
+ bundleId: bundle.bundleId,
305
+ operationId: operation.operationId,
306
+ kind: operation.kind,
307
+ destination: operation.destination,
308
+ releaseId: receipt.releaseId,
309
+ ...("message" in outcome && outcome.message !== undefined
310
+ ? { message: outcome.message }
311
+ : {}),
312
+ },
313
+ ...(receipt.error === undefined ? {} : { error: receipt.error }),
314
+ };
315
+ }
316
+ }
317
+ /** Derive one operation's state from its latest receipt. */
318
+ function toStatus(operation, criticality, idempotencyKey, receipt) {
319
+ return {
320
+ operationId: operation.operationId,
321
+ kind: operation.kind,
322
+ destination: operation.destination,
323
+ criticality,
324
+ idempotencyKey,
325
+ state: receipt === undefined ? "not_started" : receipt.status,
326
+ ...(receipt === undefined ? {} : { receipt }),
327
+ };
328
+ }
329
+ /** A bundle's state is the state of its required operations (contract §17). */
330
+ function bundleState(required) {
331
+ if (required.every((entry) => entry.state === "succeeded" || entry.state === "skipped")) {
332
+ return "succeeded";
333
+ }
334
+ if (required.some((entry) => entry.state === "failed"))
335
+ return "failed";
336
+ if (required.some((entry) => entry.state === "pending"))
337
+ return "pending";
338
+ if (required.every((entry) => entry.state === "not_started"))
339
+ return "not_started";
340
+ return "in_progress";
341
+ }
342
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `@aldus-runtime/release` — release bundles, resumable operations, and external-state reconciliation.
3
+ *
4
+ * Implements architecture contract §22 **WP-12 Release adapters** and §17.
5
+ *
6
+ * Contract §22's own wording for this work package names two publishing platforms. This package
7
+ * names none, and that is deliberate: §4.2 keeps publishing platforms out of the runtime, §1.2
8
+ * explicitly rules out prescribing particular release targets, and §4.3 places release
9
+ * configuration in Integration. What ships here is the **adapter contract and the resumable
10
+ * machinery**; an adopter supplies the client that talks to a destination.
11
+ *
12
+ * Not implemented here, by design: any real platform client, the CLI (WP-08), the Production MCP
13
+ * (WP-11), and the TTS ledger (WP-07).
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+ export { AdapterRegistry, RecordingReleaseAdapter, type AdapterOutcome, type RecordingAdapterOptions, type ReleaseAdapter, type ReleaseRequest, type RemoteState, } from "./adapter.js";
18
+ export { gateEngineAuthorizer, permitAllAuthorizer, type AuthorityVerdict, type ReleaseAuthorizer, } from "./authorization.js";
19
+ export { assertBundleValid, deriveIdempotencyKey, operationsOf, type ReleaseBundle, } from "./bundle.js";
20
+ export { ReleaseErrorCodes, releaseError, type ReleaseErrorCode } from "./errors.js";
21
+ export { OPERATION_STATES, ReleaseExecutor, type BundleStatus, type ExecuteOptions, type OperationState, type OperationStatus, type ReconciliationFinding, type ReconciliationReport, type ReleaseExecutorOptions, type ReleaseOutcome, } from "./executor.js";
22
+ export { bestEffortOperation, requiredOperation, type BestEffortOperation, type OperationCriticality, type ReleaseOperation, type ReleaseOperationBase, type RequiredOperation, } from "./operation.js";
23
+ export { MemoryReleaseEventSink, MemoryReleaseReceiptStore, eventStoreSink, latestByKey, runStoreReceipts, type ReleaseEventSink, type ReleaseReceiptStore, } from "./ports.js";
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,GACvB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAErF,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `@aldus-runtime/release` — release bundles, resumable operations, and external-state reconciliation.
3
+ *
4
+ * Implements architecture contract §22 **WP-12 Release adapters** and §17.
5
+ *
6
+ * Contract §22's own wording for this work package names two publishing platforms. This package
7
+ * names none, and that is deliberate: §4.2 keeps publishing platforms out of the runtime, §1.2
8
+ * explicitly rules out prescribing particular release targets, and §4.3 places release
9
+ * configuration in Integration. What ships here is the **adapter contract and the resumable
10
+ * machinery**; an adopter supplies the client that talks to a destination.
11
+ *
12
+ * Not implemented here, by design: any real platform client, the CLI (WP-08), the Production MCP
13
+ * (WP-11), and the TTS ledger (WP-07).
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+ export { AdapterRegistry, RecordingReleaseAdapter, } from "./adapter.js";
18
+ export { gateEngineAuthorizer, permitAllAuthorizer, } from "./authorization.js";
19
+ export { assertBundleValid, deriveIdempotencyKey, operationsOf, } from "./bundle.js";
20
+ export { ReleaseErrorCodes, releaseError } from "./errors.js";
21
+ export { OPERATION_STATES, ReleaseExecutor, } from "./executor.js";
22
+ export { bestEffortOperation, requiredOperation, } from "./operation.js";
23
+ export { MemoryReleaseEventSink, MemoryReleaseReceiptStore, eventStoreSink, latestByKey, runStoreReceipts, } from "./ports.js";
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,eAAe,EACf,uBAAuB,GAMxB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GAGpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,GAEb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAyB,MAAM,aAAa,CAAC;AAErF,OAAO,EACL,gBAAgB,EAChB,eAAe,GAShB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,GAMlB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,EACd,WAAW,EACX,gBAAgB,GAGjB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Release operations (architecture contract §17).
3
+ *
4
+ * §17 requires that "pre-release hard gates and post-upload best-effort operations MUST be
5
+ * distinguished". The distinction is made **structurally** rather than with a severity field:
6
+ * a required operation and a best-effort one are different types, produced by different
7
+ * constructors, and held in different arrays of a {@link ReleaseBundle}.
8
+ *
9
+ * A `criticality: "required" | "best_effort"` field would have been smaller, and wrong. A field
10
+ * is set at a call site, often far from where the consequence lands, and setting it incorrectly
11
+ * turns a failed thumbnail into a failed release or — far worse — a failed media upload into a
12
+ * release that reports success. Requiring the caller to *place* the operation in one list or the
13
+ * other makes the mistake a type error rather than a typo.
14
+ */
15
+ /**
16
+ * Brand distinguishing the two operation categories at the type level.
17
+ *
18
+ * Declared but never exported as a value, so an operation of either kind can only come from the
19
+ * constructor below. A caller cannot hand-write an object literal that satisfies
20
+ * {@link RequiredOperation}.
21
+ */
22
+ declare const CRITICALITY: unique symbol;
23
+ /** Fields shared by both categories. */
24
+ export interface ReleaseOperationBase {
25
+ /**
26
+ * Identity of this operation within its bundle.
27
+ *
28
+ * Stable across resumption: it is how a stored receipt is matched back to the operation that
29
+ * produced it, so renaming one orphans its receipt.
30
+ */
31
+ operationId: string;
32
+ /**
33
+ * What kind of operation this is, e.g. a media upload, a caption attachment, or a visibility
34
+ * transition.
35
+ *
36
+ * An OPEN string, never a Core-defined enum. Contract §17 lists candidate operations — media
37
+ * upload, captions, thumbnail, title and description, privacy transition, playlist, podcast
38
+ * storage and RSS, notification channels — as an illustration of what an adopter might need,
39
+ * and §4.2 keeps adopter process out of the runtime. Do not narrow this to a union.
40
+ */
41
+ kind: string;
42
+ /**
43
+ * Where the operation is directed.
44
+ *
45
+ * An OPEN string. Contract §1.2 explicitly rules out prescribing particular release targets,
46
+ * so a destination is an adopter's name for one of its own, resolved to an adapter at
47
+ * execution time.
48
+ */
49
+ destination: string;
50
+ /**
51
+ * Digests of exactly what this operation releases (contract §13.4).
52
+ *
53
+ * §13.4 requires release approval to bind to the final render, captions, metadata,
54
+ * destination, and visibility policy. These digests are what an approval binds, and they feed
55
+ * the idempotency key, so changing what is released changes the operation's identity.
56
+ */
57
+ inputHashes: readonly string[];
58
+ /**
59
+ * The authority this operation requires, if any (contract §13.4, §18.1).
60
+ *
61
+ * Names an operation string a gate grants — for example the separate upload and publication
62
+ * authorities §13.4 demands. Left absent only for operations that genuinely need no approval.
63
+ * The gate engine decides whether the authority is held; this package never re-decides it.
64
+ */
65
+ requiresAuthority?: string;
66
+ /** Opaque parameters passed through to the adapter. Never inspected here. */
67
+ parameters?: Readonly<Record<string, unknown>>;
68
+ }
69
+ /**
70
+ * An operation whose failure fails the release (contract §17 "pre-release hard gates").
71
+ *
72
+ * Required operations run in declaration order, and the first failure stops the bundle: a media
73
+ * upload that failed must not be followed by a visibility transition making nothing public.
74
+ */
75
+ export interface RequiredOperation extends ReleaseOperationBase {
76
+ readonly [CRITICALITY]: "required";
77
+ }
78
+ /**
79
+ * An operation whose failure is recorded but does not fail the release (contract §17
80
+ * "post-upload best-effort operations").
81
+ *
82
+ * A failed thumbnail or notification leaves a `failed` receipt and an operator-visible warning;
83
+ * it does not undo an upload that succeeded.
84
+ */
85
+ export interface BestEffortOperation extends ReleaseOperationBase {
86
+ readonly [CRITICALITY]: "best_effort";
87
+ }
88
+ /** Either category, where only the shared fields matter. */
89
+ export type ReleaseOperation = RequiredOperation | BestEffortOperation;
90
+ /** Declare an operation whose failure fails the release (contract §17). */
91
+ export declare function requiredOperation(operation: ReleaseOperationBase): RequiredOperation;
92
+ /** Declare an operation whose failure is recorded but tolerated (contract §17). */
93
+ export declare function bestEffortOperation(operation: ReleaseOperationBase): BestEffortOperation;
94
+ /**
95
+ * Which category an operation belongs to.
96
+ *
97
+ * Derived from where the bundle holds it rather than read off the operation, because the arrays
98
+ * are the source of truth — see this module's header.
99
+ */
100
+ export type OperationCriticality = "required" | "best_effort";
101
+ export {};
102
+ //# sourceMappingURL=operation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation.d.ts","sourceRoot":"","sources":["../src/operation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;GAMG;AACH,OAAO,CAAC,MAAM,WAAW,EAAE,OAAO,MAAM,CAAC;AAEzC,wCAAwC;AACxC,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAChD;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAkB,SAAQ,oBAAoB;IAC7D,QAAQ,CAAC,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC;CACpC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,QAAQ,CAAC,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC;CACvC;AAED,4DAA4D;AAC5D,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAEvE,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,oBAAoB,GAAG,iBAAiB,CAEpF;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,oBAAoB,GAAG,mBAAmB,CAExF;AAED;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,aAAa,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Release operations (architecture contract §17).
3
+ *
4
+ * §17 requires that "pre-release hard gates and post-upload best-effort operations MUST be
5
+ * distinguished". The distinction is made **structurally** rather than with a severity field:
6
+ * a required operation and a best-effort one are different types, produced by different
7
+ * constructors, and held in different arrays of a {@link ReleaseBundle}.
8
+ *
9
+ * A `criticality: "required" | "best_effort"` field would have been smaller, and wrong. A field
10
+ * is set at a call site, often far from where the consequence lands, and setting it incorrectly
11
+ * turns a failed thumbnail into a failed release or — far worse — a failed media upload into a
12
+ * release that reports success. Requiring the caller to *place* the operation in one list or the
13
+ * other makes the mistake a type error rather than a typo.
14
+ */
15
+ /** Declare an operation whose failure fails the release (contract §17). */
16
+ export function requiredOperation(operation) {
17
+ return { ...operation };
18
+ }
19
+ /** Declare an operation whose failure is recorded but tolerated (contract §17). */
20
+ export function bestEffortOperation(operation) {
21
+ return { ...operation };
22
+ }
23
+ //# sourceMappingURL=operation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation.js","sourceRoot":"","sources":["../src/operation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAkFH,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,SAA+B;IAC/D,OAAO,EAAE,GAAG,SAAS,EAAuB,CAAC;AAC/C,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,mBAAmB,CAAC,SAA+B;IACjE,OAAO,EAAE,GAAG,SAAS,EAAyB,CAAC;AACjD,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Ports the release executor reads and writes through.
3
+ *
4
+ * Contract §7 requires core models to be independent of physical storage, so this package binds
5
+ * to interfaces rather than to a store. Each is kept to the operations actually used and tested:
6
+ * an aspirational method on a port is worse than an absent one, because a second adapter is
7
+ * written against it and only discovers at runtime that nothing honours it.
8
+ */
9
+ import type { AldusEvent, ReleaseReceipt } from "@aldus-runtime/core";
10
+ import type { EventStore, RunStore } from "@aldus-runtime/file-store";
11
+ /**
12
+ * Storage for release receipts (contract §7 `release.json`).
13
+ *
14
+ * There is no update in place and no delete. §17's receipts are an audit record of what was
15
+ * attempted against a destination: an operation retried after a failure produces a second
16
+ * receipt, not an edit to the first, because the fact that the first attempt failed is what
17
+ * explains the retry. {@link latestFor} resolves the current outcome by reading them in order.
18
+ */
19
+ export interface ReleaseReceiptStore {
20
+ /** Every receipt recorded for a Run, in the order they were appended. */
21
+ list(runId: string): Promise<ReleaseReceipt[]>;
22
+ /** Append one receipt. */
23
+ append(runId: string, receipt: ReleaseReceipt): Promise<void>;
24
+ }
25
+ /**
26
+ * Where lifecycle events go (contract §6.4).
27
+ *
28
+ * §6.4 requires **every** state mutation to emit an immutable event, so recording a receipt and
29
+ * emitting its event are one operation from the executor's point of view.
30
+ */
31
+ export interface ReleaseEventSink {
32
+ /** Emit one event. */
33
+ emit(event: AldusEvent): Promise<void>;
34
+ }
35
+ /**
36
+ * The most recent receipt per idempotency key.
37
+ *
38
+ * Later receipts win, which is what makes a retry after a failure resolve to the retry's outcome
39
+ * rather than the original failure.
40
+ */
41
+ export declare function latestByKey(receipts: readonly ReleaseReceipt[]): Map<string, ReleaseReceipt>;
42
+ /** An in-memory {@link ReleaseReceiptStore}, for tests and for dry runs. */
43
+ export declare class MemoryReleaseReceiptStore implements ReleaseReceiptStore {
44
+ #private;
45
+ list(runId: string): Promise<ReleaseReceipt[]>;
46
+ append(runId: string, receipt: ReleaseReceipt): Promise<void>;
47
+ /**
48
+ * Discard every receipt for a Run, as a lost or never-written `release.json` would.
49
+ *
50
+ * Exists for the reconciliation tests: losing a receipt whose operation succeeded remotely is
51
+ * the exact condition §17's reconciliation requirement addresses, and it has to be reproducible
52
+ * to be tested.
53
+ */
54
+ forget(runId: string): void;
55
+ }
56
+ /** An in-memory {@link ReleaseEventSink} that retains what it was given, for tests. */
57
+ export declare class MemoryReleaseEventSink implements ReleaseEventSink {
58
+ readonly events: AldusEvent[];
59
+ emit(event: AldusEvent): Promise<void>;
60
+ }
61
+ /**
62
+ * A {@link ReleaseReceiptStore} backed by `@aldus-runtime/file-store`'s per-Run `release.json` (§7).
63
+ *
64
+ * `RunStore.addRecord` takes the Run lock itself, so this must not be called from inside code
65
+ * that already holds it — file locks are not re-entrant and the acquisition is refused outright
66
+ * (ADR-0005). The executor therefore holds no Run lock while writing receipts.
67
+ */
68
+ export declare function runStoreReceipts(runs: RunStore): ReleaseReceiptStore;
69
+ /**
70
+ * A {@link ReleaseEventSink} backed by `@aldus-runtime/file-store`'s event log (§6.4).
71
+ *
72
+ * Carries the same caution as {@link runStoreReceipts}: `EventStore.append` takes the Run lock to
73
+ * assign a sequence (ADR-0005).
74
+ */
75
+ export declare function eventStoreSink(events: EventStore): ReleaseEventSink;
76
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAEtE;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC/C,0BAA0B;IAC1B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB;IACtB,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAI5F;AAED,4EAA4E;AAC5E,qBAAa,yBAA0B,YAAW,mBAAmB;;IAGnE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAE7C;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5D;IAED;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE1B;CACF;AAED,uFAAuF;AACvF,qBAAa,sBAAuB,YAAW,gBAAgB;IAC7D,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,CAAM;IAEnC,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrC;CACF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,mBAAmB,CAKpE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAMnE"}