@fabricorg/gen-conformance 1.0.5 → 1.0.7

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/dist/index.cjs CHANGED
@@ -1,341 +1,367 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key2 of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key2) && key2 !== except)
14
- __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- MODULE_CONFORMANCE_CHECKS: () => MODULE_CONFORMANCE_CHECKS,
24
- PHASE_1_CONFORMANCE_CHECKS: () => PHASE_1_CONFORMANCE_CHECKS,
25
- PLATFORM_CONFORMANCE_CHECKS: () => PLATFORM_CONFORMANCE_CHECKS,
26
- buildModuleConformancePlan: () => buildModuleConformancePlan,
27
- conformanceGenerator: () => conformanceGenerator,
28
- createCertificationResult: () => createCertificationResult,
29
- moduleConformanceChecks: () => moduleConformanceChecks
30
- });
31
- module.exports = __toCommonJS(index_exports);
32
- var import_compiler = require("@fabricorg/compiler");
33
- var MODULE_CONFORMANCE_CHECKS = [
34
- "fabric.idempotency.same-command-replay.v1",
35
- "fabric.idempotency.conflicting-payload.v1",
36
- "fabric.idempotency.concurrent-submission.v1",
37
- "fabric.authorization.governing-moment.v1",
38
- "fabric.authorization.offline-replay.v1",
39
- "fabric.events.compatibility.v1",
40
- "fabric.provenance.propagation.v1",
41
- "fabric.consumers.tolerant-envelope.v1"
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _fabricorg_compiler = require("@fabricorg/compiler");
3
+ //#region index.ts
4
+ /** Checks a single module's own behaviour, verifiable against a governed host. */
5
+ const MODULE_CONFORMANCE_CHECKS = [
6
+ "fabric.idempotency.same-command-replay.v1",
7
+ "fabric.idempotency.conflicting-payload.v1",
8
+ "fabric.idempotency.concurrent-submission.v1",
9
+ "fabric.authorization.governing-moment.v1",
10
+ "fabric.authorization.offline-replay.v1",
11
+ "fabric.events.compatibility.v1",
12
+ "fabric.provenance.propagation.v1",
13
+ "fabric.consumers.tolerant-envelope.v1"
42
14
  ];
43
- var PLATFORM_CONFORMANCE_CHECKS = [
44
- "fabric.documentation.parity.v1",
45
- "fabric.documentation.llms-contract.v1"
46
- ];
47
- var PHASE_1_CONFORMANCE_CHECKS = [...MODULE_CONFORMANCE_CHECKS, ...PLATFORM_CONFORMANCE_CHECKS];
15
+ /**
16
+ * Checks that belong to a repository rather than a module. They are certification
17
+ * evidence like any other check, but no per-module suite can produce them.
18
+ */
19
+ const PLATFORM_CONFORMANCE_CHECKS = ["fabric.documentation.parity.v1", "fabric.documentation.llms-contract.v1"];
20
+ const PHASE_1_CONFORMANCE_CHECKS = [...MODULE_CONFORMANCE_CHECKS, ...PLATFORM_CONFORMANCE_CHECKS];
48
21
  var ConformanceFailure = class extends Error {
49
- name = "ConformanceFailure";
22
+ name = "ConformanceFailure";
50
23
  };
51
24
  function expect(condition, message) {
52
- if (!condition) throw new ConformanceFailure(message);
25
+ if (!condition) throw new ConformanceFailure(message);
53
26
  }
54
- var idempotentActions = (plan) => plan.actions.filter((action) => action.idempotency !== "prohibited" && action.idempotent);
55
- var key = (label, action) => `conformance:${label}:${action.actionId}`;
56
- var conflictReason = (conflict) => conflict.code ?? conflict.message;
27
+ const idempotentActions = (plan) => plan.actions.filter((action) => action.idempotency !== "prohibited" && action.idempotent);
28
+ const key = (label, action) => `conformance:${label}:${action.actionId}`;
29
+ const conflictReason = (conflict) => conflict.code ?? conflict.message;
57
30
  async function conflictOf(run) {
58
- try {
59
- await run();
60
- return { thrown: false, message: "" };
61
- } catch (error) {
62
- const code = error.code;
63
- return { thrown: true, ...code ? { code } : {}, message: error instanceof Error ? error.message : String(error) };
64
- }
31
+ try {
32
+ await run();
33
+ return {
34
+ thrown: false,
35
+ message: ""
36
+ };
37
+ } catch (error) {
38
+ const code = error.code;
39
+ return {
40
+ thrown: true,
41
+ ...code ? { code } : {},
42
+ message: error instanceof Error ? error.message : String(error)
43
+ };
44
+ }
65
45
  }
46
+ /**
47
+ * Runs a check against every eligible action, not just the first. A module whose
48
+ * first action conforms and whose second does not must fail, or certification
49
+ * means nothing for the rest of the module.
50
+ */
66
51
  async function forEachAction(actions, reason, body) {
67
- if (actions.length === 0) return { status: "not-applicable", reason };
68
- const evidence = [];
69
- for (const action of actions) evidence.push(...await body(action));
70
- return { status: "passed", evidence };
52
+ if (actions.length === 0) return {
53
+ status: "not-applicable",
54
+ reason
55
+ };
56
+ const evidence = [];
57
+ for (const action of actions) evidence.push(...await body(action));
58
+ return {
59
+ status: "passed",
60
+ evidence
61
+ };
71
62
  }
72
63
  function moduleConformanceChecks() {
73
- return [
74
- {
75
- id: "fabric.idempotency.same-command-replay.v1",
76
- title: "replaying one command key with the same payload does not re-execute",
77
- run: (harness, plan) => forEachAction(idempotentActions(plan), "module declares no idempotent action", async (action) => {
78
- const input = { actionId: action.actionId, parameters: harness.parameters(action.actionId), idempotencyKey: key("replay", action) };
79
- const first = await harness.submit(input);
80
- const before = await harness.sideEffects?.(action.actionId);
81
- const second = await harness.submit(input);
82
- expect(first.actionInvocationId === second.actionInvocationId, `replay of ${action.actionId} produced a second invocation ${second.actionInvocationId} for key ${input.idempotencyKey}`);
83
- const after = await harness.sideEffects?.(action.actionId);
84
- if (before !== void 0 && after !== void 0) {
85
- expect(after === before, `replay of ${action.actionId} returned the original invocation but produced ${after - before} further effects; the handler ran again`);
86
- }
87
- return [`invocation:${first.actionInvocationId}`, `key:${input.idempotencyKey}`, `effects:${after ?? "unobserved"}`];
88
- })
89
- },
90
- {
91
- id: "fabric.idempotency.conflicting-payload.v1",
92
- title: "reusing a command key with an altered payload is refused",
93
- run: (harness, plan) => forEachAction(idempotentActions(plan), "module declares no idempotent action", async (action) => {
94
- const idempotencyKey = key("conflict", action);
95
- const parameters = harness.parameters(action.actionId);
96
- await harness.submit({ actionId: action.actionId, parameters, idempotencyKey });
97
- const conflict = await conflictOf(() => harness.submit({
98
- actionId: action.actionId,
99
- parameters: { ...parameters, "x-conformance-altered": true },
100
- idempotencyKey
101
- }));
102
- expect(conflict.thrown, `altered payload reusing key ${idempotencyKey} was accepted instead of refused`);
103
- expect(
104
- conflict.code === "IDEMPOTENCY_CONFLICT" || /idempotenc/i.test(conflict.message),
105
- `altered payload for ${action.actionId} was refused, but not as an idempotency conflict: ${conflict.message}`
106
- );
107
- return [`key:${idempotencyKey}`, `refusal:${conflictReason(conflict)}`];
108
- })
109
- },
110
- {
111
- id: "fabric.idempotency.concurrent-submission.v1",
112
- title: "concurrent identical submissions collapse to one invocation",
113
- run: (harness, plan) => forEachAction(idempotentActions(plan), "module declares no idempotent action", async (action) => {
114
- const input = { actionId: action.actionId, parameters: harness.parameters(action.actionId), idempotencyKey: key("concurrent", action) };
115
- const before = await harness.sideEffects?.(action.actionId);
116
- const results = await Promise.all([harness.submit(input), harness.submit(input), harness.submit(input)]);
117
- const ids = new Set(results.map((result) => result.actionInvocationId));
118
- expect(ids.size === 1, `concurrent submissions of ${action.actionId} produced ${ids.size} invocations: ${[...ids].join(", ")}`);
119
- const after = await harness.sideEffects?.(action.actionId);
120
- if (before !== void 0 && after !== void 0) {
121
- const executions = after - before;
122
- expect(executions <= 1, `${results.length} concurrent submissions of ${action.actionId} collapsed to one invocation but produced ${executions} executions' worth of effects`);
123
- }
124
- return [`invocation:${[...ids][0]}`, `attempts:${results.length}`, `effects:${after ?? "unobserved"}`];
125
- })
126
- },
127
- {
128
- id: "fabric.authorization.governing-moment.v1",
129
- title: "an authorization binding must match the action's declared governing moment",
130
- run: (harness, plan) => forEachAction(
131
- plan.actions.filter((action) => action.authorityMoment !== void 0),
132
- "module declares no action with an authority moment",
133
- async (action) => {
134
- const mismatched = action.authorityMoment === "capture" ? "execution" : "capture";
135
- const rejection = await conflictOf(() => harness.submit({
136
- actionId: action.actionId,
137
- parameters: harness.parameters(action.actionId),
138
- authorizationBinding: { governingMoment: mismatched, capturedAt: new Date(Date.now() - 6e4).toISOString() }
139
- }));
140
- expect(rejection.thrown, `a binding declaring governingMoment "${mismatched}" was accepted for ${action.actionId}, governed at "${action.authorityMoment}"`);
141
- expect(
142
- /governingmoment|governing moment/i.test(conflictReason(rejection)),
143
- `the binding for ${action.actionId} was refused, but not for its governing moment: ${conflictReason(rejection)}`
144
- );
145
- return [`action:${action.actionId}`, `declared:${action.authorityMoment}`, `refused:${mismatched}`];
146
- }
147
- )
148
- },
149
- {
150
- id: "fabric.authorization.offline-replay.v1",
151
- title: "an offline capture replayed after authority is withdrawn is refused into reconciliation",
152
- run: (harness, plan) => forEachAction(
153
- plan.actions.filter((action) => action.connectivity === "offline-capturable" && action.authorityMoment !== void 0 && action.authorityMoment !== "capture"),
154
- "module declares no offline-capturable action governed at execution",
155
- async (action) => {
156
- await harness.revokeAuthority(action.actionId);
157
- const replay = await harness.submit({
158
- actionId: action.actionId,
159
- parameters: harness.parameters(action.actionId),
160
- idempotencyKey: key("offline-replay", action),
161
- executionReason: "offline_replay"
162
- });
163
- expect(
164
- replay.status === "reconciliation_required",
165
- `replay of ${action.actionId} after authority withdrawal returned status "${replay.status}" instead of reconciliation_required`
166
- );
167
- expect(replay.reconciliation !== void 0, `reconciliation_required was returned for ${action.actionId} without a reconciliation outcome`);
168
- return [`action:${action.actionId}`, `reconciliation:${replay.reconciliation?.kind}`];
169
- }
170
- )
171
- },
172
- {
173
- id: "fabric.events.compatibility.v1",
174
- title: "every consumed event version is one the module still declares",
175
- async run(_harness, plan) {
176
- if (plan.views.length === 0) return { status: "not-applicable", reason: "module declares no views" };
177
- const declared = new Map(plan.eventTypes.map((event) => [event.eventType, event.schemaVersion]));
178
- const evidence = [];
179
- for (const view of plan.views) {
180
- for (const consumption of view.consumes) {
181
- const current = declared.get(consumption.eventType);
182
- if (current === void 0) continue;
183
- expect(
184
- consumption.schemaVersions.includes(current),
185
- `view ${view.name}@${view.version} consumes ${consumption.eventType} at [${consumption.schemaVersions.join(", ")}] but the module now emits version ${current}`
186
- );
187
- evidence.push(`${view.name}@${view.version}:${consumption.eventType}=${current}`);
188
- }
189
- }
190
- if (evidence.length === 0) return { status: "not-applicable", reason: "no view consumes an event this module declares" };
191
- return { status: "passed", evidence };
192
- }
193
- },
194
- {
195
- id: "fabric.provenance.propagation.v1",
196
- title: "submission provenance reaches the events the action emits",
197
- run: (harness, plan) => forEachAction(
198
- plan.actions.filter((action) => action.mutatesDomain),
199
- "module declares no domain-mutating action",
200
- async (action) => {
201
- const correlationId = `conformance-corr-${action.actionId}`;
202
- const submitted = await harness.submit({
203
- actionId: action.actionId,
204
- parameters: harness.parameters(action.actionId),
205
- correlationId,
206
- // Audit attributes are allowlisted per application, so provenance identity is asserted on correlation alone.
207
- provenance: { source: "system", correlationId }
208
- });
209
- const emitted = (await harness.events()).filter((event) => event.actionInvocationId === submitted.actionInvocationId);
210
- expect(emitted.length > 0, `action ${action.actionId} mutates domain but emitted no events`);
211
- for (const event of emitted) {
212
- expect(event.correlationId === correlationId, `event ${event.eventType} carried correlationId ${String(event.correlationId)} instead of ${correlationId}`);
213
- }
214
- return [`invocation:${submitted.actionInvocationId}`, `events:${emitted.length}`, `correlationId:${correlationId}`];
215
- }
216
- )
217
- },
218
- {
219
- id: "fabric.consumers.tolerant-envelope.v1",
220
- title: "emitted envelopes are complete, and a consumer tolerates fields it does not know",
221
- run: (harness, plan) => forEachAction(
222
- plan.actions.filter((action) => action.mutatesDomain),
223
- "module declares no domain-mutating action",
224
- async (action) => {
225
- const submitted = await harness.submit({
226
- actionId: action.actionId,
227
- parameters: harness.parameters(action.actionId),
228
- idempotencyKey: key("envelope", action)
229
- });
230
- const emitted = (await harness.events()).filter((event) => event.actionInvocationId === submitted.actionInvocationId);
231
- expect(emitted.length > 0, `action ${action.actionId} mutates domain but emitted no events`);
232
- const required = ["eventType", "tenantId", "spaceId", "subjectType", "subjectId", "sequence", "correlationId"];
233
- for (const event of emitted) {
234
- for (const field of required) {
235
- expect(event[field] !== void 0 && event[field] !== null, `event ${event.eventType} omits the canonical envelope field "${field}"`);
236
- }
237
- }
238
- const evidence = emitted.map((event) => `${event.eventType}@${event.sequence}`);
239
- if (!harness.acceptsEnvelope) return [...evidence, "consumer:unobserved"];
240
- for (const event of emitted) {
241
- const extended = { ...event, "x-conformance-unknown": true, consistency: "provisional-until-reconciled" };
242
- const accepted = await harness.acceptsEnvelope(extended);
243
- expect(accepted, `a consumer rejected ${event.eventType} once it carried a field it did not recognise; additive event changes would break it`);
244
- }
245
- return [...evidence, `consumer:tolerated:${emitted.length}`];
246
- }
247
- )
248
- }
249
- ];
64
+ return [
65
+ {
66
+ id: "fabric.idempotency.same-command-replay.v1",
67
+ title: "replaying one command key with the same payload does not re-execute",
68
+ run: (harness, plan) => forEachAction(idempotentActions(plan), "module declares no idempotent action", async (action) => {
69
+ const input = {
70
+ actionId: action.actionId,
71
+ parameters: harness.parameters(action.actionId),
72
+ idempotencyKey: key("replay", action)
73
+ };
74
+ const first = await harness.submit(input);
75
+ const before = await harness.sideEffects?.(action.actionId);
76
+ const second = await harness.submit(input);
77
+ expect(first.actionInvocationId === second.actionInvocationId, `replay of ${action.actionId} produced a second invocation ${second.actionInvocationId} for key ${input.idempotencyKey}`);
78
+ const after = await harness.sideEffects?.(action.actionId);
79
+ if (before !== void 0 && after !== void 0) expect(after === before, `replay of ${action.actionId} returned the original invocation but produced ${after - before} further effects; the handler ran again`);
80
+ return [
81
+ `invocation:${first.actionInvocationId}`,
82
+ `key:${input.idempotencyKey}`,
83
+ `effects:${after ?? "unobserved"}`
84
+ ];
85
+ })
86
+ },
87
+ {
88
+ id: "fabric.idempotency.conflicting-payload.v1",
89
+ title: "reusing a command key with an altered payload is refused",
90
+ run: (harness, plan) => forEachAction(idempotentActions(plan), "module declares no idempotent action", async (action) => {
91
+ const idempotencyKey = key("conflict", action);
92
+ const parameters = harness.parameters(action.actionId);
93
+ await harness.submit({
94
+ actionId: action.actionId,
95
+ parameters,
96
+ idempotencyKey
97
+ });
98
+ const conflict = await conflictOf(() => harness.submit({
99
+ actionId: action.actionId,
100
+ parameters: {
101
+ ...parameters,
102
+ "x-conformance-altered": true
103
+ },
104
+ idempotencyKey
105
+ }));
106
+ expect(conflict.thrown, `altered payload reusing key ${idempotencyKey} was accepted instead of refused`);
107
+ expect(conflict.code === "IDEMPOTENCY_CONFLICT" || /idempotenc/i.test(conflict.message), `altered payload for ${action.actionId} was refused, but not as an idempotency conflict: ${conflict.message}`);
108
+ return [`key:${idempotencyKey}`, `refusal:${conflictReason(conflict)}`];
109
+ })
110
+ },
111
+ {
112
+ id: "fabric.idempotency.concurrent-submission.v1",
113
+ title: "concurrent identical submissions collapse to one invocation",
114
+ run: (harness, plan) => forEachAction(idempotentActions(plan), "module declares no idempotent action", async (action) => {
115
+ const input = {
116
+ actionId: action.actionId,
117
+ parameters: harness.parameters(action.actionId),
118
+ idempotencyKey: key("concurrent", action)
119
+ };
120
+ const before = await harness.sideEffects?.(action.actionId);
121
+ const results = await Promise.all([
122
+ harness.submit(input),
123
+ harness.submit(input),
124
+ harness.submit(input)
125
+ ]);
126
+ const ids = new Set(results.map((result) => result.actionInvocationId));
127
+ expect(ids.size === 1, `concurrent submissions of ${action.actionId} produced ${ids.size} invocations: ${[...ids].join(", ")}`);
128
+ const after = await harness.sideEffects?.(action.actionId);
129
+ if (before !== void 0 && after !== void 0) {
130
+ const executions = after - before;
131
+ expect(executions <= 1, `${results.length} concurrent submissions of ${action.actionId} collapsed to one invocation but produced ${executions} executions' worth of effects`);
132
+ }
133
+ return [
134
+ `invocation:${[...ids][0]}`,
135
+ `attempts:${results.length}`,
136
+ `effects:${after ?? "unobserved"}`
137
+ ];
138
+ })
139
+ },
140
+ {
141
+ id: "fabric.authorization.governing-moment.v1",
142
+ title: "an authorization binding must match the action's declared governing moment",
143
+ run: (harness, plan) => forEachAction(plan.actions.filter((action) => action.authorityMoment !== void 0), "module declares no action with an authority moment", async (action) => {
144
+ const mismatched = action.authorityMoment === "capture" ? "execution" : "capture";
145
+ const rejection = await conflictOf(() => harness.submit({
146
+ actionId: action.actionId,
147
+ parameters: harness.parameters(action.actionId),
148
+ authorizationBinding: {
149
+ governingMoment: mismatched,
150
+ capturedAt: (/* @__PURE__ */ new Date(Date.now() - 6e4)).toISOString()
151
+ }
152
+ }));
153
+ expect(rejection.thrown, `a binding declaring governingMoment "${mismatched}" was accepted for ${action.actionId}, governed at "${action.authorityMoment}"`);
154
+ expect(/governingmoment|governing moment/i.test(conflictReason(rejection)), `the binding for ${action.actionId} was refused, but not for its governing moment: ${conflictReason(rejection)}`);
155
+ return [
156
+ `action:${action.actionId}`,
157
+ `declared:${action.authorityMoment}`,
158
+ `refused:${mismatched}`
159
+ ];
160
+ })
161
+ },
162
+ {
163
+ id: "fabric.authorization.offline-replay.v1",
164
+ title: "an offline capture replayed after authority is withdrawn is refused into reconciliation",
165
+ run: (harness, plan) => forEachAction(plan.actions.filter((action) => action.connectivity === "offline-capturable" && action.authorityMoment !== void 0 && action.authorityMoment !== "capture"), "module declares no offline-capturable action governed at execution", async (action) => {
166
+ await harness.revokeAuthority(action.actionId);
167
+ const replay = await harness.submit({
168
+ actionId: action.actionId,
169
+ parameters: harness.parameters(action.actionId),
170
+ idempotencyKey: key("offline-replay", action),
171
+ executionReason: "offline_replay"
172
+ });
173
+ expect(replay.status === "reconciliation_required", `replay of ${action.actionId} after authority withdrawal returned status "${replay.status}" instead of reconciliation_required`);
174
+ expect(replay.reconciliation !== void 0, `reconciliation_required was returned for ${action.actionId} without a reconciliation outcome`);
175
+ return [`action:${action.actionId}`, `reconciliation:${replay.reconciliation?.kind}`];
176
+ })
177
+ },
178
+ {
179
+ id: "fabric.events.compatibility.v1",
180
+ title: "every consumed event version is one the module still declares",
181
+ async run(_harness, plan) {
182
+ if (plan.views.length === 0) return {
183
+ status: "not-applicable",
184
+ reason: "module declares no views"
185
+ };
186
+ const declared = new Map(plan.eventTypes.map((event) => [event.eventType, event.schemaVersion]));
187
+ const evidence = [];
188
+ for (const view of plan.views) for (const consumption of view.consumes) {
189
+ const current = declared.get(consumption.eventType);
190
+ if (current === void 0) continue;
191
+ expect(consumption.schemaVersions.includes(current), `view ${view.name}@${view.version} consumes ${consumption.eventType} at [${consumption.schemaVersions.join(", ")}] but the module now emits version ${current}`);
192
+ evidence.push(`${view.name}@${view.version}:${consumption.eventType}=${current}`);
193
+ }
194
+ if (evidence.length === 0) return {
195
+ status: "not-applicable",
196
+ reason: "no view consumes an event this module declares"
197
+ };
198
+ return {
199
+ status: "passed",
200
+ evidence
201
+ };
202
+ }
203
+ },
204
+ {
205
+ id: "fabric.provenance.propagation.v1",
206
+ title: "submission provenance reaches the events the action emits",
207
+ run: (harness, plan) => forEachAction(plan.actions.filter((action) => action.mutatesDomain), "module declares no domain-mutating action", async (action) => {
208
+ const correlationId = `conformance-corr-${action.actionId}`;
209
+ const submitted = await harness.submit({
210
+ actionId: action.actionId,
211
+ parameters: harness.parameters(action.actionId),
212
+ correlationId,
213
+ provenance: {
214
+ source: "system",
215
+ correlationId
216
+ }
217
+ });
218
+ const emitted = (await harness.events()).filter((event) => event.actionInvocationId === submitted.actionInvocationId);
219
+ expect(emitted.length > 0, `action ${action.actionId} mutates domain but emitted no events`);
220
+ for (const event of emitted) expect(event.correlationId === correlationId, `event ${event.eventType} carried correlationId ${String(event.correlationId)} instead of ${correlationId}`);
221
+ return [
222
+ `invocation:${submitted.actionInvocationId}`,
223
+ `events:${emitted.length}`,
224
+ `correlationId:${correlationId}`
225
+ ];
226
+ })
227
+ },
228
+ {
229
+ id: "fabric.consumers.tolerant-envelope.v1",
230
+ title: "emitted envelopes are complete, and a consumer tolerates fields it does not know",
231
+ run: (harness, plan) => forEachAction(plan.actions.filter((action) => action.mutatesDomain), "module declares no domain-mutating action", async (action) => {
232
+ const submitted = await harness.submit({
233
+ actionId: action.actionId,
234
+ parameters: harness.parameters(action.actionId),
235
+ idempotencyKey: key("envelope", action)
236
+ });
237
+ const emitted = (await harness.events()).filter((event) => event.actionInvocationId === submitted.actionInvocationId);
238
+ expect(emitted.length > 0, `action ${action.actionId} mutates domain but emitted no events`);
239
+ const required = [
240
+ "eventType",
241
+ "tenantId",
242
+ "spaceId",
243
+ "subjectType",
244
+ "subjectId",
245
+ "sequence",
246
+ "correlationId"
247
+ ];
248
+ for (const event of emitted) for (const field of required) expect(event[field] !== void 0 && event[field] !== null, `event ${event.eventType} omits the canonical envelope field "${field}"`);
249
+ const evidence = emitted.map((event) => `${event.eventType}@${event.sequence}`);
250
+ if (!harness.acceptsEnvelope) return [...evidence, "consumer:unobserved"];
251
+ for (const event of emitted) {
252
+ const extended = {
253
+ ...event,
254
+ "x-conformance-unknown": true,
255
+ consistency: "provisional-until-reconciled"
256
+ };
257
+ expect(await harness.acceptsEnvelope(extended), `a consumer rejected ${event.eventType} once it carried a field it did not recognise; additive event changes would break it`);
258
+ }
259
+ return [...evidence, `consumer:tolerated:${emitted.length}`];
260
+ })
261
+ }
262
+ ];
250
263
  }
251
264
  function createCertificationResult(input) {
252
- const results = new Map(input.checks.map((check) => [check.id, check]));
253
- if (results.size !== input.checks.length) throw new Error("Certification evidence contains duplicate check identifiers.");
254
- const missing = PHASE_1_CONFORMANCE_CHECKS.filter((id) => !results.has(id));
255
- if (missing.length) throw new Error(`Certification evidence is missing mandatory checks: ${missing.join(", ")}`);
256
- if (!/^[0-9a-f]{64}$/.test(input.manifestDigest)) throw new Error("Certification manifestDigest must be a lowercase SHA-256 digest.");
257
- const generatorDigests = Object.entries(input.generatorDigests);
258
- if (!generatorDigests.length || generatorDigests.some(([, digest]) => !/^[0-9a-f]{64}$/.test(digest))) throw new Error("Certification generatorDigests must contain SHA-256 evidence.");
259
- if (input.checks.some((check) => check.status === "passed" && check.evidence.length === 0)) throw new Error("Passed certification checks require evidence references.");
260
- return { ...input, formatVersion: 1, checks: [...input.checks].sort((a, b) => a.id.localeCompare(b.id)), passed: input.checks.every((check) => check.status === "passed") };
265
+ const results = new Map(input.checks.map((check) => [check.id, check]));
266
+ if (results.size !== input.checks.length) throw new Error("Certification evidence contains duplicate check identifiers.");
267
+ const missing = PHASE_1_CONFORMANCE_CHECKS.filter((id) => !results.has(id));
268
+ if (missing.length) throw new Error(`Certification evidence is missing mandatory checks: ${missing.join(", ")}`);
269
+ if (!/^[0-9a-f]{64}$/.test(input.manifestDigest)) throw new Error("Certification manifestDigest must be a lowercase SHA-256 digest.");
270
+ const generatorDigests = Object.entries(input.generatorDigests);
271
+ if (!generatorDigests.length || generatorDigests.some(([, digest]) => !/^[0-9a-f]{64}$/.test(digest))) throw new Error("Certification generatorDigests must contain SHA-256 evidence.");
272
+ if (input.checks.some((check) => check.status === "passed" && check.evidence.length === 0)) throw new Error("Passed certification checks require evidence references.");
273
+ return {
274
+ ...input,
275
+ formatVersion: 1,
276
+ checks: [...input.checks].sort((a, b) => a.id.localeCompare(b.id)),
277
+ passed: input.checks.every((check) => check.status === "passed")
278
+ };
261
279
  }
262
- function buildModuleConformancePlan(module2, manifestDigest) {
263
- return {
264
- formatVersion: 1,
265
- module: module2.namespace,
266
- manifestDigest,
267
- requiredChecks: MODULE_CONFORMANCE_CHECKS,
268
- platformChecks: PLATFORM_CONFORMANCE_CHECKS,
269
- actions: module2.actions.map((action) => ({
270
- actionId: action.actionId,
271
- version: action.version,
272
- mutatesDomain: action.mutatesDomain,
273
- idempotent: action.idempotent,
274
- ...action.execution?.idempotency ? { idempotency: action.execution.idempotency } : {},
275
- ...action.execution?.authorityMoment ? { authorityMoment: action.execution.authorityMoment } : {},
276
- ...action.execution?.connectivity ? { connectivity: action.execution.connectivity } : {},
277
- ...action.execution?.consistency ? { consistency: action.execution.consistency } : {}
278
- })),
279
- views: module2.views.map((view) => ({
280
- name: view.name,
281
- version: view.version,
282
- ...view.parameterSchema ? { parameterSchema: view.parameterSchema } : {},
283
- consumes: view.consumes.map((consumption) => ({ eventType: consumption.eventType, schemaVersions: [...consumption.schemaVersions] }))
284
- })),
285
- eventTypes: module2.eventTypes.map((event) => ({ eventType: event.eventType, schemaVersion: event.schemaVersion }))
286
- };
280
+ function buildModuleConformancePlan(module, manifestDigest) {
281
+ return {
282
+ formatVersion: 1,
283
+ module: module.namespace,
284
+ manifestDigest,
285
+ requiredChecks: MODULE_CONFORMANCE_CHECKS,
286
+ platformChecks: PLATFORM_CONFORMANCE_CHECKS,
287
+ actions: module.actions.map((action) => ({
288
+ actionId: action.actionId,
289
+ version: action.version,
290
+ mutatesDomain: action.mutatesDomain,
291
+ idempotent: action.idempotent,
292
+ ...action.execution?.idempotency ? { idempotency: action.execution.idempotency } : {},
293
+ ...action.execution?.authorityMoment ? { authorityMoment: action.execution.authorityMoment } : {},
294
+ ...action.execution?.connectivity ? { connectivity: action.execution.connectivity } : {},
295
+ ...action.execution?.consistency ? { consistency: action.execution.consistency } : {}
296
+ })),
297
+ views: module.views.map((view) => ({
298
+ name: view.name,
299
+ version: view.version,
300
+ ...view.parameterSchema ? { parameterSchema: view.parameterSchema } : {},
301
+ consumes: view.consumes.map((consumption) => ({
302
+ eventType: consumption.eventType,
303
+ schemaVersions: [...consumption.schemaVersions]
304
+ }))
305
+ })),
306
+ eventTypes: module.eventTypes.map((event) => ({
307
+ eventType: event.eventType,
308
+ schemaVersion: event.schemaVersion
309
+ }))
310
+ };
287
311
  }
288
312
  function suiteSource(plan, generator) {
289
- return [
290
- `// Generated by ${generator.name}@${generator.version}. Do not edit.`,
291
- `// Manifest digest: ${plan.manifestDigest}`,
292
- `// Supply ./conformance.harness with a createConformanceHarness() factory returning a fresh, isolated harness.`,
293
- 'import { expect, it } from "vitest";',
294
- 'import { moduleConformanceChecks, type ModuleConformancePlan } from "@fabricorg/gen-conformance";',
295
- 'import { createConformanceHarness } from "./conformance.harness";',
296
- "",
297
- `export const plan: ModuleConformancePlan = ${JSON.stringify(plan, null, 2)};`,
298
- "",
299
- "for (const check of moduleConformanceChecks()) {",
300
- " it(`${check.id} \u2014 ${check.title}`, async () => {",
301
- " const outcome = await check.run(await createConformanceHarness(), plan);",
302
- ' expect(["passed", "not-applicable"]).toContain(outcome.status);',
303
- " });",
304
- "}",
305
- ""
306
- ].join("\n");
313
+ return [
314
+ `// Generated by ${generator.name}@${generator.version}. Do not edit.`,
315
+ `// Manifest digest: ${plan.manifestDigest}`,
316
+ `// Supply ./conformance.harness with a createConformanceHarness() factory returning a fresh, isolated harness.`,
317
+ "import { expect, it } from \"vitest\";",
318
+ "import { moduleConformanceChecks, type ModuleConformancePlan } from \"@fabricorg/gen-conformance\";",
319
+ "import { createConformanceHarness } from \"./conformance.harness\";",
320
+ "",
321
+ `export const plan: ModuleConformancePlan = ${JSON.stringify(plan, null, 2)};`,
322
+ "",
323
+ "for (const check of moduleConformanceChecks()) {",
324
+ " it(`${check.id} — ${check.title}`, async () => {",
325
+ " const outcome = await check.run(await createConformanceHarness(), plan);",
326
+ " expect([\"passed\", \"not-applicable\"]).toContain(outcome.status);",
327
+ " });",
328
+ "}",
329
+ ""
330
+ ].join("\n");
307
331
  }
308
- var conformanceGenerator = {
309
- name: "@fabricorg/gen-conformance",
310
- version: "1.0.5",
311
- generate(ir) {
312
- return ir.modules.flatMap((module2) => {
313
- const plan = buildModuleConformancePlan(module2, ir.manifestDigest);
314
- return [
315
- {
316
- path: `${module2.namespace}/conformance-plan.json`,
317
- content: (0, import_compiler.stableStringify)({
318
- ...plan,
319
- generator: { name: conformanceGenerator.name, version: conformanceGenerator.version },
320
- compilerVersion: ir.compilerVersion
321
- })
322
- },
323
- {
324
- path: `${module2.namespace}/conformance.test.ts`,
325
- content: suiteSource(plan, { name: conformanceGenerator.name, version: conformanceGenerator.version })
326
- }
327
- ];
328
- });
329
- }
332
+ const conformanceGenerator = {
333
+ name: "@fabricorg/gen-conformance",
334
+ version: "1.0.7",
335
+ generate(ir) {
336
+ return ir.modules.flatMap((module) => {
337
+ const plan = buildModuleConformancePlan(module, ir.manifestDigest);
338
+ return [{
339
+ path: `${module.namespace}/conformance-plan.json`,
340
+ content: (0, _fabricorg_compiler.stableStringify)({
341
+ ...plan,
342
+ generator: {
343
+ name: conformanceGenerator.name,
344
+ version: conformanceGenerator.version
345
+ },
346
+ compilerVersion: ir.compilerVersion
347
+ })
348
+ }, {
349
+ path: `${module.namespace}/conformance.test.ts`,
350
+ content: suiteSource(plan, {
351
+ name: conformanceGenerator.name,
352
+ version: conformanceGenerator.version
353
+ })
354
+ }];
355
+ });
356
+ }
330
357
  };
331
- // Annotate the CommonJS export names for ESM import in node:
332
- 0 && (module.exports = {
333
- MODULE_CONFORMANCE_CHECKS,
334
- PHASE_1_CONFORMANCE_CHECKS,
335
- PLATFORM_CONFORMANCE_CHECKS,
336
- buildModuleConformancePlan,
337
- conformanceGenerator,
338
- createCertificationResult,
339
- moduleConformanceChecks
340
- });
358
+ //#endregion
359
+ exports.MODULE_CONFORMANCE_CHECKS = MODULE_CONFORMANCE_CHECKS;
360
+ exports.PHASE_1_CONFORMANCE_CHECKS = PHASE_1_CONFORMANCE_CHECKS;
361
+ exports.PLATFORM_CONFORMANCE_CHECKS = PLATFORM_CONFORMANCE_CHECKS;
362
+ exports.buildModuleConformancePlan = buildModuleConformancePlan;
363
+ exports.conformanceGenerator = conformanceGenerator;
364
+ exports.createCertificationResult = createCertificationResult;
365
+ exports.moduleConformanceChecks = moduleConformanceChecks;
366
+
341
367
  //# sourceMappingURL=index.cjs.map