@githolon/dsl 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.
- package/LICENSE.md +36 -0
- package/compile_package.mjs +50 -0
- package/package.json +59 -0
- package/src/aggregate.ts +167 -0
- package/src/authoring.ts +119 -0
- package/src/build_package.ts +636 -0
- package/src/certified_read.ts +313 -0
- package/src/codegen_dart.ts +2732 -0
- package/src/codegen_dot.ts +466 -0
- package/src/codegen_provider_dart.ts +358 -0
- package/src/codegen_ts.ts +365 -0
- package/src/codegen_usda.ts +388 -0
- package/src/combined.ts +195 -0
- package/src/compile_engine.ts +567 -0
- package/src/compile_package_main.ts +496 -0
- package/src/compose.ts +317 -0
- package/src/count.ts +218 -0
- package/src/ctx.ts +57 -0
- package/src/derived.ts +138 -0
- package/src/directive.ts +306 -0
- package/src/drivers.ts +95 -0
- package/src/emits_guard.ts +123 -0
- package/src/engine_entry.ts +449 -0
- package/src/exists.ts +170 -0
- package/src/extremum.ts +227 -0
- package/src/fields.ts +291 -0
- package/src/framework/bootstrap.ts +22 -0
- package/src/framework/disclosure.ts +108 -0
- package/src/framework/domain_lifecycle.ts +108 -0
- package/src/framework/identity.ts +537 -0
- package/src/framework/impure_capability.ts +643 -0
- package/src/framework/rbac.ts +418 -0
- package/src/framework/repair.ts +150 -0
- package/src/framework/sync_lifecycle.ts +125 -0
- package/src/framework/workspace_invariant.ts +128 -0
- package/src/framework/workspaces.ts +817 -0
- package/src/index.ts +317 -0
- package/src/manifest.ts +947 -0
- package/src/ops.ts +145 -0
- package/src/ordered_read.ts +228 -0
- package/src/predicate.ts +203 -0
- package/src/query/compile.ts +0 -0
- package/src/query/relations.ts +144 -0
- package/src/query.ts +151 -0
- package/src/read.ts +54 -0
- package/src/relation.ts +189 -0
- package/src/report/csv.ts +54 -0
- package/src/report.ts +401 -0
- package/src/spatial.ts +115 -0
- package/src/sum.ts +194 -0
- package/src/usd.ts +563 -0
- package/src/wire.ts +149 -0
- package/src/wire_encode.ts +250 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
// NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
|
|
2
|
+
// remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
|
|
3
|
+
// wasm32-wasip1 artifact {kernel · projection · embedded
|
|
4
|
+
// QuickJS engine} on V8 + WASI-shim, byte-identical everywhere. V8 = portability; the one
|
|
5
|
+
// wasm = determinism. No native, no wasmtime, no 2nd artifact, no domain-JS on bare V8.
|
|
6
|
+
// If a file isn't this / hosting this / authoring for this / proving this — it's gone.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Provider SDK codegen — the SYMMETRIC TWIN of the typed peer write codegen
|
|
10
|
+
* (`codegen_dart.ts`).
|
|
11
|
+
*
|
|
12
|
+
* Order and Receipt are ordinary intent roles. This codegen gives a capability provider typed
|
|
13
|
+
* decode/encode helpers; it does not create a separate admission job protocol or a privileged
|
|
14
|
+
* mutation path.
|
|
15
|
+
*
|
|
16
|
+
* ── Why this exists (Jack 2026-06-04) ────────────────────────────────────────
|
|
17
|
+
* Code is ~free; INTEGRATION POINTS are expensive + fuckup-guaranteed. A capability
|
|
18
|
+
* PROVIDER (Whisper, a renderer, a multi-target PR transporter) sits at
|
|
19
|
+
* the far end of the framework `impureCapability` Order→Receipt pair. Today a dev would
|
|
20
|
+
* hand-parse the incoming Order intent JSON and hand-build the Receipt — exactly the
|
|
21
|
+
* brittle integration the peer write codegen eliminated on the WRITE side. This generator
|
|
22
|
+
* eliminates it on the PROVIDE side: a dev writes ONE typed method
|
|
23
|
+
* `handler(Order) -> Result | Failure`, and the generated dispatcher DECODES the Order
|
|
24
|
+
* and ENCODES the ordinary Receipt intent — proven in ISOLATION (a Dart unit test), with NO deploy /
|
|
25
|
+
* daemon / e2e.
|
|
26
|
+
*
|
|
27
|
+
* ── What it emits, per `impureCapability` capability (the STANDARD shape, never invented) ──
|
|
28
|
+
* 1. a typed `XOrder` — the Order params (envelope provenance `requestId` /
|
|
29
|
+
* `requestedBy` / `requestedAt` ⊕ the tenant params from the DSL), with a
|
|
30
|
+
* `fromJson` decoding the incoming Order intent's payload JSON;
|
|
31
|
+
* 2. a typed `XResult` — the success payload the handler returns (`resultRef`, the
|
|
32
|
+
* content-addressed Evidence handle — the framework's `completeSchema.resultRef`),
|
|
33
|
+
* implementing `TaskResult.toResultRef()`;
|
|
34
|
+
* 3. typed failure/blocked/DLQ outcomes;
|
|
35
|
+
* 4. a handler typedef `XHandler = TaskOutcome<XResult> Function(XOrder)`;
|
|
36
|
+
* 5. a generated `XProvider` dispatcher (`implements TaskProvider`): given the
|
|
37
|
+
* registered typed handler, `handleOrder(orderJson, now)` DECODES → calls the
|
|
38
|
+
* handler → ENCODES the typed outcome into the ordinary RECEIPT intent — REUSING the peer write
|
|
39
|
+
* codegen's `CompleteXPayload` / `FailXPayload` / `BlockXPayload` /
|
|
40
|
+
* `DeadLetterXPayload`. The Receipt is an
|
|
41
|
+
* ordinary `NomosIntent` → it ships through the SAME `dispatch` gate (ONE-WRITE-
|
|
42
|
+
* PATH; the provider is a Nomos author, not a bypass).
|
|
43
|
+
*
|
|
44
|
+
* VENDOR-NEUTRAL: no lambda/worker/cloud-function nouns — the generated SDK is a pure
|
|
45
|
+
* typed decode/encode pair the host loop drives. TYPESAFE-TO-THE-BOUNDARY: no `dynamic`
|
|
46
|
+
* / `Map<String,dynamic>` / raw-JSON in the dev-facing handler API; types stop only at
|
|
47
|
+
* the JSON serde inside `fromJson` / the reused `toPayloadJson`.
|
|
48
|
+
*
|
|
49
|
+
* It REUSES the peer codegen's already-emitted Receipt payload classes (same file),
|
|
50
|
+
* so the Receipt the provider authors is byte-identical to the one the peer codegen
|
|
51
|
+
* already proved lowers correctly — the round-trip is closed by construction.
|
|
52
|
+
*/
|
|
53
|
+
import { z } from "zod";
|
|
54
|
+
import {
|
|
55
|
+
cap,
|
|
56
|
+
introspectPayload,
|
|
57
|
+
toJsonExpr,
|
|
58
|
+
type PayloadFieldSpec,
|
|
59
|
+
} from "./codegen_dart.js";
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The minimal shape of a `impureCapability` declaration the provider codegen needs. A
|
|
63
|
+
* `ImpureCapability` (framework `impure_capability.ts`) carries EXACTLY this — its task `aggregate`
|
|
64
|
+
* handle (whose `.id` is the wire type) and its `.order` / receipt directives
|
|
65
|
+
* directives — so a `impureCapability(...)` return value satisfies `ImpureCapabilityDecl`
|
|
66
|
+
* STRUCTURALLY. The emit script passes the values straight through: no re-derivation, no
|
|
67
|
+
* fragile structural inference over the flattened directive list.
|
|
68
|
+
*/
|
|
69
|
+
export interface ImpureCapabilityDecl {
|
|
70
|
+
/** The task aggregate handle; `.id` is the wire type (e.g. `TranscribeVoiceNoteAggregate`). */
|
|
71
|
+
readonly aggregate: { readonly id: string };
|
|
72
|
+
/** The ORDER directive: `.creates`, payload = envelope ⊕ tenant params. */
|
|
73
|
+
readonly order: { readonly id: string; readonly payloadSchema: z.ZodTypeAny };
|
|
74
|
+
/** The COMPLETE (success) Receipt directive: payload = {requestId, resultRef, completedAt}. */
|
|
75
|
+
readonly complete: { readonly id: string };
|
|
76
|
+
/** The FAIL (failure) Receipt directive: payload = {requestId, failureReason, failedAt}. */
|
|
77
|
+
readonly fail: { readonly id: string };
|
|
78
|
+
/** The BLOCKED Receipt directive: payload = {requestId, blockedReason, blockedAt, nextAction}. */
|
|
79
|
+
readonly block: { readonly id: string };
|
|
80
|
+
/** The DLQ Receipt directive: payload = {requestId, deadLetterReason, deadLetteredAt}. */
|
|
81
|
+
readonly deadLetter: { readonly id: string };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The framework ORDER-ENVELOPE field names — provenance, NOT tenant params. The Order
|
|
85
|
+
* type carries them (the provider sees the whole Order intent) but the codegen labels
|
|
86
|
+
* them so the doc separates provenance from the tenant's request params. */
|
|
87
|
+
const ORDER_ENVELOPE_FIELDS = new Set(["requestId", "requestedBy", "requestedAt"]);
|
|
88
|
+
|
|
89
|
+
/** A stable PascalCase base for a task's generated class family, derived from its ORDER
|
|
90
|
+
* directive id with a leading `create`/`order` verb stripped (so `createTranscribeVoiceNote`
|
|
91
|
+
* → `TranscribeVoiceNote`). Pure + deterministic. */
|
|
92
|
+
export function taskBaseName(orderDirectiveId: string): string {
|
|
93
|
+
const stripped = orderDirectiveId.replace(/^(create|order)/, "");
|
|
94
|
+
const base = stripped === "" ? orderDirectiveId : stripped;
|
|
95
|
+
return cap(base);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The reused peer-codegen Receipt payload class names (same generated file). */
|
|
99
|
+
function completePayloadClass(completeDirectiveId: string): string {
|
|
100
|
+
return `${cap(completeDirectiveId)}Payload`;
|
|
101
|
+
}
|
|
102
|
+
function failPayloadClass(failDirectiveId: string): string {
|
|
103
|
+
return `${cap(failDirectiveId)}Payload`;
|
|
104
|
+
}
|
|
105
|
+
function blockPayloadClass(blockDirectiveId: string): string {
|
|
106
|
+
return `${cap(blockDirectiveId)}Payload`;
|
|
107
|
+
}
|
|
108
|
+
function deadLetterPayloadClass(deadLetterDirectiveId: string): string {
|
|
109
|
+
return `${cap(deadLetterDirectiveId)}Payload`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Dart field DECL for one Order field (optional → nullable). */
|
|
113
|
+
function orderFieldDecl(s: PayloadFieldSpec): string {
|
|
114
|
+
const provenance = ORDER_ENVELOPE_FIELDS.has(s.name)
|
|
115
|
+
? ` // envelope provenance (framework)`
|
|
116
|
+
: ``;
|
|
117
|
+
return ` final ${s.dartType}${s.optional ? "?" : ""} ${s.name};${provenance}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Dart ctor PARAM for one Order field. */
|
|
121
|
+
function orderCtorParam(s: PayloadFieldSpec): string {
|
|
122
|
+
return ` ${s.optional ? "" : "required "}this.${s.name},`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Expression decoding ONE Order field FROM the already-`jsonDecode`d payload map `j`.
|
|
127
|
+
* The REVERSE of the peer codegen's `toJsonExpr` — same field-kind matrix:
|
|
128
|
+
* • scalar — read directly, cast to the Dart scalar type.
|
|
129
|
+
* • enum — `XEnum.fromWire(j['f'] as String)`.
|
|
130
|
+
* • strList — `(j['f'] as List).cast<String>()`.
|
|
131
|
+
* • json — the nested object/list verbatim (`Map<String,Object?>` / `List<Object?>`).
|
|
132
|
+
* Optional fields tolerate absence (return null); required fields cast strictly (a
|
|
133
|
+
* missing required field throws on the cast — the strict-decode contract).
|
|
134
|
+
*/
|
|
135
|
+
function orderFromJsonExpr(s: PayloadFieldSpec): string {
|
|
136
|
+
const raw = `j['${s.name}']`;
|
|
137
|
+
switch (s.toJson) {
|
|
138
|
+
case "scalar": {
|
|
139
|
+
// int reads tolerate a JSON number that decodes as double/int; cast via `as num`.
|
|
140
|
+
if (s.dartType === "int") {
|
|
141
|
+
return s.optional
|
|
142
|
+
? `${raw} == null ? null : (${raw} as num).toInt()`
|
|
143
|
+
: `(${raw} as num).toInt()`;
|
|
144
|
+
}
|
|
145
|
+
if (s.dartType === "double") {
|
|
146
|
+
return s.optional
|
|
147
|
+
? `${raw} == null ? null : (${raw} as num).toDouble()`
|
|
148
|
+
: `(${raw} as num).toDouble()`;
|
|
149
|
+
}
|
|
150
|
+
// String / bool.
|
|
151
|
+
return s.optional ? `${raw} as ${s.dartType}?` : `${raw} as ${s.dartType}`;
|
|
152
|
+
}
|
|
153
|
+
case "enum": {
|
|
154
|
+
return s.optional
|
|
155
|
+
? `${raw} == null ? null : ${s.dartType}.fromWire(${raw} as String)`
|
|
156
|
+
: `${s.dartType}.fromWire(${raw} as String)`;
|
|
157
|
+
}
|
|
158
|
+
case "strList": {
|
|
159
|
+
return s.optional
|
|
160
|
+
? `(${raw} as List<Object?>?)?.cast<String>()`
|
|
161
|
+
: `(${raw} as List<Object?>).cast<String>()`;
|
|
162
|
+
}
|
|
163
|
+
case "json": {
|
|
164
|
+
// A nested object → `Map<String,Object?>`; a `List<Object?>` payload (rare) stays
|
|
165
|
+
// a list. The Dart type drives the cast.
|
|
166
|
+
return s.optional ? `${raw} as ${s.dartType}?` : `${raw} as ${s.dartType}`;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Emit the typed `XOrder` class — the DECODED incoming Order. */
|
|
172
|
+
function emitOrderClass(
|
|
173
|
+
base: string,
|
|
174
|
+
orderDirectiveId: string,
|
|
175
|
+
specs: PayloadFieldSpec[],
|
|
176
|
+
): string {
|
|
177
|
+
const cls = `${base}Order`;
|
|
178
|
+
const fieldDecls = specs.map(orderFieldDecl).join("\n");
|
|
179
|
+
const ctorParams = specs.map(orderCtorParam).join("\n");
|
|
180
|
+
const fromJsonAssigns = specs
|
|
181
|
+
.map((s) => ` ${s.name}: ${orderFromJsonExpr(s)},`)
|
|
182
|
+
.join("\n");
|
|
183
|
+
return [
|
|
184
|
+
`/// The typed ORDER for the \`${orderDirectiveId}\` capability — what the provider`,
|
|
185
|
+
`/// RECEIVES. Carries the framework envelope provenance (requestId / requestedBy /`,
|
|
186
|
+
`/// requestedAt) ⊕ the tenant request params. Decoded from the incoming Order intent's`,
|
|
187
|
+
`/// payload JSON via [${cls}.fromJson]; the handler sees ONLY this typed value.`,
|
|
188
|
+
`class ${cls} {`,
|
|
189
|
+
fieldDecls,
|
|
190
|
+
``,
|
|
191
|
+
` const ${cls}({`,
|
|
192
|
+
ctorParams,
|
|
193
|
+
` });`,
|
|
194
|
+
``,
|
|
195
|
+
` /// Decode an Order from the already-\`jsonDecode\`d payload map (the EXACT shape the`,
|
|
196
|
+
` /// peer's \`${orderDirectiveId}\` \`toPayloadJson()\` produced). Reverse of the peer`,
|
|
197
|
+
` /// encode — types stop here, at the JSON serde.`,
|
|
198
|
+
` factory ${cls}.fromJson(Map<String, Object?> j) => ${cls}(`,
|
|
199
|
+
fromJsonAssigns,
|
|
200
|
+
` );`,
|
|
201
|
+
`}`,
|
|
202
|
+
].join("\n");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Emit the typed `XResult` (success) + `XFailure` classes the handler returns. */
|
|
206
|
+
function emitResultClasses(base: string, completeDirectiveId: string): string {
|
|
207
|
+
const resultCls = `${base}Result`;
|
|
208
|
+
const failureCls = `${base}Failure`;
|
|
209
|
+
return [
|
|
210
|
+
`/// The typed SUCCESS RESULT the \`${base}\` handler returns. Carries the content-addressed`,
|
|
211
|
+
`/// [resultRef] (the Evidence handle — the framework \`${completeDirectiveId}\` Receipt's`,
|
|
212
|
+
`/// authoritative \`resultRef\`). [toResultRef] encodes it for the Receipt; the framework`,
|
|
213
|
+
`/// Receipt records it verbatim and a tenant \`completeResult\` may ALSO derive a typed`,
|
|
214
|
+
`/// result field from it (e.g. a voice-note's \`transcription\`).`,
|
|
215
|
+
`class ${resultCls} implements TaskResult {`,
|
|
216
|
+
` /// The content-addressed result the side-effect produced (a blob ref / url / text`,
|
|
217
|
+
` /// captured as Evidence) — recorded verbatim as the Receipt's \`resultRef\`.`,
|
|
218
|
+
` final String resultRef;`,
|
|
219
|
+
` const ${resultCls}({required this.resultRef});`,
|
|
220
|
+
``,
|
|
221
|
+
` @override`,
|
|
222
|
+
` String toResultRef() => resultRef;`,
|
|
223
|
+
`}`,
|
|
224
|
+
``,
|
|
225
|
+
`/// The typed FAILURE the \`${base}\` handler returns — the recorded reason the side-effect`,
|
|
226
|
+
`/// failed. Maps to the framework \`fail…\` Receipt (status:failed + failureReason +`,
|
|
227
|
+
`/// closesOrder); a recorded failure is terminal, NOT a retry storm.`,
|
|
228
|
+
`class ${failureCls} {`,
|
|
229
|
+
` final String failureReason;`,
|
|
230
|
+
` const ${failureCls}({required this.failureReason});`,
|
|
231
|
+
`}`,
|
|
232
|
+
].join("\n");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Emit the handler typedef + the generated `XProvider` dispatcher. */
|
|
236
|
+
function emitProviderClass(
|
|
237
|
+
task: ImpureCapabilityDecl,
|
|
238
|
+
base: string,
|
|
239
|
+
): string {
|
|
240
|
+
const orderCls = `${base}Order`;
|
|
241
|
+
const resultCls = `${base}Result`;
|
|
242
|
+
const handlerTypedef = `${base}Handler`;
|
|
243
|
+
const providerCls = `${base}Provider`;
|
|
244
|
+
const completeCls = completePayloadClass(task.complete.id);
|
|
245
|
+
const failCls = failPayloadClass(task.fail.id);
|
|
246
|
+
const blockCls = blockPayloadClass(task.block.id);
|
|
247
|
+
const deadLetterCls = deadLetterPayloadClass(task.deadLetter.id);
|
|
248
|
+
return [
|
|
249
|
+
`/// The typed HANDLER the dev writes for the \`${base}\` capability: given the typed`,
|
|
250
|
+
`/// [${orderCls}], perform the side-effect and return a typed [TaskOutcome] — a`,
|
|
251
|
+
`/// [TaskSuccess]<[${resultCls}]>, [TaskFailure], [TaskBlocked], or [TaskDeadLetter]. NO raw JSON, NO directive id, NO`,
|
|
252
|
+
`/// Receipt construction: the dev writes pure typed business logic; the [${providerCls}]`,
|
|
253
|
+
`/// does the decode/encode round-trip.`,
|
|
254
|
+
`typedef ${handlerTypedef} = TaskOutcome<${resultCls}> Function(${orderCls} order);`,
|
|
255
|
+
``,
|
|
256
|
+
`/// The GENERATED dispatcher for the \`${base}\` capability — the symmetric twin of the`,
|
|
257
|
+
`/// peer's typed dispatch. Register a typed [${handlerTypedef}]; [handleOrder] DECODES an`,
|
|
258
|
+
`/// incoming Order intent JSON into [${orderCls}], runs the handler, and ENCODES the typed`,
|
|
259
|
+
`/// outcome into the ordinary RECEIPT intent: [${completeCls}], [${failCls}],`,
|
|
260
|
+
`/// [${blockCls}], or [${deadLetterCls}]. The returned [NomosIntent] ships through the SAME`,
|
|
261
|
+
`/// \`dispatch\` gate as any authored write (ONE-WRITE-PATH).`,
|
|
262
|
+
`class ${providerCls} implements TaskProvider<${orderCls}, ${resultCls}> {`,
|
|
263
|
+
` /// The dev's typed handler.`,
|
|
264
|
+
` final ${handlerTypedef} handler;`,
|
|
265
|
+
` const ${providerCls}(this.handler);`,
|
|
266
|
+
``,
|
|
267
|
+
` @override`,
|
|
268
|
+
` String get orderDirectiveId => '${task.order.id}';`,
|
|
269
|
+
` @override`,
|
|
270
|
+
` String get completeDirectiveId => '${task.complete.id}';`,
|
|
271
|
+
` @override`,
|
|
272
|
+
` String get failDirectiveId => '${task.fail.id}';`,
|
|
273
|
+
` @override`,
|
|
274
|
+
` String get blockDirectiveId => '${task.block.id}';`,
|
|
275
|
+
` @override`,
|
|
276
|
+
` String get deadLetterDirectiveId => '${task.deadLetter.id}';`,
|
|
277
|
+
``,
|
|
278
|
+
` @override`,
|
|
279
|
+
` NomosIntent handleOrder(String orderPayloadJson, {required HostClock now}) =>`,
|
|
280
|
+
` handleOrderMap(decodeOrderPayload(orderPayloadJson), now: now);`,
|
|
281
|
+
``,
|
|
282
|
+
` @override`,
|
|
283
|
+
` NomosIntent handleOrderMap(`,
|
|
284
|
+
` Map<String, Object?> orderPayload, {`,
|
|
285
|
+
` required HostClock now,`,
|
|
286
|
+
` }) {`,
|
|
287
|
+
` final order = ${orderCls}.fromJson(orderPayload);`,
|
|
288
|
+
` final outcome = handler(order);`,
|
|
289
|
+
` // ENCODE the outcome into the ordinary RECEIPT intent (reusing the peer-codegen Receipt`,
|
|
290
|
+
` // payload classes — same generated file). The Receipt addresses the SAME instance`,
|
|
291
|
+
` // the Order created (keyed by requestId), so closesOrder == requestId by`,
|
|
292
|
+
` // construction in the engine's \`.plan()\` — terminal-once is the kernel's gate.`,
|
|
293
|
+
` return switch (outcome) {`,
|
|
294
|
+
` TaskSuccess<${resultCls}>(:final result) => ${completeCls}(`,
|
|
295
|
+
` requestId: order.requestId,`,
|
|
296
|
+
` resultRef: result.toResultRef(),`,
|
|
297
|
+
` completedAt: now(),`,
|
|
298
|
+
` ),`,
|
|
299
|
+
` TaskFailure<${resultCls}>(:final failureReason) => ${failCls}(`,
|
|
300
|
+
` requestId: order.requestId,`,
|
|
301
|
+
` failureReason: failureReason,`,
|
|
302
|
+
` failedAt: now(),`,
|
|
303
|
+
` ),`,
|
|
304
|
+
` TaskBlocked<${resultCls}>(:final blockedReason, :final nextAction) => ${blockCls}(`,
|
|
305
|
+
` requestId: order.requestId,`,
|
|
306
|
+
` blockedReason: blockedReason,`,
|
|
307
|
+
` blockedAt: now(),`,
|
|
308
|
+
` nextAction: nextAction,`,
|
|
309
|
+
` ),`,
|
|
310
|
+
` TaskDeadLetter<${resultCls}>(:final deadLetterReason) => ${deadLetterCls}(`,
|
|
311
|
+
` requestId: order.requestId,`,
|
|
312
|
+
` deadLetterReason: deadLetterReason,`,
|
|
313
|
+
` deadLetteredAt: now(),`,
|
|
314
|
+
` ),`,
|
|
315
|
+
` };`,
|
|
316
|
+
` }`,
|
|
317
|
+
`}`,
|
|
318
|
+
].join("\n");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Emit the full provider SDK for ONE `impureCapability`: the typed Order/Result/Failure
|
|
323
|
+
* classes + the handler typedef + the dispatcher. `enumDartTypes` is the SAME map the
|
|
324
|
+
* domain's `collectEnums` produced (so an enum-typed order param references the
|
|
325
|
+
* already-emitted domain enum — NO duplicate enum decl).
|
|
326
|
+
*/
|
|
327
|
+
export function emitProviderTask(
|
|
328
|
+
task: ImpureCapabilityDecl,
|
|
329
|
+
enumDartTypes: Map<string, string>,
|
|
330
|
+
): string {
|
|
331
|
+
const base = taskBaseName(task.order.id);
|
|
332
|
+
const specs = introspectPayload(task.order.payloadSchema, enumDartTypes);
|
|
333
|
+
return [
|
|
334
|
+
`// ── provider SDK: ${base} (impureCapability '${task.aggregate.id}') ──`,
|
|
335
|
+
emitOrderClass(base, task.order.id, specs),
|
|
336
|
+
``,
|
|
337
|
+
emitResultClasses(base, task.complete.id),
|
|
338
|
+
``,
|
|
339
|
+
emitProviderClass(task, base),
|
|
340
|
+
].join("\n");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Emit the provider SDK block for a domain's `impureCapabilities`. Returns `""` when the
|
|
345
|
+
* domain declares none (ADDITIVE + omit-when-empty — a domain without impure capabilities is
|
|
346
|
+
* byte-identical to before this block existed). `toJsonExpr` is re-exported only to keep
|
|
347
|
+
* the peer/provider codegen symmetric in one module surface; it is not used here
|
|
348
|
+
* directly (the Receipt encode reuses the peer payload classes), so reference it to
|
|
349
|
+
* avoid an unused-import lint while documenting the symmetry.
|
|
350
|
+
*/
|
|
351
|
+
export function emitProviderSdk(
|
|
352
|
+
tasks: readonly ImpureCapabilityDecl[],
|
|
353
|
+
enumDartTypes: Map<string, string>,
|
|
354
|
+
): string {
|
|
355
|
+
if (tasks.length === 0) return "";
|
|
356
|
+
void toJsonExpr; // symmetry marker (see doc); the encode reuses the peer payload classes.
|
|
357
|
+
return tasks.map((t) => emitProviderTask(t, enumDartTypes)).join("\n\n");
|
|
358
|
+
}
|