@vizejs/marquette 0.347.7 → 0.351.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/README.md +6 -4
- package/dist/adapter.d.mts +5 -1
- package/dist/adapter.d.mts.map +1 -1
- package/dist/adapter.mjs +10 -0
- package/dist/adapter.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/{model-DWcxWbC1.d.mts → model-B_mM8AeH.d.mts} +1 -1
- package/dist/model-B_mM8AeH.d.mts.map +1 -0
- package/dist/test-run-admission.d.mts +2 -2
- package/dist/test-run-admission.d.mts.map +1 -1
- package/dist/test-run-admission.mjs +1 -1
- package/dist/test-run-admission.mjs.map +1 -1
- package/dist/{test-run-canonical-CB-KzZxk.d.mts → test-run-canonical-B1IuzC5s.d.mts} +2 -2
- package/dist/test-run-canonical-B1IuzC5s.d.mts.map +1 -0
- package/dist/test-run-canonical.d.mts +1 -1
- package/dist/test-run-canonical.mjs.map +1 -1
- package/dist/test-run-check.d.mts +1 -1
- package/dist/test-run-check.d.mts.map +1 -1
- package/dist/test-run-check.mjs +1 -1
- package/dist/test-run-check.mjs.map +1 -1
- package/dist/{test-run-model-DkllmEsY.d.mts → test-run-model-CF2EYJOQ.d.mts} +1 -1
- package/dist/test-run-model-CF2EYJOQ.d.mts.map +1 -0
- package/dist/{test-run-model-B2INFVlw.mjs → test-run-model-DgJ2nd5p.mjs} +1 -1
- package/dist/{test-run-model-B2INFVlw.mjs.map → test-run-model-DgJ2nd5p.mjs.map} +1 -1
- package/dist/test-run-transition.d.mts.map +1 -1
- package/dist/test-run-transition.mjs +1 -1
- package/dist/test-run-transition.mjs.map +1 -1
- package/dist/{test-run-validate-C_KR031E.mjs → test-run-validate-DwGMo8ON.mjs} +2 -2
- package/dist/{test-run-validate-C_KR031E.mjs.map → test-run-validate-DwGMo8ON.mjs.map} +1 -1
- package/dist/test-run-validate.d.mts +1 -1
- package/dist/test-run-validate.d.mts.map +1 -1
- package/dist/test-run-validate.mjs +1 -1
- package/dist/test-run.d.mts +1 -1
- package/dist/test-run.d.mts.map +1 -1
- package/dist/test-run.mjs +1 -1
- package/dist/test-run.mjs.map +1 -1
- package/dist/validate.d.mts +1 -1
- package/dist/validate.d.mts.map +1 -1
- package/dist/validate.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/model-DWcxWbC1.d.mts.map +0 -1
- package/dist/test-run-canonical-CB-KzZxk.d.mts.map +0 -1
- package/dist/test-run-model-DkllmEsY.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -122,10 +122,12 @@ if (!result.compatible) {
|
|
|
122
122
|
Exact and inclusive-bound matches are compatible. Missing support,
|
|
123
123
|
requirements below an adapter's minimum, and requirements above its maximum
|
|
124
124
|
use the stable `missing-capability`, `version-below-minimum`, and
|
|
125
|
-
`version-above-maximum` codes.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
`version-above-maximum` codes. Each mismatch also carries a deterministic
|
|
126
|
+
`path` and `message`, so native adapter diagnostics can be rendered without
|
|
127
|
+
target-specific string formatting. Adding a support range or widening either
|
|
128
|
+
bound is additive; removing support or narrowing either bound is breaking.
|
|
129
|
+
Both negotiation and compatibility reports are sorted deterministically and do
|
|
130
|
+
not mutate their inputs. Compatibility reports expose validation diagnostics for
|
|
129
131
|
both manifests and omit changes when either input is invalid. The published manifest schema is available from
|
|
130
132
|
`@vizejs/marquette/adapter/schema`.
|
|
131
133
|
|
package/dist/adapter.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ApplicationMarquette } from "./model-
|
|
1
|
+
import { t as ApplicationMarquette } from "./model-B_mM8AeH.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/adapter-model.d.ts
|
|
4
4
|
/** Current serialized adapter-capability manifest format. */
|
|
@@ -48,6 +48,10 @@ interface AdapterCapabilityMismatch {
|
|
|
48
48
|
readonly code: AdapterCapabilityMismatchCode;
|
|
49
49
|
/** Required capability identifier. */
|
|
50
50
|
readonly capability: string;
|
|
51
|
+
/** JSON-style path of the unsupported application capability requirement. */
|
|
52
|
+
readonly path: string;
|
|
53
|
+
/** Human-readable explanation with stable wording for renderer diagnostics. */
|
|
54
|
+
readonly message: string;
|
|
51
55
|
/** Required contract version when the capability is declared. */
|
|
52
56
|
readonly requiredVersion?: number;
|
|
53
57
|
/** Adapter minimum when support exists. */
|
package/dist/adapter.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.mts","names":[],"sources":["../src/adapter-model.ts","../src/adapter-native-profile.ts","../src/adapter-compatibility.ts","../src/adapter-negotiate.ts","../src/adapter-validate.ts"],"mappings":";;;;cACa,iCAAA;;UAGI,wBAAA;EAH0C;EAAA,SAKhD,EAAA;EALgD;EAAA,SAOhD,UAAA;EAJM;EAAA,SAMN,UAAA;AAAA;;UAIM,yBAAA;EANN
|
|
1
|
+
{"version":3,"file":"adapter.d.mts","names":[],"sources":["../src/adapter-model.ts","../src/adapter-native-profile.ts","../src/adapter-compatibility.ts","../src/adapter-negotiate.ts","../src/adapter-validate.ts"],"mappings":";;;;cACa,iCAAA;;UAGI,wBAAA;EAH0C;EAAA,SAKhD,EAAA;EALgD;EAAA,SAOhD,UAAA;EAJM;EAAA,SAMN,UAAA;AAAA;;UAIM,yBAAA;EANN;;;AAEU;AAIrB;EANW,SAYA,aAAA,UAAuB,iCAAA;;WAEvB,OAAA;EAFA;;;;;EAAA,SAQA,YAAA,YAAwB,wBAAwB;AAAA;AAI3D;AAAA,KAAY,+BAAA;;UASK,2BAAA;EAT0B;EAAA,SAWhC,IAAA,EAAM,+BAA+B;EAFJ;EAAA,SAIjC,IAAA;EAFqC;EAAA,SAIrC,OAAA;AAAA;;KAIC,6BAAA;;UAOK,yBAAA;EAPL;EAAA,SASD,IAAA,EAAM,6BAA6B;;WAEnC,UAAA;EAX8B;EAAA,SAa9B,IAAA;EAN+B;EAAA,SAQ/B,OAAA;EANmC;EAAA,SAQnC,eAAA;EARM;EAAA,SAUN,UAAA;EANA;EAAA,SAQA,UAAA;AAAA;;UAIM,4BAAA;EAJI;EAAA,SAMV,OAAA;EAFM;EAAA,SAIN,UAAA;;WAEA,WAAA,WAAsB,2BAAA;EAJtB;EAAA,SAMA,UAAA,WAAqB,yBAAyB;AAAA;;KAI7C,uBAAA;;UAGK,oCAAA;EAPwC;EAAA,SAS9C,IAAA,EAAM,uBAAuB;EALL;EAAA,SAOxB,IAAA;EAPwB;EAAA,SASxB,OAAA;AAAA;;UAIM,oCAAA;EARuB;EAAA,SAU7B,mBAAA,WAA8B,2BAAA;EAVxB;EAAA,SAYN,eAAA,WAA0B,2BAAA;EAR1B;EAAA,SAUA,OAAA,WAAkB,oCAAA;AAAA;;;;cCxGhB,gCAAA;ADFb;AAAA,cCKa,4BAAA;;KAYD,wBAAA,WAAmC,4BAA4B;ADjBhB;AAG3D;;;;;AAH2D,iBCyB3C,6BAAA,aAA0C,wBAAwB;;;;;ADzBlF;;;;iBEcgB,0BAAA,CACd,QAAA,EAAU,yBAAA,EACV,IAAA,EAAM,yBAAA,GACL,oCAAA;;;;AFjBH;;;;AAA2D;AAG3D;iBGagB,4BAAA,CACd,SAAA,EAAW,oBAAA,EACX,oBAAA,qBACA,QAAA,EAAU,yBAAA,GACT,4BAAA;;;;iBCHa,iCAAA,CACd,QAAA,EAAU,yBAAA,GACT,2BAA2B;AJnB9B;;;;AAA2D;AAG3D;;AAHA,iBI0GgB,8BAAA,CAA+B,KAAA,YAAiB,yBAAyB"}
|
package/dist/adapter.mjs
CHANGED
|
@@ -190,6 +190,8 @@ function problem(code, capability, requiredVersion, support) {
|
|
|
190
190
|
return {
|
|
191
191
|
code,
|
|
192
192
|
capability,
|
|
193
|
+
path: `capabilities.${capability}`,
|
|
194
|
+
message: mismatchMessage(code),
|
|
193
195
|
...requiredVersion === void 0 ? {} : { requiredVersion },
|
|
194
196
|
...support === void 0 ? {} : {
|
|
195
197
|
minVersion: support.minVersion,
|
|
@@ -197,6 +199,14 @@ function problem(code, capability, requiredVersion, support) {
|
|
|
197
199
|
}
|
|
198
200
|
};
|
|
199
201
|
}
|
|
202
|
+
function mismatchMessage(code) {
|
|
203
|
+
switch (code) {
|
|
204
|
+
case "unknown-requirement": return "application references an undeclared capability requirement";
|
|
205
|
+
case "missing-capability": return "adapter does not support the required capability";
|
|
206
|
+
case "version-below-minimum": return "application requires a capability version below the adapter minimum";
|
|
207
|
+
case "version-above-maximum": return "application requires a capability version above the adapter maximum";
|
|
208
|
+
}
|
|
209
|
+
}
|
|
200
210
|
//#endregion
|
|
201
211
|
export { ADAPTER_CAPABILITY_FORMAT_VERSION, NATIVE_ENGINE_CAPABILITY_IDS, NATIVE_ENGINE_CAPABILITY_VERSION, compareAdapterCapabilities, nativeEngineCapabilityProfile, negotiateAdapterCapabilities, parseAdapterCapabilityManifest, validateAdapterCapabilityManifest };
|
|
202
212
|
|
package/dist/adapter.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.mjs","names":["compareText"],"sources":["../src/adapter-model.ts","../src/adapter-native-profile.ts","../src/adapter-validate.ts","../src/adapter-compatibility.ts","../src/adapter-negotiate.ts"],"sourcesContent":["/** Current serialized adapter-capability manifest format. */\nexport const ADAPTER_CAPABILITY_FORMAT_VERSION = 1 as const;\n\n/** Inclusive version range supported for one capability contract. */\nexport interface AdapterCapabilitySupport {\n /** Stable capability identifier from an application marquette. */\n readonly id: string;\n /** Oldest supported capability contract version, inclusive. */\n readonly minVersion: number;\n /** Newest supported capability contract version, inclusive. */\n readonly maxVersion: number;\n}\n\n/** Versioned, language-neutral capabilities offered by one adapter. */\nexport interface AdapterCapabilityManifest {\n /**\n * Serialized manifest format.\n *\n * @default 1\n */\n readonly formatVersion?: typeof ADAPTER_CAPABILITY_FORMAT_VERSION;\n /** Stable adapter identifier shown in diagnostics and reports. */\n readonly adapter: string;\n /**\n * Supported capability ranges.\n *\n * @default []\n */\n readonly capabilities?: readonly AdapterCapabilitySupport[];\n}\n\n/** Stable validation code for an adapter capability manifest. */\nexport type AdapterCapabilityDiagnosticCode =\n | \"invalid-format-version\"\n | \"invalid-adapter-id\"\n | \"invalid-capability-id\"\n | \"invalid-version\"\n | \"invalid-version-range\"\n | \"duplicate-capability\";\n\n/** Deterministic validation diagnostic for an adapter capability manifest. */\nexport interface AdapterCapabilityDiagnostic {\n /** Stable machine-readable diagnostic code. */\n readonly code: AdapterCapabilityDiagnosticCode;\n /** JSON-style path of the invalid value. */\n readonly path: string;\n /** Human-readable explanation. */\n readonly message: string;\n}\n\n/** Stable incompatibility code emitted during capability negotiation. */\nexport type AdapterCapabilityMismatchCode =\n | \"unknown-requirement\"\n | \"missing-capability\"\n | \"version-below-minimum\"\n | \"version-above-maximum\";\n\n/** One failed adapter capability requirement. */\nexport interface AdapterCapabilityMismatch {\n /** Stable machine-readable mismatch code. */\n readonly code: AdapterCapabilityMismatchCode;\n /** Required capability identifier. */\n readonly capability: string;\n /** Required contract version when the capability is declared. */\n readonly requiredVersion?: number;\n /** Adapter minimum when support exists. */\n readonly minVersion?: number;\n /** Adapter maximum when support exists. */\n readonly maxVersion?: number;\n}\n\n/** Deterministic result of negotiating requirements with one adapter. */\nexport interface AdapterCapabilityNegotiation {\n /** Adapter whose support was inspected. */\n readonly adapter: string;\n /** Whether the manifest is valid and every requirement is supported. */\n readonly compatible: boolean;\n /** Manifest validation failures, in stable path order. */\n readonly diagnostics: readonly AdapterCapabilityDiagnostic[];\n /** Unsupported or unknown requirements, in stable capability order. */\n readonly mismatches: readonly AdapterCapabilityMismatch[];\n}\n\n/** Compatibility classification for one adapter capability change. */\nexport type CompatibilityChangeKind = \"additive\" | \"breaking\";\n\n/** One stable adapter capability compatibility change. */\nexport interface AdapterCapabilityCompatibilityChange {\n /** Compatibility classification. */\n readonly kind: CompatibilityChangeKind;\n /** JSON-style path of the changed capability support. */\n readonly path: string;\n /** Human-readable summary. */\n readonly message: string;\n}\n\n/** Deterministic compatibility report between two adapter manifests. */\nexport interface AdapterCapabilityCompatibilityReport {\n /** Validation failures in the previous manifest. */\n readonly previousDiagnostics: readonly AdapterCapabilityDiagnostic[];\n /** Validation failures in the next manifest. */\n readonly nextDiagnostics: readonly AdapterCapabilityDiagnostic[];\n /** All changes in stable path order. */\n readonly changes: readonly AdapterCapabilityCompatibilityChange[];\n}\n","import type { AdapterCapabilitySupport } from \"./adapter-model.js\";\n\n/** Current contract version shared by the native-engine capability profile. */\nexport const NATIVE_ENGINE_CAPABILITY_VERSION = 1 as const;\n\n/** Closed set of capability identifiers required from a native rendering engine. */\nexport const NATIVE_ENGINE_CAPABILITY_IDS = [\n \"native.rendering\",\n \"native.events\",\n \"native.layout\",\n \"native.text\",\n \"native.images\",\n \"native.animation\",\n \"native.accessibility\",\n \"native.lifecycle\",\n] as const;\n\n/** One identifier from the canonical native-engine capability profile. */\nexport type NativeEngineCapabilityId = (typeof NATIVE_ENGINE_CAPABILITY_IDS)[number];\n\n/**\n * Creates the canonical version-one native-engine capability profile.\n *\n * A fresh list is returned so an adapter can extend its manifest without\n * mutating the shared profile seen by another consumer.\n */\nexport function nativeEngineCapabilityProfile(): readonly AdapterCapabilitySupport[] {\n return NATIVE_ENGINE_CAPABILITY_IDS.map((id) => ({\n id,\n minVersion: NATIVE_ENGINE_CAPABILITY_VERSION,\n maxVersion: NATIVE_ENGINE_CAPABILITY_VERSION,\n }));\n}\n","import {\n ADAPTER_CAPABILITY_FORMAT_VERSION,\n type AdapterCapabilityDiagnostic,\n type AdapterCapabilityDiagnosticCode,\n type AdapterCapabilityManifest,\n} from \"./adapter-model.js\";\n\nconst IDENTIFIER = /^[a-z0-9][a-z0-9._-]*$/;\nconst DIAGNOSTIC_ORDER: Record<AdapterCapabilityDiagnosticCode, number> = {\n \"invalid-format-version\": 0,\n \"invalid-adapter-id\": 1,\n \"invalid-capability-id\": 2,\n \"invalid-version\": 3,\n \"invalid-version-range\": 4,\n \"duplicate-capability\": 5,\n};\n\n/** Validates an adapter capability manifest without mutating it. */\nexport function validateAdapterCapabilityManifest(\n manifest: AdapterCapabilityManifest,\n): AdapterCapabilityDiagnostic[] {\n const diagnostics: AdapterCapabilityDiagnostic[] = [];\n if ((manifest.formatVersion ?? 1) !== ADAPTER_CAPABILITY_FORMAT_VERSION) {\n diagnostics.push(\n diagnostic(\n \"invalid-format-version\",\n \"formatVersion\",\n \"unsupported adapter capability manifest format version\",\n ),\n );\n }\n if (!IDENTIFIER.test(manifest.adapter)) {\n diagnostics.push(\n diagnostic(\n \"invalid-adapter-id\",\n \"adapter\",\n \"adapter must be a lowercase portable identifier\",\n ),\n );\n }\n\n const seen = new Set<string>();\n for (const [index, capability] of (manifest.capabilities ?? []).entries()) {\n const path = `capabilities.${index}`;\n if (!IDENTIFIER.test(capability.id)) {\n diagnostics.push(\n diagnostic(\n \"invalid-capability-id\",\n `${path}.id`,\n \"capability id must be a lowercase portable identifier\",\n ),\n );\n }\n if (seen.has(capability.id)) {\n diagnostics.push(\n diagnostic(\n \"duplicate-capability\",\n `${path}.id`,\n \"capability id must be unique within the adapter manifest\",\n ),\n );\n }\n seen.add(capability.id);\n if (capability.minVersion <= 0) {\n diagnostics.push(\n diagnostic(\n \"invalid-version\",\n `${path}.minVersion`,\n \"minimum supported version must be greater than zero\",\n ),\n );\n }\n if (capability.maxVersion <= 0) {\n diagnostics.push(\n diagnostic(\n \"invalid-version\",\n `${path}.maxVersion`,\n \"maximum supported version must be greater than zero\",\n ),\n );\n }\n if (capability.minVersion > capability.maxVersion) {\n diagnostics.push(\n diagnostic(\n \"invalid-version-range\",\n path,\n \"minimum supported version must not exceed maximum supported version\",\n ),\n );\n }\n }\n\n return diagnostics.sort(\n (left, right) =>\n compareText(left.path, right.path) ||\n DIAGNOSTIC_ORDER[left.code] - DIAGNOSTIC_ORDER[right.code] ||\n compareText(left.message, right.message),\n );\n}\n\n/**\n * Parses an untrusted manifest with strict field and primitive checks.\n *\n * Unknown fields, missing required fields, and mismatched primitive types\n * throw before negotiation. Semantic range errors remain available from\n * {@link validateAdapterCapabilityManifest}.\n */\nexport function parseAdapterCapabilityManifest(value: unknown): AdapterCapabilityManifest {\n assertRecord(value, \"manifest\");\n assertKnownFields(value, [\"formatVersion\", \"adapter\", \"capabilities\"], \"manifest\");\n if (value.formatVersion !== undefined && typeof value.formatVersion !== \"number\") {\n throw new TypeError(\"formatVersion must be a number\");\n }\n if (typeof value.adapter !== \"string\") {\n throw new TypeError(\"adapter must be a string\");\n }\n if (value.capabilities !== undefined) {\n if (!Array.isArray(value.capabilities)) {\n throw new TypeError(\"capabilities must be an array\");\n }\n for (const [index, capability] of value.capabilities.entries()) {\n const path = `capabilities.${index}`;\n assertRecord(capability, path);\n assertKnownFields(capability, [\"id\", \"minVersion\", \"maxVersion\"], path);\n if (typeof capability.id !== \"string\") throw new TypeError(`${path}.id must be a string`);\n if (\n typeof capability.minVersion !== \"number\" ||\n !Number.isSafeInteger(capability.minVersion) ||\n capability.minVersion < 0\n ) {\n throw new TypeError(`${path}.minVersion must be a safe integer`);\n }\n if (\n typeof capability.maxVersion !== \"number\" ||\n !Number.isSafeInteger(capability.maxVersion) ||\n capability.maxVersion < 0\n ) {\n throw new TypeError(`${path}.maxVersion must be a safe integer`);\n }\n }\n }\n return value as unknown as AdapterCapabilityManifest;\n}\n\nfunction diagnostic(\n code: AdapterCapabilityDiagnosticCode,\n path: string,\n message: string,\n): AdapterCapabilityDiagnostic {\n return { code, path, message };\n}\n\nfunction assertRecord(value: unknown, path: string): asserts value is Record<string, unknown> {\n if (value == null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new TypeError(`${path} must be an object`);\n }\n}\n\nfunction assertKnownFields(\n value: Record<string, unknown>,\n expected: readonly string[],\n path: string,\n): void {\n const known = new Set(expected);\n const unknown = Object.keys(value).find((field) => !known.has(field));\n if (unknown !== undefined) throw new TypeError(`${path} has unknown field ${unknown}`);\n}\n\nfunction compareText(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n","import type {\n AdapterCapabilityCompatibilityChange,\n AdapterCapabilityCompatibilityReport,\n AdapterCapabilityManifest,\n AdapterCapabilitySupport,\n CompatibilityChangeKind,\n} from \"./adapter-model.js\";\nimport { validateAdapterCapabilityManifest } from \"./adapter-validate.js\";\n\n/**\n * Compares adapter capability support from older to newer.\n *\n * Adding support or widening an inclusive version range is additive.\n * Removing support or narrowing either bound is breaking.\n */\nexport function compareAdapterCapabilities(\n previous: AdapterCapabilityManifest,\n next: AdapterCapabilityManifest,\n): AdapterCapabilityCompatibilityReport {\n const previousDiagnostics = validateAdapterCapabilityManifest(previous);\n const nextDiagnostics = validateAdapterCapabilityManifest(next);\n if (previousDiagnostics.length > 0 || nextDiagnostics.length > 0) {\n return { previousDiagnostics, nextDiagnostics, changes: [] };\n }\n\n const changes: AdapterCapabilityCompatibilityChange[] = [];\n if (previous.adapter !== next.adapter) {\n changes.push(change(\"breaking\", \"adapter\", \"adapter identity changed\"));\n }\n\n const oldSupport = byId(previous);\n const newSupport = byId(next);\n for (const id of [...oldSupport.keys()].filter((id) => !newSupport.has(id)).sort()) {\n changes.push(change(\"breaking\", `capabilities.${id}`, \"capability support was removed\"));\n }\n for (const id of [...newSupport.keys()].filter((id) => !oldSupport.has(id)).sort()) {\n changes.push(change(\"additive\", `capabilities.${id}`, \"capability support was added\"));\n }\n for (const [id, old] of oldSupport) {\n const current = newSupport.get(id);\n if (current === undefined) continue;\n if (old.minVersion !== current.minVersion) {\n changes.push(\n change(\n current.minVersion < old.minVersion ? \"additive\" : \"breaking\",\n `capabilities.${id}.minVersion`,\n current.minVersion < old.minVersion\n ? \"minimum supported version decreased\"\n : \"minimum supported version increased\",\n ),\n );\n }\n if (old.maxVersion !== current.maxVersion) {\n changes.push(\n change(\n current.maxVersion > old.maxVersion ? \"additive\" : \"breaking\",\n `capabilities.${id}.maxVersion`,\n current.maxVersion > old.maxVersion\n ? \"maximum supported version increased\"\n : \"maximum supported version decreased\",\n ),\n );\n }\n }\n\n changes.sort(\n (left, right) => compareText(left.path, right.path) || compareText(left.kind, right.kind),\n );\n return { previousDiagnostics, nextDiagnostics, changes };\n}\n\nfunction byId(manifest: AdapterCapabilityManifest): Map<string, AdapterCapabilitySupport> {\n return new Map((manifest.capabilities ?? []).map((value) => [value.id, value] as const));\n}\n\nfunction change(\n kind: CompatibilityChangeKind,\n path: string,\n message: string,\n): AdapterCapabilityCompatibilityChange {\n return { kind, path, message };\n}\n\nfunction compareText(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n","import type { ApplicationMarquette } from \"./model.js\";\nimport type {\n AdapterCapabilityManifest,\n AdapterCapabilityMismatch,\n AdapterCapabilityMismatchCode,\n AdapterCapabilityNegotiation,\n AdapterCapabilitySupport,\n} from \"./adapter-model.js\";\nimport { validateAdapterCapabilityManifest } from \"./adapter-validate.js\";\n\n/**\n * Negotiates application capability requirements against one adapter.\n *\n * Requirement identifiers are deduplicated and sorted. Unknown application\n * capability identifiers fail closed instead of being treated as adapter\n * omissions. An invalid manifest never produces a compatible result.\n */\nexport function negotiateAdapterCapabilities(\n marquette: ApplicationMarquette,\n requiredCapabilities: readonly string[],\n manifest: AdapterCapabilityManifest,\n): AdapterCapabilityNegotiation {\n const diagnostics = validateAdapterCapabilityManifest(manifest);\n const support = new Map(\n (manifest.capabilities ?? []).map((capability) => [capability.id, capability] as const),\n );\n const mismatches = [...new Set(requiredCapabilities)]\n .sort()\n .flatMap((id) => mismatch(marquette, id, support.get(id), diagnostics.length === 0));\n\n return {\n adapter: manifest.adapter,\n compatible: diagnostics.length === 0 && mismatches.length === 0,\n diagnostics,\n mismatches,\n };\n}\n\nfunction mismatch(\n marquette: ApplicationMarquette,\n id: string,\n support: AdapterCapabilitySupport | undefined,\n manifestIsValid: boolean,\n): AdapterCapabilityMismatch[] {\n const requirement = marquette.capabilities?.[id];\n if (requirement === undefined) return [problem(\"unknown-requirement\", id)];\n if (!manifestIsValid) return [];\n\n const requiredVersion = requirement.version ?? 1;\n if (support === undefined) return [problem(\"missing-capability\", id, requiredVersion)];\n if (requiredVersion < support.minVersion) {\n return [problem(\"version-below-minimum\", id, requiredVersion, support)];\n }\n if (requiredVersion > support.maxVersion) {\n return [problem(\"version-above-maximum\", id, requiredVersion, support)];\n }\n return [];\n}\n\nfunction problem(\n code: AdapterCapabilityMismatchCode,\n capability: string,\n requiredVersion?: number,\n support?: AdapterCapabilitySupport,\n): AdapterCapabilityMismatch {\n return {\n code,\n capability,\n ...(requiredVersion === undefined ? {} : { requiredVersion }),\n ...(support === undefined\n ? {}\n : { minVersion: support.minVersion, maxVersion: support.maxVersion }),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"adapter.mjs","names":["compareText"],"sources":["../src/adapter-model.ts","../src/adapter-native-profile.ts","../src/adapter-validate.ts","../src/adapter-compatibility.ts","../src/adapter-negotiate.ts"],"sourcesContent":["/** Current serialized adapter-capability manifest format. */\nexport const ADAPTER_CAPABILITY_FORMAT_VERSION = 1 as const;\n\n/** Inclusive version range supported for one capability contract. */\nexport interface AdapterCapabilitySupport {\n /** Stable capability identifier from an application marquette. */\n readonly id: string;\n /** Oldest supported capability contract version, inclusive. */\n readonly minVersion: number;\n /** Newest supported capability contract version, inclusive. */\n readonly maxVersion: number;\n}\n\n/** Versioned, language-neutral capabilities offered by one adapter. */\nexport interface AdapterCapabilityManifest {\n /**\n * Serialized manifest format.\n *\n * @default 1\n */\n readonly formatVersion?: typeof ADAPTER_CAPABILITY_FORMAT_VERSION;\n /** Stable adapter identifier shown in diagnostics and reports. */\n readonly adapter: string;\n /**\n * Supported capability ranges.\n *\n * @default []\n */\n readonly capabilities?: readonly AdapterCapabilitySupport[];\n}\n\n/** Stable validation code for an adapter capability manifest. */\nexport type AdapterCapabilityDiagnosticCode =\n | \"invalid-format-version\"\n | \"invalid-adapter-id\"\n | \"invalid-capability-id\"\n | \"invalid-version\"\n | \"invalid-version-range\"\n | \"duplicate-capability\";\n\n/** Deterministic validation diagnostic for an adapter capability manifest. */\nexport interface AdapterCapabilityDiagnostic {\n /** Stable machine-readable diagnostic code. */\n readonly code: AdapterCapabilityDiagnosticCode;\n /** JSON-style path of the invalid value. */\n readonly path: string;\n /** Human-readable explanation. */\n readonly message: string;\n}\n\n/** Stable incompatibility code emitted during capability negotiation. */\nexport type AdapterCapabilityMismatchCode =\n | \"unknown-requirement\"\n | \"missing-capability\"\n | \"version-below-minimum\"\n | \"version-above-maximum\";\n\n/** One failed adapter capability requirement. */\nexport interface AdapterCapabilityMismatch {\n /** Stable machine-readable mismatch code. */\n readonly code: AdapterCapabilityMismatchCode;\n /** Required capability identifier. */\n readonly capability: string;\n /** JSON-style path of the unsupported application capability requirement. */\n readonly path: string;\n /** Human-readable explanation with stable wording for renderer diagnostics. */\n readonly message: string;\n /** Required contract version when the capability is declared. */\n readonly requiredVersion?: number;\n /** Adapter minimum when support exists. */\n readonly minVersion?: number;\n /** Adapter maximum when support exists. */\n readonly maxVersion?: number;\n}\n\n/** Deterministic result of negotiating requirements with one adapter. */\nexport interface AdapterCapabilityNegotiation {\n /** Adapter whose support was inspected. */\n readonly adapter: string;\n /** Whether the manifest is valid and every requirement is supported. */\n readonly compatible: boolean;\n /** Manifest validation failures, in stable path order. */\n readonly diagnostics: readonly AdapterCapabilityDiagnostic[];\n /** Unsupported or unknown requirements, in stable capability order. */\n readonly mismatches: readonly AdapterCapabilityMismatch[];\n}\n\n/** Compatibility classification for one adapter capability change. */\nexport type CompatibilityChangeKind = \"additive\" | \"breaking\";\n\n/** One stable adapter capability compatibility change. */\nexport interface AdapterCapabilityCompatibilityChange {\n /** Compatibility classification. */\n readonly kind: CompatibilityChangeKind;\n /** JSON-style path of the changed capability support. */\n readonly path: string;\n /** Human-readable summary. */\n readonly message: string;\n}\n\n/** Deterministic compatibility report between two adapter manifests. */\nexport interface AdapterCapabilityCompatibilityReport {\n /** Validation failures in the previous manifest. */\n readonly previousDiagnostics: readonly AdapterCapabilityDiagnostic[];\n /** Validation failures in the next manifest. */\n readonly nextDiagnostics: readonly AdapterCapabilityDiagnostic[];\n /** All changes in stable path order. */\n readonly changes: readonly AdapterCapabilityCompatibilityChange[];\n}\n","import type { AdapterCapabilitySupport } from \"./adapter-model.js\";\n\n/** Current contract version shared by the native-engine capability profile. */\nexport const NATIVE_ENGINE_CAPABILITY_VERSION = 1 as const;\n\n/** Closed set of capability identifiers required from a native rendering engine. */\nexport const NATIVE_ENGINE_CAPABILITY_IDS = [\n \"native.rendering\",\n \"native.events\",\n \"native.layout\",\n \"native.text\",\n \"native.images\",\n \"native.animation\",\n \"native.accessibility\",\n \"native.lifecycle\",\n] as const;\n\n/** One identifier from the canonical native-engine capability profile. */\nexport type NativeEngineCapabilityId = (typeof NATIVE_ENGINE_CAPABILITY_IDS)[number];\n\n/**\n * Creates the canonical version-one native-engine capability profile.\n *\n * A fresh list is returned so an adapter can extend its manifest without\n * mutating the shared profile seen by another consumer.\n */\nexport function nativeEngineCapabilityProfile(): readonly AdapterCapabilitySupport[] {\n return NATIVE_ENGINE_CAPABILITY_IDS.map((id) => ({\n id,\n minVersion: NATIVE_ENGINE_CAPABILITY_VERSION,\n maxVersion: NATIVE_ENGINE_CAPABILITY_VERSION,\n }));\n}\n","import {\n ADAPTER_CAPABILITY_FORMAT_VERSION,\n type AdapterCapabilityDiagnostic,\n type AdapterCapabilityDiagnosticCode,\n type AdapterCapabilityManifest,\n} from \"./adapter-model.js\";\n\nconst IDENTIFIER = /^[a-z0-9][a-z0-9._-]*$/;\nconst DIAGNOSTIC_ORDER: Record<AdapterCapabilityDiagnosticCode, number> = {\n \"invalid-format-version\": 0,\n \"invalid-adapter-id\": 1,\n \"invalid-capability-id\": 2,\n \"invalid-version\": 3,\n \"invalid-version-range\": 4,\n \"duplicate-capability\": 5,\n};\n\n/** Validates an adapter capability manifest without mutating it. */\nexport function validateAdapterCapabilityManifest(\n manifest: AdapterCapabilityManifest,\n): AdapterCapabilityDiagnostic[] {\n const diagnostics: AdapterCapabilityDiagnostic[] = [];\n if ((manifest.formatVersion ?? 1) !== ADAPTER_CAPABILITY_FORMAT_VERSION) {\n diagnostics.push(\n diagnostic(\n \"invalid-format-version\",\n \"formatVersion\",\n \"unsupported adapter capability manifest format version\",\n ),\n );\n }\n if (!IDENTIFIER.test(manifest.adapter)) {\n diagnostics.push(\n diagnostic(\n \"invalid-adapter-id\",\n \"adapter\",\n \"adapter must be a lowercase portable identifier\",\n ),\n );\n }\n\n const seen = new Set<string>();\n for (const [index, capability] of (manifest.capabilities ?? []).entries()) {\n const path = `capabilities.${index}`;\n if (!IDENTIFIER.test(capability.id)) {\n diagnostics.push(\n diagnostic(\n \"invalid-capability-id\",\n `${path}.id`,\n \"capability id must be a lowercase portable identifier\",\n ),\n );\n }\n if (seen.has(capability.id)) {\n diagnostics.push(\n diagnostic(\n \"duplicate-capability\",\n `${path}.id`,\n \"capability id must be unique within the adapter manifest\",\n ),\n );\n }\n seen.add(capability.id);\n if (capability.minVersion <= 0) {\n diagnostics.push(\n diagnostic(\n \"invalid-version\",\n `${path}.minVersion`,\n \"minimum supported version must be greater than zero\",\n ),\n );\n }\n if (capability.maxVersion <= 0) {\n diagnostics.push(\n diagnostic(\n \"invalid-version\",\n `${path}.maxVersion`,\n \"maximum supported version must be greater than zero\",\n ),\n );\n }\n if (capability.minVersion > capability.maxVersion) {\n diagnostics.push(\n diagnostic(\n \"invalid-version-range\",\n path,\n \"minimum supported version must not exceed maximum supported version\",\n ),\n );\n }\n }\n\n return diagnostics.sort(\n (left, right) =>\n compareText(left.path, right.path) ||\n DIAGNOSTIC_ORDER[left.code] - DIAGNOSTIC_ORDER[right.code] ||\n compareText(left.message, right.message),\n );\n}\n\n/**\n * Parses an untrusted manifest with strict field and primitive checks.\n *\n * Unknown fields, missing required fields, and mismatched primitive types\n * throw before negotiation. Semantic range errors remain available from\n * {@link validateAdapterCapabilityManifest}.\n */\nexport function parseAdapterCapabilityManifest(value: unknown): AdapterCapabilityManifest {\n assertRecord(value, \"manifest\");\n assertKnownFields(value, [\"formatVersion\", \"adapter\", \"capabilities\"], \"manifest\");\n if (value.formatVersion !== undefined && typeof value.formatVersion !== \"number\") {\n throw new TypeError(\"formatVersion must be a number\");\n }\n if (typeof value.adapter !== \"string\") {\n throw new TypeError(\"adapter must be a string\");\n }\n if (value.capabilities !== undefined) {\n if (!Array.isArray(value.capabilities)) {\n throw new TypeError(\"capabilities must be an array\");\n }\n for (const [index, capability] of value.capabilities.entries()) {\n const path = `capabilities.${index}`;\n assertRecord(capability, path);\n assertKnownFields(capability, [\"id\", \"minVersion\", \"maxVersion\"], path);\n if (typeof capability.id !== \"string\") throw new TypeError(`${path}.id must be a string`);\n if (\n typeof capability.minVersion !== \"number\" ||\n !Number.isSafeInteger(capability.minVersion) ||\n capability.minVersion < 0\n ) {\n throw new TypeError(`${path}.minVersion must be a safe integer`);\n }\n if (\n typeof capability.maxVersion !== \"number\" ||\n !Number.isSafeInteger(capability.maxVersion) ||\n capability.maxVersion < 0\n ) {\n throw new TypeError(`${path}.maxVersion must be a safe integer`);\n }\n }\n }\n return value as unknown as AdapterCapabilityManifest;\n}\n\nfunction diagnostic(\n code: AdapterCapabilityDiagnosticCode,\n path: string,\n message: string,\n): AdapterCapabilityDiagnostic {\n return { code, path, message };\n}\n\nfunction assertRecord(value: unknown, path: string): asserts value is Record<string, unknown> {\n if (value == null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new TypeError(`${path} must be an object`);\n }\n}\n\nfunction assertKnownFields(\n value: Record<string, unknown>,\n expected: readonly string[],\n path: string,\n): void {\n const known = new Set(expected);\n const unknown = Object.keys(value).find((field) => !known.has(field));\n if (unknown !== undefined) throw new TypeError(`${path} has unknown field ${unknown}`);\n}\n\nfunction compareText(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n","import type {\n AdapterCapabilityCompatibilityChange,\n AdapterCapabilityCompatibilityReport,\n AdapterCapabilityManifest,\n AdapterCapabilitySupport,\n CompatibilityChangeKind,\n} from \"./adapter-model.js\";\nimport { validateAdapterCapabilityManifest } from \"./adapter-validate.js\";\n\n/**\n * Compares adapter capability support from older to newer.\n *\n * Adding support or widening an inclusive version range is additive.\n * Removing support or narrowing either bound is breaking.\n */\nexport function compareAdapterCapabilities(\n previous: AdapterCapabilityManifest,\n next: AdapterCapabilityManifest,\n): AdapterCapabilityCompatibilityReport {\n const previousDiagnostics = validateAdapterCapabilityManifest(previous);\n const nextDiagnostics = validateAdapterCapabilityManifest(next);\n if (previousDiagnostics.length > 0 || nextDiagnostics.length > 0) {\n return { previousDiagnostics, nextDiagnostics, changes: [] };\n }\n\n const changes: AdapterCapabilityCompatibilityChange[] = [];\n if (previous.adapter !== next.adapter) {\n changes.push(change(\"breaking\", \"adapter\", \"adapter identity changed\"));\n }\n\n const oldSupport = byId(previous);\n const newSupport = byId(next);\n for (const id of [...oldSupport.keys()].filter((id) => !newSupport.has(id)).sort()) {\n changes.push(change(\"breaking\", `capabilities.${id}`, \"capability support was removed\"));\n }\n for (const id of [...newSupport.keys()].filter((id) => !oldSupport.has(id)).sort()) {\n changes.push(change(\"additive\", `capabilities.${id}`, \"capability support was added\"));\n }\n for (const [id, old] of oldSupport) {\n const current = newSupport.get(id);\n if (current === undefined) continue;\n if (old.minVersion !== current.minVersion) {\n changes.push(\n change(\n current.minVersion < old.minVersion ? \"additive\" : \"breaking\",\n `capabilities.${id}.minVersion`,\n current.minVersion < old.minVersion\n ? \"minimum supported version decreased\"\n : \"minimum supported version increased\",\n ),\n );\n }\n if (old.maxVersion !== current.maxVersion) {\n changes.push(\n change(\n current.maxVersion > old.maxVersion ? \"additive\" : \"breaking\",\n `capabilities.${id}.maxVersion`,\n current.maxVersion > old.maxVersion\n ? \"maximum supported version increased\"\n : \"maximum supported version decreased\",\n ),\n );\n }\n }\n\n changes.sort(\n (left, right) => compareText(left.path, right.path) || compareText(left.kind, right.kind),\n );\n return { previousDiagnostics, nextDiagnostics, changes };\n}\n\nfunction byId(manifest: AdapterCapabilityManifest): Map<string, AdapterCapabilitySupport> {\n return new Map((manifest.capabilities ?? []).map((value) => [value.id, value] as const));\n}\n\nfunction change(\n kind: CompatibilityChangeKind,\n path: string,\n message: string,\n): AdapterCapabilityCompatibilityChange {\n return { kind, path, message };\n}\n\nfunction compareText(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n","import type { ApplicationMarquette } from \"./model.js\";\nimport type {\n AdapterCapabilityManifest,\n AdapterCapabilityMismatch,\n AdapterCapabilityMismatchCode,\n AdapterCapabilityNegotiation,\n AdapterCapabilitySupport,\n} from \"./adapter-model.js\";\nimport { validateAdapterCapabilityManifest } from \"./adapter-validate.js\";\n\n/**\n * Negotiates application capability requirements against one adapter.\n *\n * Requirement identifiers are deduplicated and sorted. Unknown application\n * capability identifiers fail closed instead of being treated as adapter\n * omissions. An invalid manifest never produces a compatible result.\n */\nexport function negotiateAdapterCapabilities(\n marquette: ApplicationMarquette,\n requiredCapabilities: readonly string[],\n manifest: AdapterCapabilityManifest,\n): AdapterCapabilityNegotiation {\n const diagnostics = validateAdapterCapabilityManifest(manifest);\n const support = new Map(\n (manifest.capabilities ?? []).map((capability) => [capability.id, capability] as const),\n );\n const mismatches = [...new Set(requiredCapabilities)]\n .sort()\n .flatMap((id) => mismatch(marquette, id, support.get(id), diagnostics.length === 0));\n\n return {\n adapter: manifest.adapter,\n compatible: diagnostics.length === 0 && mismatches.length === 0,\n diagnostics,\n mismatches,\n };\n}\n\nfunction mismatch(\n marquette: ApplicationMarquette,\n id: string,\n support: AdapterCapabilitySupport | undefined,\n manifestIsValid: boolean,\n): AdapterCapabilityMismatch[] {\n const requirement = marquette.capabilities?.[id];\n if (requirement === undefined) return [problem(\"unknown-requirement\", id)];\n if (!manifestIsValid) return [];\n\n const requiredVersion = requirement.version ?? 1;\n if (support === undefined) return [problem(\"missing-capability\", id, requiredVersion)];\n if (requiredVersion < support.minVersion) {\n return [problem(\"version-below-minimum\", id, requiredVersion, support)];\n }\n if (requiredVersion > support.maxVersion) {\n return [problem(\"version-above-maximum\", id, requiredVersion, support)];\n }\n return [];\n}\n\nfunction problem(\n code: AdapterCapabilityMismatchCode,\n capability: string,\n requiredVersion?: number,\n support?: AdapterCapabilitySupport,\n): AdapterCapabilityMismatch {\n return {\n code,\n capability,\n path: `capabilities.${capability}`,\n message: mismatchMessage(code),\n ...(requiredVersion === undefined ? {} : { requiredVersion }),\n ...(support === undefined\n ? {}\n : { minVersion: support.minVersion, maxVersion: support.maxVersion }),\n };\n}\n\nfunction mismatchMessage(code: AdapterCapabilityMismatchCode): string {\n switch (code) {\n case \"unknown-requirement\":\n return \"application references an undeclared capability requirement\";\n case \"missing-capability\":\n return \"adapter does not support the required capability\";\n case \"version-below-minimum\":\n return \"application requires a capability version below the adapter minimum\";\n case \"version-above-maximum\":\n return \"application requires a capability version above the adapter maximum\";\n }\n}\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as RouteId, a as CapabilityId, b as TargetId, c as MARQUETTE_FORMAT_VERSION, d as MarquetteProtocol, f as MarquetteRoute, g as RenderingMode, h as ProtocolId, i as CapabilityDefinition, l as MarquetteBackend, m as ProtocolFamily, n as BackendFamily, o as EnvironmentConsumer, p as MarquetteTarget, r as BackendId, s as EnvironmentId, t as ApplicationMarquette, u as MarquetteEnvironment, v as RuntimeFamily, y as ServerEnvironmentId } from "./model-
|
|
1
|
+
import { _ as RouteId, a as CapabilityId, b as TargetId, c as MARQUETTE_FORMAT_VERSION, d as MarquetteProtocol, f as MarquetteRoute, g as RenderingMode, h as ProtocolId, i as CapabilityDefinition, l as MarquetteBackend, m as ProtocolFamily, n as BackendFamily, o as EnvironmentConsumer, p as MarquetteTarget, r as BackendId, s as EnvironmentId, t as ApplicationMarquette, u as MarquetteEnvironment, v as RuntimeFamily, y as ServerEnvironmentId } from "./model-B_mM8AeH.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/author.d.ts
|
|
4
4
|
/**
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/author.ts"],"mappings":";;;;;AAqBA;;;;KAAY,6BAAA,mBAAgD,oBAAA;EAAA,SACjD,YAAA,qBACS,YAAA,CAAa,SAAA,IAAa,oBAAA;IAAA,SAAkC,EAAA,EAAI,EAAA;EAAA;EAAA,SAEzE,YAAA,aAAyB,oBAAA,SAA6B,aAAA,CAAc,SAAA;IAAA,SAClE,YAAA,YAAwB,YAAA,CAAa,SAAA;IAAA,SACrC,MAAA,EAAQ,QAAA,CAAS,SAAA;EAAA;EAAA,SAEnB,QAAA,aAAqB,gBAAA,SAAyB,mBAAA,CAAoB,SAAA;IAAA,SAChE,YAAA,YAAwB,YAAA,CAAa,SAAA;EAAA;EAAA,SAEvC,SAAA,aAAsB,iBAAA,SAA0B,SAAA,CAAU,SAAA;IAAA,SACxD,YAAA,YAAwB,YAAA,CAAa,SAAA;EAAA;EAAA,SAEvC,MAAA,aAAmB,cAAA,SAE1B,aAAA,CAAc,SAAA,GACd,SAAA,CAAU,SAAA,GACV,UAAA,CAAW,SAAA;IAAA,SACE,YAAA,YAAwB,YAAA,CAAa,SAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;iBAwBtC,0BAAA,yBAAmD,oBAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/author.ts"],"mappings":";;;;;AAqBA;;;;KAAY,6BAAA,mBAAgD,oBAAA;EAAA,SACjD,YAAA,qBACS,YAAA,CAAa,SAAA,IAAa,oBAAA;IAAA,SAAkC,EAAA,EAAI,EAAA;EAAA;EAAA,SAEzE,YAAA,aAAyB,oBAAA,SAA6B,aAAA,CAAc,SAAA;IAAA,SAClE,YAAA,YAAwB,YAAA,CAAa,SAAA;IAAA,SACrC,MAAA,EAAQ,QAAA,CAAS,SAAA;EAAA;EAAA,SAEnB,QAAA,aAAqB,gBAAA,SAAyB,mBAAA,CAAoB,SAAA;IAAA,SAChE,YAAA,YAAwB,YAAA,CAAa,SAAA;EAAA;EAAA,SAEvC,SAAA,aAAsB,iBAAA,SAA0B,SAAA,CAAU,SAAA;IAAA,SACxD,YAAA,YAAwB,YAAA,CAAa,SAAA;EAAA;EAAA,SAEvC,MAAA,aAAmB,cAAA,SAE1B,aAAA,CAAc,SAAA,GACd,SAAA,CAAU,SAAA,GACV,UAAA,CAAW,SAAA;IAAA,SACE,YAAA,YAAwB,YAAA,CAAa,SAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;iBAwBtC,0BAAA,yBAAmD,oBAAA,EACjE,SAAA,EAAW,SAAA,GAAY,6BAAA,CAA8B,OAAA,CAAQ,SAAA,KAC5D,SAAA"}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/author.ts","../src/model.ts"],"sourcesContent":["import type {\n ApplicationMarquette,\n BackendId,\n CapabilityDefinition,\n CapabilityId,\n EnvironmentId,\n MarquetteBackend,\n MarquetteEnvironment,\n MarquetteProtocol,\n MarquetteRoute,\n ProtocolId,\n ServerEnvironmentId,\n TargetId,\n} from \"./model.js\";\n\n/**\n * Cross-reference constraints derived from the literals in one marquette.\n *\n * Keeping this type separate makes editor diagnostics point at the authored\n * reference instead of widening every identifier to `string`.\n */\nexport type MarquetteReferenceConstraints<Marquette extends ApplicationMarquette> = {\n readonly capabilities?: {\n readonly [Id in CapabilityId<Marquette>]: CapabilityDefinition & { readonly id: Id };\n };\n readonly environments?: readonly (MarquetteEnvironment<string, EnvironmentId<Marquette>> & {\n readonly capabilities?: readonly CapabilityId<Marquette>[];\n readonly target: TargetId<Marquette>;\n })[];\n readonly backends?: readonly (MarquetteBackend<string, ServerEnvironmentId<Marquette>> & {\n readonly capabilities?: readonly CapabilityId<Marquette>[];\n })[];\n readonly protocols?: readonly (MarquetteProtocol<string, BackendId<Marquette>> & {\n readonly capabilities?: readonly CapabilityId<Marquette>[];\n })[];\n readonly routes?: readonly (MarquetteRoute<\n string,\n EnvironmentId<Marquette>,\n BackendId<Marquette>,\n ProtocolId<Marquette>\n > & { readonly capabilities?: readonly CapabilityId<Marquette>[] })[];\n};\n\n/**\n * Defines an application marquette while preserving every authored literal.\n *\n * Environment dependencies, backend owners, protocol owners, and route\n * references are checked against identifiers declared in the same object.\n * The function returns its input without allocation or runtime work.\n *\n * @example\n * ```ts\n * const app = defineApplicationMarquette({\n * application: \"shop\",\n * targets: [\"web\"],\n * environments: [\n * { id: \"web\", target: \"web\", consumer: \"client\", runtime: \"browser\" },\n * ],\n * routes: [\n * { id: \"home\", path: \"/\", environment: \"web\", rendering: \"client\" },\n * ],\n * });\n * ```\n */\nexport function defineApplicationMarquette<const Marquette extends ApplicationMarquette>(\n marquette: Marquette & MarquetteReferenceConstraints<NoInfer<Marquette>>,\n): Marquette {\n return marquette;\n}\n","/** Current serialized application-marquette format. */\nexport const MARQUETTE_FORMAT_VERSION = 1 as const;\n\n/** User-visible target produced by an application marquette. */\nexport type MarquetteTarget = \"web\" | \"native\" | \"desktop\" | \"terminal\";\n\n/** Runtime family that executes one environment. */\nexport type RuntimeFamily =\n | \"browser\"\n | \"javascript\"\n | \"rust\"\n | \"go\"\n | \"jvm\"\n | \"native\"\n | \"desktop\"\n | \"terminal\"\n | \"external\";\n\n/** Side of the application graph that consumes an environment. */\nexport type EnvironmentConsumer = \"client\" | \"server\";\n\n/** Backend implementation family selected by an application. */\nexport type BackendFamily = \"javascript\" | \"rust\" | \"go\" | \"jvm\" | \"external\";\n\n/** Semantic family used by one data or procedure endpoint. */\nexport type ProtocolFamily = \"schema-query\" | \"typed-rpc\" | \"binary-rpc\";\n\n/** Rendering strategy selected for one route or target view. */\nexport type RenderingMode =\n | \"client\"\n | \"static\"\n | \"server\"\n | \"stream\"\n | \"partial\"\n | \"hybrid\"\n | \"native\"\n | \"desktop\"\n | \"terminal\";\n\n/** Versioned capability understood by adapters and developer tools. */\nexport interface CapabilityDefinition {\n /** Stable lowercase capability identifier. */\n readonly id: string;\n /** Summary shown by diagnostics, documentation, and AI tooling. */\n readonly description: string;\n /**\n * Capability contract version.\n *\n * @default 1\n */\n readonly version?: number;\n}\n\n/** Independently transformed and cached application environment. */\nexport interface MarquetteEnvironment<\n Id extends string = string,\n DependencyId extends string = string,\n> {\n /** Stable identifier, unique within the application. */\n readonly id: Id;\n /** User-visible target served by this environment. */\n readonly target: MarquetteTarget;\n /** Whether output is consumed by an end user or a trusted runtime. */\n readonly consumer: EnvironmentConsumer;\n /** Runtime family that executes the output. */\n readonly runtime: RuntimeFamily;\n /**\n * Authored entry module.\n *\n * @default undefined\n */\n readonly entry?: string;\n /**\n * Environments that must build first.\n *\n * @default []\n */\n readonly dependsOn?: readonly DependencyId[];\n /**\n * Capabilities required from the environment adapter.\n *\n * @default []\n */\n readonly capabilities?: readonly string[];\n}\n\n/** Backend application or independently deployed service. */\nexport interface MarquetteBackend<\n Id extends string = string,\n EnvironmentId extends string = string,\n> {\n /** Stable backend identifier. */\n readonly id: Id;\n /** Backend implementation family. */\n readonly family: BackendFamily;\n /**\n * Server environment that owns local execution.\n *\n * @default undefined\n */\n readonly environment?: EnvironmentId;\n /**\n * Capabilities required from the backend adapter.\n *\n * @default []\n */\n readonly capabilities?: readonly string[];\n}\n\n/** Protocol endpoint exposed by a backend. */\nexport interface MarquetteProtocol<Id extends string = string, BackendId extends string = string> {\n /** Stable endpoint identifier. */\n readonly id: Id;\n /** Protocol semantic family. */\n readonly family: ProtocolFamily;\n /** Backend that owns the endpoint. */\n readonly backend: BackendId;\n /**\n * Capabilities required from the protocol adapter.\n *\n * @default []\n */\n readonly capabilities?: readonly string[];\n}\n\n/** Typed application route or target view. */\nexport interface MarquetteRoute<\n Id extends string = string,\n EnvironmentId extends string = string,\n BackendId extends string = string,\n ProtocolId extends string = string,\n> {\n /** Stable identifier used by generated navigation APIs. */\n readonly id: Id;\n /** Logical path, always beginning with `/`. */\n readonly path: `/${string}`;\n /** Environment that owns the rendered route. */\n readonly environment: EnvironmentId;\n /** Rendering strategy used by the route. */\n readonly rendering: RenderingMode;\n /**\n * Backend used by data loading, mutations, or server rendering.\n *\n * @default undefined\n */\n readonly backend?: BackendId;\n /**\n * Protocol endpoint used as the route data contract.\n *\n * @default undefined\n */\n readonly protocol?: ProtocolId;\n /**\n * Capabilities required by the route.\n *\n * @default []\n */\n readonly capabilities?: readonly string[];\n}\n\n/** Complete, versioned application marquette. */\nexport interface ApplicationMarquette<\n Environments extends readonly MarquetteEnvironment[] = readonly MarquetteEnvironment[],\n Backends extends readonly MarquetteBackend[] = readonly MarquetteBackend[],\n Protocols extends readonly MarquetteProtocol[] = readonly MarquetteProtocol[],\n Routes extends readonly MarquetteRoute[] = readonly MarquetteRoute[],\n> {\n /**\n * Serialized contract format.\n *\n * @default 1\n */\n readonly formatVersion?: typeof MARQUETTE_FORMAT_VERSION;\n /** Stable lowercase application identifier. */\n readonly application: string;\n /**\n * User-visible application targets.\n *\n * @default []\n */\n readonly targets?: readonly MarquetteTarget[];\n /**\n * Versioned capability definitions keyed by identifier.\n *\n * @default {}\n */\n readonly capabilities?: Readonly<Record<string, CapabilityDefinition>>;\n /**\n * Independently transformed application environments.\n *\n * @default []\n */\n readonly environments?: Environments;\n /**\n * Backend applications and services.\n *\n * @default []\n */\n readonly backends?: Backends;\n /**\n * Protocol endpoints.\n *\n * @default []\n */\n readonly protocols?: Protocols;\n /**\n * Typed routes and target views.\n *\n * @default []\n */\n readonly routes?: Routes;\n}\n\n/** Extracts identifiers only when a collection is present in the authored object. */\ntype DeclaredId<Marquette, Collection extends PropertyKey> = Marquette extends {\n readonly [Key in Collection]: readonly { readonly id: infer Id extends string }[];\n}\n ? Id\n : never;\n\n/** Exact environment identifier union inferred from a marquette. */\nexport type EnvironmentId<Marquette extends ApplicationMarquette> = DeclaredId<\n Marquette,\n \"environments\"\n>;\n\n/** Exact server-owned environment identifier union inferred from a marquette. */\nexport type ServerEnvironmentId<Marquette extends ApplicationMarquette> = Marquette extends {\n readonly environments: readonly (infer Environment)[];\n}\n ? Environment extends {\n readonly consumer: \"server\";\n readonly id: infer Id extends string;\n }\n ? Id\n : never\n : never;\n\n/** Exact backend identifier union inferred from a marquette. */\nexport type BackendId<Marquette extends ApplicationMarquette> = DeclaredId<Marquette, \"backends\">;\n\n/** Exact protocol identifier union inferred from a marquette. */\nexport type ProtocolId<Marquette extends ApplicationMarquette> = DeclaredId<Marquette, \"protocols\">;\n\n/** Exact route identifier union inferred from a marquette. */\nexport type RouteId<Marquette extends ApplicationMarquette> = DeclaredId<Marquette, \"routes\">;\n\n/** Exact target union inferred from the authored target list. */\nexport type TargetId<Marquette extends ApplicationMarquette> = Marquette extends {\n readonly targets: readonly (infer Target extends MarquetteTarget)[];\n}\n ? Target\n : never;\n\n/** Exact capability identifier union inferred from authored capability keys. */\nexport type CapabilityId<Marquette extends ApplicationMarquette> = Marquette extends {\n readonly capabilities: Readonly<Record<infer Id extends string, CapabilityDefinition>>;\n}\n ? Id\n : never;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgEA,SAAgB,2BACd,WACW;CACX,OAAO;;;;;AClET,MAAa,2BAA2B"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/author.ts","../src/model.ts"],"sourcesContent":["import type {\n ApplicationMarquette,\n BackendId,\n CapabilityDefinition,\n CapabilityId,\n EnvironmentId,\n MarquetteBackend,\n MarquetteEnvironment,\n MarquetteProtocol,\n MarquetteRoute,\n ProtocolId,\n ServerEnvironmentId,\n TargetId,\n} from \"./model.js\";\n\n/**\n * Cross-reference constraints derived from the literals in one marquette.\n *\n * Keeping this type separate makes editor diagnostics point at the authored\n * reference instead of widening every identifier to `string`.\n */\nexport type MarquetteReferenceConstraints<Marquette extends ApplicationMarquette> = {\n readonly capabilities?: {\n readonly [Id in CapabilityId<Marquette>]: CapabilityDefinition & { readonly id: Id };\n };\n readonly environments?: readonly (MarquetteEnvironment<string, EnvironmentId<Marquette>> & {\n readonly capabilities?: readonly CapabilityId<Marquette>[];\n readonly target: TargetId<Marquette>;\n })[];\n readonly backends?: readonly (MarquetteBackend<string, ServerEnvironmentId<Marquette>> & {\n readonly capabilities?: readonly CapabilityId<Marquette>[];\n })[];\n readonly protocols?: readonly (MarquetteProtocol<string, BackendId<Marquette>> & {\n readonly capabilities?: readonly CapabilityId<Marquette>[];\n })[];\n readonly routes?: readonly (MarquetteRoute<\n string,\n EnvironmentId<Marquette>,\n BackendId<Marquette>,\n ProtocolId<Marquette>\n > & { readonly capabilities?: readonly CapabilityId<Marquette>[] })[];\n};\n\n/**\n * Defines an application marquette while preserving every authored literal.\n *\n * Environment dependencies, backend owners, protocol owners, and route\n * references are checked against identifiers declared in the same object.\n * The function returns its input without allocation or runtime work.\n *\n * @example\n * ```ts\n * const app = defineApplicationMarquette({\n * application: \"shop\",\n * targets: [\"web\"],\n * environments: [\n * { id: \"web\", target: \"web\", consumer: \"client\", runtime: \"browser\" },\n * ],\n * routes: [\n * { id: \"home\", path: \"/\", environment: \"web\", rendering: \"client\" },\n * ],\n * });\n * ```\n */\nexport function defineApplicationMarquette<const Marquette extends ApplicationMarquette>(\n marquette: Marquette & MarquetteReferenceConstraints<NoInfer<Marquette>>,\n): Marquette {\n return marquette;\n}\n","/** Current serialized application-marquette format. */\nexport const MARQUETTE_FORMAT_VERSION = 1 as const;\n\n/** User-visible target produced by an application marquette. */\nexport type MarquetteTarget = \"web\" | \"native\" | \"desktop\" | \"terminal\";\n\n/** Runtime family that executes one environment. */\nexport type RuntimeFamily =\n | \"browser\"\n | \"javascript\"\n | \"rust\"\n | \"go\"\n | \"jvm\"\n | \"native\"\n | \"desktop\"\n | \"terminal\"\n | \"external\";\n\n/** Side of the application graph that consumes an environment. */\nexport type EnvironmentConsumer = \"client\" | \"server\";\n\n/** Backend implementation family selected by an application. */\nexport type BackendFamily = \"javascript\" | \"rust\" | \"go\" | \"jvm\" | \"external\";\n\n/** Semantic family used by one data or procedure endpoint. */\nexport type ProtocolFamily = \"schema-query\" | \"typed-rpc\" | \"binary-rpc\";\n\n/** Rendering strategy selected for one route or target view. */\nexport type RenderingMode =\n | \"client\"\n | \"static\"\n | \"server\"\n | \"stream\"\n | \"partial\"\n | \"hybrid\"\n | \"native\"\n | \"desktop\"\n | \"terminal\";\n\n/** Versioned capability understood by adapters and developer tools. */\nexport interface CapabilityDefinition {\n /** Stable lowercase capability identifier. */\n readonly id: string;\n /** Summary shown by diagnostics, documentation, and AI tooling. */\n readonly description: string;\n /**\n * Capability contract version.\n *\n * @default 1\n */\n readonly version?: number;\n}\n\n/** Independently transformed and cached application environment. */\nexport interface MarquetteEnvironment<\n Id extends string = string,\n DependencyId extends string = string,\n> {\n /** Stable identifier, unique within the application. */\n readonly id: Id;\n /** User-visible target served by this environment. */\n readonly target: MarquetteTarget;\n /** Whether output is consumed by an end user or a trusted runtime. */\n readonly consumer: EnvironmentConsumer;\n /** Runtime family that executes the output. */\n readonly runtime: RuntimeFamily;\n /**\n * Authored entry module.\n *\n * @default undefined\n */\n readonly entry?: string;\n /**\n * Environments that must build first.\n *\n * @default []\n */\n readonly dependsOn?: readonly DependencyId[];\n /**\n * Capabilities required from the environment adapter.\n *\n * @default []\n */\n readonly capabilities?: readonly string[];\n}\n\n/** Backend application or independently deployed service. */\nexport interface MarquetteBackend<\n Id extends string = string,\n EnvironmentId extends string = string,\n> {\n /** Stable backend identifier. */\n readonly id: Id;\n /** Backend implementation family. */\n readonly family: BackendFamily;\n /**\n * Server environment that owns local execution.\n *\n * @default undefined\n */\n readonly environment?: EnvironmentId;\n /**\n * Capabilities required from the backend adapter.\n *\n * @default []\n */\n readonly capabilities?: readonly string[];\n}\n\n/** Protocol endpoint exposed by a backend. */\nexport interface MarquetteProtocol<Id extends string = string, BackendId extends string = string> {\n /** Stable endpoint identifier. */\n readonly id: Id;\n /** Protocol semantic family. */\n readonly family: ProtocolFamily;\n /** Backend that owns the endpoint. */\n readonly backend: BackendId;\n /**\n * Capabilities required from the protocol adapter.\n *\n * @default []\n */\n readonly capabilities?: readonly string[];\n}\n\n/** Typed application route or target view. */\nexport interface MarquetteRoute<\n Id extends string = string,\n EnvironmentId extends string = string,\n BackendId extends string = string,\n ProtocolId extends string = string,\n> {\n /** Stable identifier used by generated navigation APIs. */\n readonly id: Id;\n /** Logical path, always beginning with `/`. */\n readonly path: `/${string}`;\n /** Environment that owns the rendered route. */\n readonly environment: EnvironmentId;\n /** Rendering strategy used by the route. */\n readonly rendering: RenderingMode;\n /**\n * Backend used by data loading, mutations, or server rendering.\n *\n * @default undefined\n */\n readonly backend?: BackendId;\n /**\n * Protocol endpoint used as the route data contract.\n *\n * @default undefined\n */\n readonly protocol?: ProtocolId;\n /**\n * Capabilities required by the route.\n *\n * @default []\n */\n readonly capabilities?: readonly string[];\n}\n\n/** Complete, versioned application marquette. */\nexport interface ApplicationMarquette<\n Environments extends readonly MarquetteEnvironment[] = readonly MarquetteEnvironment[],\n Backends extends readonly MarquetteBackend[] = readonly MarquetteBackend[],\n Protocols extends readonly MarquetteProtocol[] = readonly MarquetteProtocol[],\n Routes extends readonly MarquetteRoute[] = readonly MarquetteRoute[],\n> {\n /**\n * Serialized contract format.\n *\n * @default 1\n */\n readonly formatVersion?: typeof MARQUETTE_FORMAT_VERSION;\n /** Stable lowercase application identifier. */\n readonly application: string;\n /**\n * User-visible application targets.\n *\n * @default []\n */\n readonly targets?: readonly MarquetteTarget[];\n /**\n * Versioned capability definitions keyed by identifier.\n *\n * @default {}\n */\n readonly capabilities?: Readonly<Record<string, CapabilityDefinition>>;\n /**\n * Independently transformed application environments.\n *\n * @default []\n */\n readonly environments?: Environments;\n /**\n * Backend applications and services.\n *\n * @default []\n */\n readonly backends?: Backends;\n /**\n * Protocol endpoints.\n *\n * @default []\n */\n readonly protocols?: Protocols;\n /**\n * Typed routes and target views.\n *\n * @default []\n */\n readonly routes?: Routes;\n}\n\n/** Extracts identifiers only when a collection is present in the authored object. */\ntype DeclaredId<Marquette, Collection extends PropertyKey> = Marquette extends {\n readonly [Key in Collection]: readonly { readonly id: infer Id extends string }[];\n}\n ? Id\n : never;\n\n/** Exact environment identifier union inferred from a marquette. */\nexport type EnvironmentId<Marquette extends ApplicationMarquette> = DeclaredId<\n Marquette,\n \"environments\"\n>;\n\n/** Exact server-owned environment identifier union inferred from a marquette. */\nexport type ServerEnvironmentId<Marquette extends ApplicationMarquette> = Marquette extends {\n readonly environments: readonly (infer Environment)[];\n}\n ? Environment extends {\n readonly consumer: \"server\";\n readonly id: infer Id extends string;\n }\n ? Id\n : never\n : never;\n\n/** Exact backend identifier union inferred from a marquette. */\nexport type BackendId<Marquette extends ApplicationMarquette> = DeclaredId<Marquette, \"backends\">;\n\n/** Exact protocol identifier union inferred from a marquette. */\nexport type ProtocolId<Marquette extends ApplicationMarquette> = DeclaredId<Marquette, \"protocols\">;\n\n/** Exact route identifier union inferred from a marquette. */\nexport type RouteId<Marquette extends ApplicationMarquette> = DeclaredId<Marquette, \"routes\">;\n\n/** Exact target union inferred from the authored target list. */\nexport type TargetId<Marquette extends ApplicationMarquette> = Marquette extends {\n readonly targets: readonly (infer Target extends MarquetteTarget)[];\n}\n ? Target\n : never;\n\n/** Exact capability identifier union inferred from authored capability keys. */\nexport type CapabilityId<Marquette extends ApplicationMarquette> = Marquette extends {\n readonly capabilities: Readonly<Record<infer Id extends string, CapabilityDefinition>>;\n}\n ? Id\n : never;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgEA,SAAgB,2BACd,WACW;CACX,OAAO;AACT;;;;ACnEA,MAAa,2BAA2B"}
|
|
@@ -194,4 +194,4 @@ type CapabilityId<Marquette extends ApplicationMarquette> = Marquette extends {
|
|
|
194
194
|
} ? Id : never;
|
|
195
195
|
//#endregion
|
|
196
196
|
export { RouteId as _, CapabilityId as a, TargetId as b, MARQUETTE_FORMAT_VERSION as c, MarquetteProtocol as d, MarquetteRoute as f, RenderingMode as g, ProtocolId as h, CapabilityDefinition as i, MarquetteBackend as l, ProtocolFamily as m, BackendFamily as n, EnvironmentConsumer as o, MarquetteTarget as p, BackendId as r, EnvironmentId as s, ApplicationMarquette as t, MarquetteEnvironment as u, RuntimeFamily as v, ServerEnvironmentId as y };
|
|
197
|
-
//# sourceMappingURL=model-
|
|
197
|
+
//# sourceMappingURL=model-B_mM8AeH.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-B_mM8AeH.d.mts","names":[],"sources":["../src/model.ts"],"mappings":";;cACa,wBAAA;;KAGD,eAAA;;KAGA,aAAA;AAHZ;AAAA,KAeY,mBAAA;;KAGA,aAAA;AAlBe;AAAA,KAqBf,cAAA;;KAGA,aAAA;;UAYK,oBAAA;EArBL;EAAA,SAuBD,EAAA;;WAEA,WAAA;EAzBoB;AAG/B;;;;EAH+B,SA+BpB,OAAA;AAAA;;UAIM,oBAAA;EA7BS;EAAA,SAkCf,EAAA,EAAI,EAAA;EA/BH;EAAA,SAiCD,MAAA,EAAQ,eAAA;;WAER,QAAA,EAAU,mBAAA;EAnCI;EAAA,SAqCd,OAAA,EAAS,aAAA;EAzBiB;;;;;EAAA,SA+B1B,KAAA;EArBO;AAAA;AAIlB;;;EAJkB,SA2BP,SAAA,YAAqB,YAAA;EAhBb;;;;;EAAA,SAsBR,YAAA;AAAA;;UAIM,gBAAA;EA5BF;EAAA,SAiCJ,EAAA,EAAI,EAAA;EA/BI;EAAA,SAiCR,MAAA,EAAQ,aAAA;EA/BE;;;;;EAAA,SAqCV,WAAA,GAAc,aAAA;EAjBd;;AAAY;AAIvB;;EAJW,SAuBA,YAAA;AAAA;;UAIM,iBAAA;EAVqB;EAAA,SAY3B,EAAA,EAAI,EAAA;EAxBb;EAAA,SA0BS,MAAA,EAAQ,cAAA;EAtBR;EAAA,SAwBA,OAAA,EAAS,SAAA;EAtBT;;;;;EAAA,SA4BA,YAAA;AAAA;AAZX;AAAA,UAgBiB,cAAA;EAhBiB;EAAA,SAuBvB,EAAA,EAAI,EAAA;EAnBI;EAAA,SAqBR,IAAA;EAnBkB;EAAA,SAqBlB,WAAA,EAAa,aAAA;EA3BW;EAAA,SA6BxB,SAAA,EAAW,aAAA;EA3BX;;;;;EAAA,SAiCA,OAAA,GAAU,SAAA;EAvBV;;AAAY;AAIvB;;EAJW,SA6BA,QAAA,GAAW,UAAA;EAlBP;;;;;EAAA,SAwBJ,YAAA;AAAA;;UAIM,oBAAA,+BACe,oBAAA,cAAkC,oBAAA,8BACtC,gBAAA,cAA8B,gBAAA,+BAC7B,iBAAA,cAA+B,iBAAA,4BAClC,cAAA,cAA4B,cAAA;EApCpD;;;;;EAAA,SA2CS,aAAA,UAAuB,wBAAA;EAnCV;EAAA,SAqCb,WAAA;EAnCW;;;;;EAAA,SAyCX,OAAA,YAAmB,eAAA;EAvBP;AAAA;AAIvB;;;EAJuB,SA6BZ,YAAA,GAAe,QAAA,CAAS,MAAA,SAAe,oBAAA;EAxBgB;;;;;EAAA,SA8BvD,YAAA,GAAe,YAAA;EA3B4B;;;;;EAAA,SAiC3C,QAAA,GAAW,QAAA;EANI;;;;;EAAA,SAYf,SAAA,GAAY,SAAA;EA1CrB;;;;;EAAA,SAgDS,MAAA,GAAS,MAAA;AAAA;;KAIf,UAAA,+BAAyC,WAAA,IAAe,SAAA,4BAC1C,UAAA;EAAA,SAAiC,EAAA;AAAA,QAEhD,EAAA;;KAIQ,aAAA,mBAAgC,oBAAA,IAAwB,UAAA,CAClE,SAAA;;KAKU,mBAAA,mBAAsC,oBAAA,IAAwB,SAAS;EAAA,SACxE,YAAA;AAAA,IAEP,WAAA;EAAA,SACW,QAAA;EAAA,SACA,EAAA;AAAA,IAET,EAAA;;KAKM,SAAA,mBAA4B,oBAAA,IAAwB,UAAA,CAAW,SAAA;;KAG/D,UAAA,mBAA6B,oBAAA,IAAwB,UAAA,CAAW,SAAA;;KAGhE,OAAA,mBAA0B,oBAAA,IAAwB,UAAA,CAAW,SAAA;;KAG7D,QAAA,mBAA2B,oBAAA,IAAwB,SAAA;EAAA,SACpD,OAAA,iCAAwC,eAAA;AAAA,IAE/C,MAAA;;KAIQ,YAAA,mBAA+B,oBAAA,IAAwB,SAAA;EAAA,SACxD,YAAA,EAAc,QAAA,CAAS,MAAA,0BAAgC,oBAAA;AAAA,IAE9D,EAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as TestRunEvidence } from "./test-run-model-
|
|
1
|
+
import { i as TestRunEvidence } from "./test-run-model-CF2EYJOQ.mjs";
|
|
2
2
|
import { MarquetteDiagnostic } from "./validate.mjs";
|
|
3
|
-
import { r as parseTestRunAdmissionId, t as TEST_RUN_ADMISSION_PREFIX } from "./test-run-canonical-
|
|
3
|
+
import { r as parseTestRunAdmissionId, t as TEST_RUN_ADMISSION_PREFIX } from "./test-run-canonical-B1IuzC5s.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/test-run-admission.d.ts
|
|
6
6
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-admission.d.mts","names":[],"sources":["../src/test-run-admission.ts"],"mappings":";;;;;;;AAcA;;;;UAAiB,gBAAA;EAIN;EAAA,SAFA,WAAA;EAMA;EAAA,SAJA,WAAA;EAQA;EAAA,SANA,mBAAA;EAMmB;EAAA,SAJnB,cAAA;EA0BuB;EAAA,SAxBvB,OAAA;EAyBC;EAAA,SAvBD,mBAAA;AAAA
|
|
1
|
+
{"version":3,"file":"test-run-admission.d.mts","names":[],"sources":["../src/test-run-admission.ts"],"mappings":";;;;;;;AAcA;;;;UAAiB,gBAAA;EAIN;EAAA,SAFA,WAAA;EAMA;EAAA,SAJA,WAAA;EAQA;EAAA,SANA,mBAAA;EAMmB;EAAA,SAJnB,cAAA;EA0BuB;EAAA,SAxBvB,OAAA;EAyBC;EAAA,SAvBD,mBAAA;AAAA;;;;;;;;;;;;;AA2BmB;AAoG9B;;;;AAqBU;AAGV;iBAjIsB,YAAA,CACpB,QAAA,EAAU,eAAA,EACV,SAAA,EAAW,gBAAA,EACX,WAAA,UACA,GAAA,WACC,OAAA,CAAQ,mBAAA;;;AA4HkD;AAW7D;;;;;;cAnCa,qBAAA;;KAwBD,iBAAA,WAA4B,qBAAqB;;AAiBT;AA0BpD;;;;;;;UAhCiB,wBAAA;EAgCoE;EAAA,SA9B1E,OAAA;EAqFiC;EAAA,SAnFjC,WAAA,WAAsB,iBAAA;EAoFrB;EAAA,SAlFD,WAAA,WAAsB,mBAAmB;AAAA;;;;;;;;;;;;;AAsFjB;;iBA5DnB,iBAAA,CAAkB,UAAA,EAAY,mBAAA,GAAsB,iBAAiB;;;;;;;;;;;;iBAuD/D,sBAAA,CACpB,QAAA,EAAU,eAAA,EACV,SAAA,EAAW,gBAAA,EACX,WAAA,UACA,GAAA,WACC,OAAA,CAAQ,wBAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as isStrictTimestamp, t as validateTestRunEvidence, u as error } from "./test-run-validate-
|
|
1
|
+
import { d as isStrictTimestamp, t as validateTestRunEvidence, u as error } from "./test-run-validate-DwGMo8ON.mjs";
|
|
2
2
|
import { TEST_RUN_ADMISSION_PREFIX, parseTestRunAdmissionId, testRunFingerprint } from "./test-run-canonical.mjs";
|
|
3
3
|
//#region src/test-run-admission.ts
|
|
4
4
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-admission.mjs","names":[],"sources":["../src/test-run-admission.ts"],"sourcesContent":["import type { TestRunEvidence } from \"./test-run-model.js\";\nimport type { MarquetteDiagnostic } from \"./validate.js\";\nimport { parseTestRunAdmissionId, testRunFingerprint } from \"./test-run-canonical.js\";\nimport { validateTestRunEvidence } from \"./test-run-validate.js\";\nimport { error, isStrictTimestamp } from \"./test-run-validate-rules.js\";\n\nexport { TEST_RUN_ADMISSION_PREFIX, parseTestRunAdmissionId } from \"./test-run-canonical.js\";\n\n/**\n * Exact release candidate a deployment gate wants evidence for.\n *\n * Every field must match the record exactly; admission never falls back to a\n * newer, older, or partially matching record.\n */\nexport interface TestRunCandidate {\n /** Application the gate is deploying. */\n readonly application: string;\n /** Deployment environment the gate is promoting into. */\n readonly environment: string;\n /** Lowercase SHA-256 fingerprint of the application contract. */\n readonly contractFingerprint: string;\n /** Exact source revision of the candidate. */\n readonly sourceRevision: string;\n /** Release the candidate belongs to. */\n readonly release: string;\n /** Lowercase SHA-256 fingerprint of the exact artifact being promoted. */\n readonly artifactFingerprint: string;\n}\n\n/**\n * Decides whether one record admits one exact candidate at one instant.\n *\n * An empty result admits the deployment. Any diagnostic rejects it: the\n * record must validate cleanly, its canonical fingerprint must be the one\n * named by `admissionId`, every candidate binding must match exactly, the\n * record must not be expired at `now`, the independent verification must\n * have accepted the run, and no skipped test may remain unaccounted for.\n *\n * Codes, paths, messages, and ordering are identical to the native\n * implementation, so both host families reach the same decision for the\n * same record.\n *\n * `now` must be a millisecond-precision UTC timestamp such as\n * `2026-01-01T00:00:00.000Z`; the fixed-width format keeps the expiry\n * comparison exact. Callers own retrieval: fetch the canonical bytes from an\n * immutable store within their own deadline, refuse oversized content, and\n * hand the parsed record here.\n */\nexport async function admitTestRun(\n evidence: TestRunEvidence,\n candidate: TestRunCandidate,\n admissionId: string,\n now: string,\n): Promise<MarquetteDiagnostic[]> {\n const diagnostics = validateTestRunEvidence(evidence);\n\n const nowIsExact = isStrictTimestamp(now);\n if (!nowIsExact) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_148\",\n \"admission.now\",\n \"admission time must be a millisecond-precision UTC instant\",\n ),\n );\n }\n\n const expected = parseTestRunAdmissionId(admissionId);\n if (expected === undefined) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"admission.id\",\n \"admission id must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n } else if ((await testRunFingerprint(evidence)) !== expected) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_142\",\n \"admission.id\",\n \"admission id does not name this record's canonical fingerprint\",\n ),\n );\n }\n\n const bindings = [\n [evidence.application, candidate.application, \"application\"],\n [evidence.environment, candidate.environment, \"environment\"],\n [evidence.contractFingerprint, candidate.contractFingerprint, \"contractFingerprint\"],\n [evidence.sourceRevision, candidate.sourceRevision, \"sourceRevision\"],\n [evidence.release, candidate.release, \"release\"],\n [evidence.artifact.fingerprint, candidate.artifactFingerprint, \"artifact.fingerprint\"],\n ] as const;\n for (const [recorded, wanted, field] of bindings) {\n if (recorded !== wanted) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_144\", field, `record does not bind the candidate ${field}`),\n );\n }\n }\n\n if (nowIsExact && evidence.validUntil <= now) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_145\", \"validUntil\", \"record is expired at the admission time\"),\n );\n }\n if (evidence.verification.outcome !== \"accepted\") {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_146\",\n \"verification.outcome\",\n \"only an accepted verification can admit a deployment\",\n ),\n );\n }\n if (evidence.verification.skipped > 0) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_147\",\n \"verification.skipped\",\n \"skipped tests are not approved for deployment admission\",\n ),\n );\n }\n\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n return diagnostics;\n}\n\n/**\n * Every denial code, in the stable lexicographic decision order.\n *\n * The vocabulary is shared by every backend family: a JavaScript, Rust, Go,\n * or JVM host must derive the same codes from the same diagnostics, as\n * pinned by the shared `tests/fixtures/test-run-evidence` decision fixtures.\n * Codes are append-only: they are never renamed, renumbered, reused, or\n * removed, and a new rejection cause always ships with a new code.\n */\nexport const TEST_RUN_DENIAL_CODES = [\n \"admission-id-malformed\",\n \"admission-id-mismatch\",\n \"admission-time-malformed\",\n \"candidate-application-mismatch\",\n \"candidate-artifact-fingerprint-mismatch\",\n \"candidate-contract-fingerprint-mismatch\",\n \"candidate-environment-mismatch\",\n \"candidate-release-mismatch\",\n \"candidate-source-revision-mismatch\",\n \"check-candidate-mismatch\",\n \"check-invalid\",\n \"check-observer-not-independent\",\n \"record-expired\",\n \"record-invalid\",\n \"skipped-tests-recorded\",\n \"transition-chain-broken\",\n \"transition-invalid\",\n \"transition-replayed\",\n \"transition-state-mismatch\",\n \"verification-not-accepted\",\n] as const;\n\n/** Stable machine-readable cause class of one admission denial. */\nexport type TestRunDenialCode = (typeof TEST_RUN_DENIAL_CODES)[number];\n\n/**\n * Structured allow-or-deny admission decision for one exact candidate.\n *\n * The decision carries the machine-readable cause classes next to the exact\n * diagnostics, so a deployment gate in any language can act on one bounded\n * vocabulary while operators keep the full explanation. Serialization\n * follows the shared `test-run-admission` schema; decisions are outputs, so\n * a gate must never trust a decision it did not compute itself.\n */\nexport interface TestRunAdmissionDecision {\n /** Whether the record admits the candidate; true only with no diagnostics. */\n readonly allowed: boolean;\n /** Deduplicated denial causes sorted lexicographically; empty when allowed. */\n readonly denialCodes: readonly TestRunDenialCode[];\n /** Complete diagnostics in the stable path, code, message order. */\n readonly diagnostics: readonly MarquetteDiagnostic[];\n}\n\nconst CANDIDATE_MISMATCH_CODES: ReadonlyMap<string, TestRunDenialCode> = new Map([\n [\"application\", \"candidate-application-mismatch\"],\n [\"artifact.fingerprint\", \"candidate-artifact-fingerprint-mismatch\"],\n [\"contractFingerprint\", \"candidate-contract-fingerprint-mismatch\"],\n [\"environment\", \"candidate-environment-mismatch\"],\n [\"release\", \"candidate-release-mismatch\"],\n [\"sourceRevision\", \"candidate-source-revision-mismatch\"],\n]);\n\n/**\n * Returns the stable denial code one admission diagnostic maps to.\n *\n * The mapping is total and identical in every host family: admission codes\n * `VIZE_MARQUETTE_141` through `VIZE_MARQUETTE_148` map to their exact\n * cause, `VIZE_MARQUETTE_144` distinguishes the mismatched candidate binding\n * by its diagnostic path, `VIZE_MARQUETTE_149` through `VIZE_MARQUETTE_151`\n * map to their tests-check cause, `VIZE_MARQUETTE_156` through\n * `VIZE_MARQUETTE_159` map to their transition cause, every other\n * diagnostic at a `check.` path is a `check-invalid` tests-check validation\n * failure, every other diagnostic at a `transition.` path is a\n * `transition-invalid` transition validation failure, and every remaining\n * diagnostic is a `record-invalid` record-validation failure.\n */\nexport function testRunDenialCode(diagnostic: MarquetteDiagnostic): TestRunDenialCode {\n switch (diagnostic.code) {\n case \"VIZE_MARQUETTE_141\":\n return \"admission-id-malformed\";\n case \"VIZE_MARQUETTE_142\":\n return \"admission-id-mismatch\";\n case \"VIZE_MARQUETTE_144\": {\n const mismatch = CANDIDATE_MISMATCH_CODES.get(diagnostic.path);\n if (mismatch !== undefined) {\n return mismatch;\n }\n break;\n }\n case \"VIZE_MARQUETTE_145\":\n return \"record-expired\";\n case \"VIZE_MARQUETTE_146\":\n return \"verification-not-accepted\";\n case \"VIZE_MARQUETTE_147\":\n return \"skipped-tests-recorded\";\n case \"VIZE_MARQUETTE_148\":\n return \"admission-time-malformed\";\n case \"VIZE_MARQUETTE_149\":\n return \"check-candidate-mismatch\";\n case \"VIZE_MARQUETTE_150\":\n return \"check-invalid\";\n case \"VIZE_MARQUETTE_151\":\n return \"check-observer-not-independent\";\n case \"VIZE_MARQUETTE_156\":\n return \"transition-state-mismatch\";\n case \"VIZE_MARQUETTE_157\":\n return \"transition-chain-broken\";\n case \"VIZE_MARQUETTE_158\":\n return \"transition-replayed\";\n case \"VIZE_MARQUETTE_159\":\n return \"transition-state-mismatch\";\n default:\n break;\n }\n if (diagnostic.path.startsWith(\"check.\")) {\n return \"check-invalid\";\n }\n return diagnostic.path.startsWith(\"transition.\") ? \"transition-invalid\" : \"record-invalid\";\n}\n\n/**\n * Decides one candidate and returns the structured admission decision.\n *\n * The decision wraps {@link admitTestRun}: `diagnostics` is exactly its\n * result, `denialCodes` maps every diagnostic through\n * {@link testRunDenialCode} and then deduplicates and sorts the codes\n * lexicographically, and `allowed` is true only when both are empty. Codes,\n * ordering, and diagnostics are identical to the native implementation, as\n * pinned by the shared decision fixtures. Inputs carry the same obligations\n * as {@link admitTestRun}.\n */\nexport async function decideTestRunAdmission(\n evidence: TestRunEvidence,\n candidate: TestRunCandidate,\n admissionId: string,\n now: string,\n): Promise<TestRunAdmissionDecision> {\n const diagnostics = await admitTestRun(evidence, candidate, admissionId, now);\n const denialCodes = [...new Set(diagnostics.map(testRunDenialCode))].sort();\n return { allowed: diagnostics.length === 0, denialCodes, diagnostics };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgDA,eAAsB,aACpB,UACA,WACA,aACA,KACgC;CAChC,MAAM,cAAc,wBAAwB,SAAS;CAErD,MAAM,aAAa,kBAAkB,IAAI;CACzC,IAAI,CAAC,YACH,YAAY,KACV,MACE,sBACA,iBACA,6DACD,CACF;CAGH,MAAM,WAAW,wBAAwB,YAAY;CACrD,IAAI,aAAa,KAAA,GACf,YAAY,KACV,MACE,sBACA,gBACA,iFACD,CACF;MACI,IAAK,MAAM,mBAAmB,SAAS,KAAM,UAClD,YAAY,KACV,MACE,sBACA,gBACA,iEACD,CACF;CAGH,MAAM,WAAW;EACf;GAAC,SAAS;GAAa,UAAU;GAAa;GAAc;EAC5D;GAAC,SAAS;GAAa,UAAU;GAAa;GAAc;EAC5D;GAAC,SAAS;GAAqB,UAAU;GAAqB;GAAsB;EACpF;GAAC,SAAS;GAAgB,UAAU;GAAgB;GAAiB;EACrE;GAAC,SAAS;GAAS,UAAU;GAAS;GAAU;EAChD;GAAC,SAAS,SAAS;GAAa,UAAU;GAAqB;GAAuB;EACvF;CACD,KAAK,MAAM,CAAC,UAAU,QAAQ,UAAU,UACtC,IAAI,aAAa,QACf,YAAY,KACV,MAAM,sBAAsB,OAAO,sCAAsC,QAAQ,CAClF;CAIL,IAAI,cAAc,SAAS,cAAc,KACvC,YAAY,KACV,MAAM,sBAAsB,cAAc,0CAA0C,CACrF;CAEH,IAAI,SAAS,aAAa,YAAY,YACpC,YAAY,KACV,MACE,sBACA,wBACA,uDACD,CACF;CAEH,IAAI,SAAS,aAAa,UAAU,GAClC,YAAY,KACV,MACE,sBACA,wBACA,0DACD,CACF;CAGH,YAAY,MAAM,MAAM,UACtB,KAAK,SAAS,MAAM,OAChB,KAAK,OAAO,MAAM,OAChB,KACA,IACF,KAAK,SAAS,MAAM,OAClB,KAAK,OAAO,MAAM,OAChB,KACA,IACF,KAAK,UAAU,MAAM,UACnB,KACA,KAAK,UAAU,MAAM,UACnB,IACA,EACX;CACD,OAAO;;;;;;;;;;;AAYT,MAAa,wBAAwB;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAuBD,MAAM,2BAAmE,IAAI,IAAI;CAC/E,CAAC,eAAe,iCAAiC;CACjD,CAAC,wBAAwB,0CAA0C;CACnE,CAAC,uBAAuB,0CAA0C;CAClE,CAAC,eAAe,iCAAiC;CACjD,CAAC,WAAW,6BAA6B;CACzC,CAAC,kBAAkB,qCAAqC;CACzD,CAAC;;;;;;;;;;;;;;;AAgBF,SAAgB,kBAAkB,YAAoD;CACpF,QAAQ,WAAW,MAAnB;EACE,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBAAsB;GACzB,MAAM,WAAW,yBAAyB,IAAI,WAAW,KAAK;GAC9D,IAAI,aAAa,KAAA,GACf,OAAO;GAET;;EAEF,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,KAAK,sBACH,OAAO;EACT,SACE;;CAEJ,IAAI,WAAW,KAAK,WAAW,SAAS,EACtC,OAAO;CAET,OAAO,WAAW,KAAK,WAAW,cAAc,GAAG,uBAAuB;;;;;;;;;;;;;AAc5E,eAAsB,uBACpB,UACA,WACA,aACA,KACmC;CACnC,MAAM,cAAc,MAAM,aAAa,UAAU,WAAW,aAAa,IAAI;CAC7E,MAAM,cAAc,CAAC,GAAG,IAAI,IAAI,YAAY,IAAI,kBAAkB,CAAC,CAAC,CAAC,MAAM;CAC3E,OAAO;EAAE,SAAS,YAAY,WAAW;EAAG;EAAa;EAAa"}
|
|
1
|
+
{"version":3,"file":"test-run-admission.mjs","names":[],"sources":["../src/test-run-admission.ts"],"sourcesContent":["import type { TestRunEvidence } from \"./test-run-model.js\";\nimport type { MarquetteDiagnostic } from \"./validate.js\";\nimport { parseTestRunAdmissionId, testRunFingerprint } from \"./test-run-canonical.js\";\nimport { validateTestRunEvidence } from \"./test-run-validate.js\";\nimport { error, isStrictTimestamp } from \"./test-run-validate-rules.js\";\n\nexport { TEST_RUN_ADMISSION_PREFIX, parseTestRunAdmissionId } from \"./test-run-canonical.js\";\n\n/**\n * Exact release candidate a deployment gate wants evidence for.\n *\n * Every field must match the record exactly; admission never falls back to a\n * newer, older, or partially matching record.\n */\nexport interface TestRunCandidate {\n /** Application the gate is deploying. */\n readonly application: string;\n /** Deployment environment the gate is promoting into. */\n readonly environment: string;\n /** Lowercase SHA-256 fingerprint of the application contract. */\n readonly contractFingerprint: string;\n /** Exact source revision of the candidate. */\n readonly sourceRevision: string;\n /** Release the candidate belongs to. */\n readonly release: string;\n /** Lowercase SHA-256 fingerprint of the exact artifact being promoted. */\n readonly artifactFingerprint: string;\n}\n\n/**\n * Decides whether one record admits one exact candidate at one instant.\n *\n * An empty result admits the deployment. Any diagnostic rejects it: the\n * record must validate cleanly, its canonical fingerprint must be the one\n * named by `admissionId`, every candidate binding must match exactly, the\n * record must not be expired at `now`, the independent verification must\n * have accepted the run, and no skipped test may remain unaccounted for.\n *\n * Codes, paths, messages, and ordering are identical to the native\n * implementation, so both host families reach the same decision for the\n * same record.\n *\n * `now` must be a millisecond-precision UTC timestamp such as\n * `2026-01-01T00:00:00.000Z`; the fixed-width format keeps the expiry\n * comparison exact. Callers own retrieval: fetch the canonical bytes from an\n * immutable store within their own deadline, refuse oversized content, and\n * hand the parsed record here.\n */\nexport async function admitTestRun(\n evidence: TestRunEvidence,\n candidate: TestRunCandidate,\n admissionId: string,\n now: string,\n): Promise<MarquetteDiagnostic[]> {\n const diagnostics = validateTestRunEvidence(evidence);\n\n const nowIsExact = isStrictTimestamp(now);\n if (!nowIsExact) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_148\",\n \"admission.now\",\n \"admission time must be a millisecond-precision UTC instant\",\n ),\n );\n }\n\n const expected = parseTestRunAdmissionId(admissionId);\n if (expected === undefined) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"admission.id\",\n \"admission id must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n } else if ((await testRunFingerprint(evidence)) !== expected) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_142\",\n \"admission.id\",\n \"admission id does not name this record's canonical fingerprint\",\n ),\n );\n }\n\n const bindings = [\n [evidence.application, candidate.application, \"application\"],\n [evidence.environment, candidate.environment, \"environment\"],\n [evidence.contractFingerprint, candidate.contractFingerprint, \"contractFingerprint\"],\n [evidence.sourceRevision, candidate.sourceRevision, \"sourceRevision\"],\n [evidence.release, candidate.release, \"release\"],\n [evidence.artifact.fingerprint, candidate.artifactFingerprint, \"artifact.fingerprint\"],\n ] as const;\n for (const [recorded, wanted, field] of bindings) {\n if (recorded !== wanted) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_144\", field, `record does not bind the candidate ${field}`),\n );\n }\n }\n\n if (nowIsExact && evidence.validUntil <= now) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_145\", \"validUntil\", \"record is expired at the admission time\"),\n );\n }\n if (evidence.verification.outcome !== \"accepted\") {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_146\",\n \"verification.outcome\",\n \"only an accepted verification can admit a deployment\",\n ),\n );\n }\n if (evidence.verification.skipped > 0) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_147\",\n \"verification.skipped\",\n \"skipped tests are not approved for deployment admission\",\n ),\n );\n }\n\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n return diagnostics;\n}\n\n/**\n * Every denial code, in the stable lexicographic decision order.\n *\n * The vocabulary is shared by every backend family: a JavaScript, Rust, Go,\n * or JVM host must derive the same codes from the same diagnostics, as\n * pinned by the shared `tests/fixtures/test-run-evidence` decision fixtures.\n * Codes are append-only: they are never renamed, renumbered, reused, or\n * removed, and a new rejection cause always ships with a new code.\n */\nexport const TEST_RUN_DENIAL_CODES = [\n \"admission-id-malformed\",\n \"admission-id-mismatch\",\n \"admission-time-malformed\",\n \"candidate-application-mismatch\",\n \"candidate-artifact-fingerprint-mismatch\",\n \"candidate-contract-fingerprint-mismatch\",\n \"candidate-environment-mismatch\",\n \"candidate-release-mismatch\",\n \"candidate-source-revision-mismatch\",\n \"check-candidate-mismatch\",\n \"check-invalid\",\n \"check-observer-not-independent\",\n \"record-expired\",\n \"record-invalid\",\n \"skipped-tests-recorded\",\n \"transition-chain-broken\",\n \"transition-invalid\",\n \"transition-replayed\",\n \"transition-state-mismatch\",\n \"verification-not-accepted\",\n] as const;\n\n/** Stable machine-readable cause class of one admission denial. */\nexport type TestRunDenialCode = (typeof TEST_RUN_DENIAL_CODES)[number];\n\n/**\n * Structured allow-or-deny admission decision for one exact candidate.\n *\n * The decision carries the machine-readable cause classes next to the exact\n * diagnostics, so a deployment gate in any language can act on one bounded\n * vocabulary while operators keep the full explanation. Serialization\n * follows the shared `test-run-admission` schema; decisions are outputs, so\n * a gate must never trust a decision it did not compute itself.\n */\nexport interface TestRunAdmissionDecision {\n /** Whether the record admits the candidate; true only with no diagnostics. */\n readonly allowed: boolean;\n /** Deduplicated denial causes sorted lexicographically; empty when allowed. */\n readonly denialCodes: readonly TestRunDenialCode[];\n /** Complete diagnostics in the stable path, code, message order. */\n readonly diagnostics: readonly MarquetteDiagnostic[];\n}\n\nconst CANDIDATE_MISMATCH_CODES: ReadonlyMap<string, TestRunDenialCode> = new Map([\n [\"application\", \"candidate-application-mismatch\"],\n [\"artifact.fingerprint\", \"candidate-artifact-fingerprint-mismatch\"],\n [\"contractFingerprint\", \"candidate-contract-fingerprint-mismatch\"],\n [\"environment\", \"candidate-environment-mismatch\"],\n [\"release\", \"candidate-release-mismatch\"],\n [\"sourceRevision\", \"candidate-source-revision-mismatch\"],\n]);\n\n/**\n * Returns the stable denial code one admission diagnostic maps to.\n *\n * The mapping is total and identical in every host family: admission codes\n * `VIZE_MARQUETTE_141` through `VIZE_MARQUETTE_148` map to their exact\n * cause, `VIZE_MARQUETTE_144` distinguishes the mismatched candidate binding\n * by its diagnostic path, `VIZE_MARQUETTE_149` through `VIZE_MARQUETTE_151`\n * map to their tests-check cause, `VIZE_MARQUETTE_156` through\n * `VIZE_MARQUETTE_159` map to their transition cause, every other\n * diagnostic at a `check.` path is a `check-invalid` tests-check validation\n * failure, every other diagnostic at a `transition.` path is a\n * `transition-invalid` transition validation failure, and every remaining\n * diagnostic is a `record-invalid` record-validation failure.\n */\nexport function testRunDenialCode(diagnostic: MarquetteDiagnostic): TestRunDenialCode {\n switch (diagnostic.code) {\n case \"VIZE_MARQUETTE_141\":\n return \"admission-id-malformed\";\n case \"VIZE_MARQUETTE_142\":\n return \"admission-id-mismatch\";\n case \"VIZE_MARQUETTE_144\": {\n const mismatch = CANDIDATE_MISMATCH_CODES.get(diagnostic.path);\n if (mismatch !== undefined) {\n return mismatch;\n }\n break;\n }\n case \"VIZE_MARQUETTE_145\":\n return \"record-expired\";\n case \"VIZE_MARQUETTE_146\":\n return \"verification-not-accepted\";\n case \"VIZE_MARQUETTE_147\":\n return \"skipped-tests-recorded\";\n case \"VIZE_MARQUETTE_148\":\n return \"admission-time-malformed\";\n case \"VIZE_MARQUETTE_149\":\n return \"check-candidate-mismatch\";\n case \"VIZE_MARQUETTE_150\":\n return \"check-invalid\";\n case \"VIZE_MARQUETTE_151\":\n return \"check-observer-not-independent\";\n case \"VIZE_MARQUETTE_156\":\n return \"transition-state-mismatch\";\n case \"VIZE_MARQUETTE_157\":\n return \"transition-chain-broken\";\n case \"VIZE_MARQUETTE_158\":\n return \"transition-replayed\";\n case \"VIZE_MARQUETTE_159\":\n return \"transition-state-mismatch\";\n default:\n break;\n }\n if (diagnostic.path.startsWith(\"check.\")) {\n return \"check-invalid\";\n }\n return diagnostic.path.startsWith(\"transition.\") ? \"transition-invalid\" : \"record-invalid\";\n}\n\n/**\n * Decides one candidate and returns the structured admission decision.\n *\n * The decision wraps {@link admitTestRun}: `diagnostics` is exactly its\n * result, `denialCodes` maps every diagnostic through\n * {@link testRunDenialCode} and then deduplicates and sorts the codes\n * lexicographically, and `allowed` is true only when both are empty. Codes,\n * ordering, and diagnostics are identical to the native implementation, as\n * pinned by the shared decision fixtures. Inputs carry the same obligations\n * as {@link admitTestRun}.\n */\nexport async function decideTestRunAdmission(\n evidence: TestRunEvidence,\n candidate: TestRunCandidate,\n admissionId: string,\n now: string,\n): Promise<TestRunAdmissionDecision> {\n const diagnostics = await admitTestRun(evidence, candidate, admissionId, now);\n const denialCodes = [...new Set(diagnostics.map(testRunDenialCode))].sort();\n return { allowed: diagnostics.length === 0, denialCodes, diagnostics };\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as TestRunEvidence } from "./test-run-model-
|
|
1
|
+
import { i as TestRunEvidence } from "./test-run-model-CF2EYJOQ.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/test-run-canonical.d.ts
|
|
4
4
|
/** Prefix of every test-run deployment admission id. */
|
|
@@ -32,4 +32,4 @@ declare function testRunAdmissionId(evidence: TestRunEvidence): Promise<string>;
|
|
|
32
32
|
declare function parseTestRunAdmissionId(id: string): string | undefined;
|
|
33
33
|
//#endregion
|
|
34
34
|
export { testRunFingerprint as a, testRunAdmissionId as i, canonicalTestRunJson as n, parseTestRunAdmissionId as r, TEST_RUN_ADMISSION_PREFIX as t };
|
|
35
|
-
//# sourceMappingURL=test-run-canonical-
|
|
35
|
+
//# sourceMappingURL=test-run-canonical-B1IuzC5s.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-run-canonical-B1IuzC5s.d.mts","names":[],"sources":["../src/test-run-canonical.ts"],"mappings":";;;;cAQa,yBAAA;AAAb;;;;AAAsC;AAwCtC;;;;AAxCA,iBAwCgB,oBAAA,CAAqB,QAAyB,EAAf,eAAe;AA6D9D;;;;;;;AAAA,iBAAsB,kBAAA,CAAmB,QAAA,EAAU,eAAA,GAAkB,OAAO;AAAA;AAAA,iBAWtD,kBAAA,CAAmB,QAAA,EAAU,eAAA,GAAkB,OAAO;;;;;;;iBAU5D,uBAAA,CAAwB,EAAU"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as testRunFingerprint, i as testRunAdmissionId, n as canonicalTestRunJson, r as parseTestRunAdmissionId, t as TEST_RUN_ADMISSION_PREFIX } from "./test-run-canonical-
|
|
1
|
+
import { a as testRunFingerprint, i as testRunAdmissionId, n as canonicalTestRunJson, r as parseTestRunAdmissionId, t as TEST_RUN_ADMISSION_PREFIX } from "./test-run-canonical-B1IuzC5s.mjs";
|
|
2
2
|
export { TEST_RUN_ADMISSION_PREFIX, canonicalTestRunJson, parseTestRunAdmissionId, testRunAdmissionId, testRunFingerprint };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-canonical.mjs","names":[],"sources":["../src/test-run-canonical.ts"],"sourcesContent":["import type {\n TestRunEvidence,\n TestRunRetainedEvidence,\n TestRunSuiteExecution,\n TestRunTargetExecution,\n} from \"./test-run-model.js\";\n\n/** Prefix of every test-run deployment admission id. */\nexport const TEST_RUN_ADMISSION_PREFIX = \"test-run:\";\n\nconst ADMISSION_FINGERPRINT = /^[a-f0-9]{64}$/;\n\nfunction retained(evidence: TestRunRetainedEvidence): TestRunRetainedEvidence {\n return { reference: evidence.reference, fingerprint: evidence.fingerprint };\n}\n\nfunction target(execution: TestRunTargetExecution): TestRunTargetExecution {\n return { id: execution.id, kind: execution.kind, environment: execution.environment };\n}\n\nfunction suite(execution: TestRunSuiteExecution): TestRunSuiteExecution {\n return {\n id: execution.id,\n targetId: execution.targetId,\n kind: execution.kind,\n shardIndex: execution.shardIndex,\n shardCount: execution.shardCount,\n outcome: execution.outcome,\n passed: execution.passed,\n failed: execution.failed,\n skipped: execution.skipped,\n retries: execution.retries,\n durationMs: execution.durationMs,\n invocationFingerprint: execution.invocationFingerprint,\n report: retained(execution.report),\n log: retained(execution.log),\n };\n}\n\n/**\n * Serializes a test-run evidence record canonically.\n *\n * Property order matches the record schema, targets sort by id, suites sort\n * by id then shard index, and selection identifiers sort lexicographically,\n * so equivalent records produce byte-identical JSON in every language. Call\n * validation before trusting the record; canonicalization does not make an\n * invalid record valid.\n */\nexport function canonicalTestRunJson(evidence: TestRunEvidence): string {\n const canonical = {\n format: evidence.format,\n formatVersion: evidence.formatVersion ?? 1,\n id: evidence.id,\n application: evidence.application,\n environment: evidence.environment,\n contractFingerprint: evidence.contractFingerprint,\n sourceRevision: evidence.sourceRevision,\n release: evidence.release,\n artifact: {\n id: evidence.artifact.id,\n fingerprint: evidence.artifact.fingerprint,\n sizeBytes: evidence.artifact.sizeBytes,\n },\n startedAt: evidence.startedAt,\n completedAt: evidence.completedAt,\n validUntil: evidence.validUntil,\n runner: {\n identity: evidence.runner.identity,\n authenticationEvidence: retained(evidence.runner.authenticationEvidence),\n isolation: evidence.runner.isolation,\n invocationFingerprint: evidence.runner.invocationFingerprint,\n environmentEvidence: retained(evidence.runner.environmentEvidence),\n environmentFingerprint: evidence.runner.environmentFingerprint,\n },\n selection: {\n targetIds: [...evidence.selection.targetIds].sort(),\n suiteIds: [...evidence.selection.suiteIds].sort(),\n },\n targets: evidence.targets\n .map(target)\n .sort((left, right) => (left.id < right.id ? -1 : left.id > right.id ? 1 : 0)),\n suites: evidence.suites\n .map(suite)\n .sort((left, right) =>\n left.id !== right.id ? (left.id < right.id ? -1 : 1) : left.shardIndex - right.shardIndex,\n ),\n verification: {\n verifier: evidence.verification.verifier,\n completedAt: evidence.verification.completedAt,\n outcome: evidence.verification.outcome,\n targetCount: evidence.verification.targetCount,\n suiteCount: evidence.verification.suiteCount,\n passed: evidence.verification.passed,\n failed: evidence.verification.failed,\n skipped: evidence.verification.skipped,\n retries: evidence.verification.retries,\n evidence: retained(evidence.verification.evidence),\n },\n };\n return JSON.stringify(canonical);\n}\n\n/**\n * Returns the lowercase SHA-256 fingerprint of the canonical record.\n *\n * The fingerprint is the exact value admitted as `test-run:<sha256>` by\n * deployment gates. Uses the Web Crypto API available in every supported\n * runtime.\n */\nexport async function testRunFingerprint(evidence: TestRunEvidence): Promise<string> {\n const bytes = new TextEncoder().encode(canonicalTestRunJson(evidence));\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", bytes);\n let fingerprint = \"\";\n for (const byte of new Uint8Array(digest)) {\n fingerprint += byte.toString(16).padStart(2, \"0\");\n }\n return fingerprint;\n}\n\n/** Returns the `test-run:<sha256>` admission id for one record. */\nexport async function testRunAdmissionId(evidence: TestRunEvidence): Promise<string> {\n return `${TEST_RUN_ADMISSION_PREFIX}${await testRunFingerprint(evidence)}`;\n}\n\n/**\n * Returns the fingerprint named by a `test-run:<sha256>` admission id.\n *\n * Returns `undefined` unless the prefix, length, and lowercase hexadecimal\n * grammar are all exact.\n */\nexport function parseTestRunAdmissionId(id: string): string | undefined {\n if (!id.startsWith(TEST_RUN_ADMISSION_PREFIX)) {\n return undefined;\n }\n const fingerprint = id.slice(TEST_RUN_ADMISSION_PREFIX.length);\n return ADMISSION_FINGERPRINT.test(fingerprint) ? fingerprint : undefined;\n}\n"],"mappings":";;AAQA,MAAa,4BAA4B;AAEzC,MAAM,wBAAwB;AAE9B,SAAS,SAAS,UAA4D;CAC5E,OAAO;EAAE,WAAW,SAAS;EAAW,aAAa,SAAS;
|
|
1
|
+
{"version":3,"file":"test-run-canonical.mjs","names":[],"sources":["../src/test-run-canonical.ts"],"sourcesContent":["import type {\n TestRunEvidence,\n TestRunRetainedEvidence,\n TestRunSuiteExecution,\n TestRunTargetExecution,\n} from \"./test-run-model.js\";\n\n/** Prefix of every test-run deployment admission id. */\nexport const TEST_RUN_ADMISSION_PREFIX = \"test-run:\";\n\nconst ADMISSION_FINGERPRINT = /^[a-f0-9]{64}$/;\n\nfunction retained(evidence: TestRunRetainedEvidence): TestRunRetainedEvidence {\n return { reference: evidence.reference, fingerprint: evidence.fingerprint };\n}\n\nfunction target(execution: TestRunTargetExecution): TestRunTargetExecution {\n return { id: execution.id, kind: execution.kind, environment: execution.environment };\n}\n\nfunction suite(execution: TestRunSuiteExecution): TestRunSuiteExecution {\n return {\n id: execution.id,\n targetId: execution.targetId,\n kind: execution.kind,\n shardIndex: execution.shardIndex,\n shardCount: execution.shardCount,\n outcome: execution.outcome,\n passed: execution.passed,\n failed: execution.failed,\n skipped: execution.skipped,\n retries: execution.retries,\n durationMs: execution.durationMs,\n invocationFingerprint: execution.invocationFingerprint,\n report: retained(execution.report),\n log: retained(execution.log),\n };\n}\n\n/**\n * Serializes a test-run evidence record canonically.\n *\n * Property order matches the record schema, targets sort by id, suites sort\n * by id then shard index, and selection identifiers sort lexicographically,\n * so equivalent records produce byte-identical JSON in every language. Call\n * validation before trusting the record; canonicalization does not make an\n * invalid record valid.\n */\nexport function canonicalTestRunJson(evidence: TestRunEvidence): string {\n const canonical = {\n format: evidence.format,\n formatVersion: evidence.formatVersion ?? 1,\n id: evidence.id,\n application: evidence.application,\n environment: evidence.environment,\n contractFingerprint: evidence.contractFingerprint,\n sourceRevision: evidence.sourceRevision,\n release: evidence.release,\n artifact: {\n id: evidence.artifact.id,\n fingerprint: evidence.artifact.fingerprint,\n sizeBytes: evidence.artifact.sizeBytes,\n },\n startedAt: evidence.startedAt,\n completedAt: evidence.completedAt,\n validUntil: evidence.validUntil,\n runner: {\n identity: evidence.runner.identity,\n authenticationEvidence: retained(evidence.runner.authenticationEvidence),\n isolation: evidence.runner.isolation,\n invocationFingerprint: evidence.runner.invocationFingerprint,\n environmentEvidence: retained(evidence.runner.environmentEvidence),\n environmentFingerprint: evidence.runner.environmentFingerprint,\n },\n selection: {\n targetIds: [...evidence.selection.targetIds].sort(),\n suiteIds: [...evidence.selection.suiteIds].sort(),\n },\n targets: evidence.targets\n .map(target)\n .sort((left, right) => (left.id < right.id ? -1 : left.id > right.id ? 1 : 0)),\n suites: evidence.suites\n .map(suite)\n .sort((left, right) =>\n left.id !== right.id ? (left.id < right.id ? -1 : 1) : left.shardIndex - right.shardIndex,\n ),\n verification: {\n verifier: evidence.verification.verifier,\n completedAt: evidence.verification.completedAt,\n outcome: evidence.verification.outcome,\n targetCount: evidence.verification.targetCount,\n suiteCount: evidence.verification.suiteCount,\n passed: evidence.verification.passed,\n failed: evidence.verification.failed,\n skipped: evidence.verification.skipped,\n retries: evidence.verification.retries,\n evidence: retained(evidence.verification.evidence),\n },\n };\n return JSON.stringify(canonical);\n}\n\n/**\n * Returns the lowercase SHA-256 fingerprint of the canonical record.\n *\n * The fingerprint is the exact value admitted as `test-run:<sha256>` by\n * deployment gates. Uses the Web Crypto API available in every supported\n * runtime.\n */\nexport async function testRunFingerprint(evidence: TestRunEvidence): Promise<string> {\n const bytes = new TextEncoder().encode(canonicalTestRunJson(evidence));\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", bytes);\n let fingerprint = \"\";\n for (const byte of new Uint8Array(digest)) {\n fingerprint += byte.toString(16).padStart(2, \"0\");\n }\n return fingerprint;\n}\n\n/** Returns the `test-run:<sha256>` admission id for one record. */\nexport async function testRunAdmissionId(evidence: TestRunEvidence): Promise<string> {\n return `${TEST_RUN_ADMISSION_PREFIX}${await testRunFingerprint(evidence)}`;\n}\n\n/**\n * Returns the fingerprint named by a `test-run:<sha256>` admission id.\n *\n * Returns `undefined` unless the prefix, length, and lowercase hexadecimal\n * grammar are all exact.\n */\nexport function parseTestRunAdmissionId(id: string): string | undefined {\n if (!id.startsWith(TEST_RUN_ADMISSION_PREFIX)) {\n return undefined;\n }\n const fingerprint = id.slice(TEST_RUN_ADMISSION_PREFIX.length);\n return ADMISSION_FINGERPRINT.test(fingerprint) ? fingerprint : undefined;\n}\n"],"mappings":";;AAQA,MAAa,4BAA4B;AAEzC,MAAM,wBAAwB;AAE9B,SAAS,SAAS,UAA4D;CAC5E,OAAO;EAAE,WAAW,SAAS;EAAW,aAAa,SAAS;CAAY;AAC5E;AAEA,SAAS,OAAO,WAA2D;CACzE,OAAO;EAAE,IAAI,UAAU;EAAI,MAAM,UAAU;EAAM,aAAa,UAAU;CAAY;AACtF;AAEA,SAAS,MAAM,WAAyD;CACtE,OAAO;EACL,IAAI,UAAU;EACd,UAAU,UAAU;EACpB,MAAM,UAAU;EAChB,YAAY,UAAU;EACtB,YAAY,UAAU;EACtB,SAAS,UAAU;EACnB,QAAQ,UAAU;EAClB,QAAQ,UAAU;EAClB,SAAS,UAAU;EACnB,SAAS,UAAU;EACnB,YAAY,UAAU;EACtB,uBAAuB,UAAU;EACjC,QAAQ,SAAS,UAAU,MAAM;EACjC,KAAK,SAAS,UAAU,GAAG;CAC7B;AACF;;;;;;;;;;AAWA,SAAgB,qBAAqB,UAAmC;CACtE,MAAM,YAAY;EAChB,QAAQ,SAAS;EACjB,eAAe,SAAS,iBAAiB;EACzC,IAAI,SAAS;EACb,aAAa,SAAS;EACtB,aAAa,SAAS;EACtB,qBAAqB,SAAS;EAC9B,gBAAgB,SAAS;EACzB,SAAS,SAAS;EAClB,UAAU;GACR,IAAI,SAAS,SAAS;GACtB,aAAa,SAAS,SAAS;GAC/B,WAAW,SAAS,SAAS;EAC/B;EACA,WAAW,SAAS;EACpB,aAAa,SAAS;EACtB,YAAY,SAAS;EACrB,QAAQ;GACN,UAAU,SAAS,OAAO;GAC1B,wBAAwB,SAAS,SAAS,OAAO,sBAAsB;GACvE,WAAW,SAAS,OAAO;GAC3B,uBAAuB,SAAS,OAAO;GACvC,qBAAqB,SAAS,SAAS,OAAO,mBAAmB;GACjE,wBAAwB,SAAS,OAAO;EAC1C;EACA,WAAW;GACT,WAAW,CAAC,GAAG,SAAS,UAAU,SAAS,EAAE,KAAK;GAClD,UAAU,CAAC,GAAG,SAAS,UAAU,QAAQ,EAAE,KAAK;EAClD;EACA,SAAS,SAAS,QACf,IAAI,MAAM,EACV,MAAM,MAAM,UAAW,KAAK,KAAK,MAAM,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,IAAI,CAAE;EAC/E,QAAQ,SAAS,OACd,IAAI,KAAK,EACT,MAAM,MAAM,UACX,KAAK,OAAO,MAAM,KAAM,KAAK,KAAK,MAAM,KAAK,KAAK,IAAK,KAAK,aAAa,MAAM,UACjF;EACF,cAAc;GACZ,UAAU,SAAS,aAAa;GAChC,aAAa,SAAS,aAAa;GACnC,SAAS,SAAS,aAAa;GAC/B,aAAa,SAAS,aAAa;GACnC,YAAY,SAAS,aAAa;GAClC,QAAQ,SAAS,aAAa;GAC9B,QAAQ,SAAS,aAAa;GAC9B,SAAS,SAAS,aAAa;GAC/B,SAAS,SAAS,aAAa;GAC/B,UAAU,SAAS,SAAS,aAAa,QAAQ;EACnD;CACF;CACA,OAAO,KAAK,UAAU,SAAS;AACjC;;;;;;;;AASA,eAAsB,mBAAmB,UAA4C;CACnF,MAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,qBAAqB,QAAQ,CAAC;CACrE,MAAM,SAAS,MAAM,WAAW,OAAO,OAAO,OAAO,WAAW,KAAK;CACrE,IAAI,cAAc;CAClB,KAAK,MAAM,QAAQ,IAAI,WAAW,MAAM,GACtC,eAAe,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;CAElD,OAAO;AACT;;AAGA,eAAsB,mBAAmB,UAA4C;CACnF,OAAO,GAAG,4BAA4B,MAAM,mBAAmB,QAAQ;AACzE;;;;;;;AAQA,SAAgB,wBAAwB,IAAgC;CACtE,IAAI,CAAC,GAAG,WAAA,WAAoC,GAC1C;CAEF,MAAM,cAAc,GAAG,MAAM,CAAgC;CAC7D,OAAO,sBAAsB,KAAK,WAAW,IAAI,cAAc,KAAA;AACjE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as TestRunEvidence } from "./test-run-model-
|
|
1
|
+
import { i as TestRunEvidence } from "./test-run-model-CF2EYJOQ.mjs";
|
|
2
2
|
import { MarquetteDiagnostic } from "./validate.mjs";
|
|
3
3
|
import { TestRunAdmissionDecision, TestRunCandidate } from "./test-run-admission.mjs";
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-check.d.mts","names":[],"sources":["../src/test-run-check.ts"],"mappings":";;;;;;;AAwBA;;;cAAa,qBAAA
|
|
1
|
+
{"version":3,"file":"test-run-check.d.mts","names":[],"sources":["../src/test-run-check.ts"],"mappings":";;;;;;;AAwBA;;;cAAa,qBAAA;AAAqB;AAOlC;;;;AAPkC,cAOrB,6BAAA;AAab;;;;;;;;;;;AAAA,UAAiB,YAAA;EAUN;EAAA,SARA,MAAA,SAAe,qBAAA;EAUJ;;;;AASD;EATC,SAJX,aAAA,UAAuB,6BAAA;EA2BE;EAAA,SAzBzB,QAAA;EAyBmE;EAAA,SAvBnE,SAAA,EAAW,gBAAA;EAuBe;;;AAAyC;AA4D9E;;EA5DqC,SAhB1B,QAAA;EA6EF;EAAA,SA3EE,UAAA;AAAA;;;;;;;;;;;;iBAcK,oBAAA,CAAqB,KAAA,EAAO,YAAA,GAAe,mBAAmB;;;AAiE3C;;;;;;;;;;;iBALb,kBAAA,CACpB,KAAA,EAAO,YAAA,EACP,SAAA,EAAW,gBAAA,EACX,QAAA,EAAU,eAAA,EACV,GAAA,WACC,OAAA,CAAQ,wBAAA"}
|
package/dist/test-run-check.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as checkDigest, c as checkSourceRevision, d as isStrictTimestamp, l as checkTimestamp, o as checkIdentifier, u as error } from "./test-run-validate-
|
|
1
|
+
import { a as checkDigest, c as checkSourceRevision, d as isStrictTimestamp, l as checkTimestamp, o as checkIdentifier, u as error } from "./test-run-validate-DwGMo8ON.mjs";
|
|
2
2
|
import { parseTestRunAdmissionId } from "./test-run-canonical.mjs";
|
|
3
3
|
import { admitTestRun, testRunDenialCode } from "./test-run-admission.mjs";
|
|
4
4
|
//#region src/test-run-check.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-check.mjs","names":[],"sources":["../src/test-run-check.ts"],"sourcesContent":["import type { TestRunEvidence } from \"./test-run-model.js\";\nimport type { MarquetteDiagnostic } from \"./validate.js\";\nimport {\n admitTestRun,\n testRunDenialCode,\n type TestRunAdmissionDecision,\n type TestRunCandidate,\n type TestRunDenialCode,\n} from \"./test-run-admission.js\";\nimport { parseTestRunAdmissionId } from \"./test-run-canonical.js\";\nimport {\n checkDigest,\n checkIdentifier,\n checkSourceRevision,\n checkTimestamp,\n error,\n isStrictTimestamp,\n} from \"./test-run-validate-rules.js\";\n\n/**\n * Serialized `format` marker for retained tests-check records.\n *\n * Readers must reject any other value before trusting the record.\n */\nexport const TEST_RUN_CHECK_FORMAT = \"vize.test-run.check\";\n\n/**\n * Current serialized tests-check format.\n *\n * Readers must reject a higher value until they explicitly support it.\n */\nexport const TEST_RUN_CHECK_FORMAT_VERSION = 1;\n\n/**\n * Retained, release-bound `tests` check for one deployment decision.\n *\n * The record replaces every generic test-result reference — a summary blob,\n * a report path, or a green workflow label — with the exact\n * `test-run:<sha256>` admission id of an independently verified run, the six\n * candidate facts the run was admitted for, and the identity and instant of\n * the independent observer that recorded the admission. A release decision\n * retaining anything else as its tests evidence cannot pass\n * {@link verifyTestRunCheck}.\n */\nexport interface TestRunCheck {\n /** Serialized format marker; always {@link TEST_RUN_CHECK_FORMAT}. */\n readonly format: typeof TEST_RUN_CHECK_FORMAT;\n /**\n * Serialized format version.\n *\n * Defaults to {@link TEST_RUN_CHECK_FORMAT_VERSION}.\n */\n readonly formatVersion?: typeof TEST_RUN_CHECK_FORMAT_VERSION;\n /** Exact `test-run:<sha256>` admission id of the observed run. */\n readonly evidence: string;\n /** Exact candidate facts the run was admitted for. */\n readonly candidate: TestRunCandidate;\n /**\n * Identity of the independent observer that recorded the admission.\n *\n * The observer is the trusted promotion boundary, never the runner that\n * executed the tests.\n */\n readonly observer: string;\n /** Millisecond-precision UTC instant the admission was observed. */\n readonly observedAt: string;\n}\n\n/**\n * Validates a retained tests-check record structurally.\n *\n * Diagnostics use `check.` paths and are deterministic and sorted by path,\n * code, and message. A generic evidence reference fails here with\n * `VIZE_MARQUETTE_141`: only an exact `test-run:<sha256>` admission id can\n * name retained test evidence. Structural validity never admits anything by\n * itself; {@link verifyTestRunCheck} must confirm the record against the\n * caller's candidate and the retained run. Codes, paths, messages, and\n * ordering are identical to the native implementation.\n */\nexport function validateTestRunCheck(check: TestRunCheck): MarquetteDiagnostic[] {\n const diagnostics: MarquetteDiagnostic[] = [];\n\n if ((check.format as string) !== TEST_RUN_CHECK_FORMAT) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_101\", \"check.format\", \"unsupported tests-check format marker\"),\n );\n }\n if ((check.formatVersion ?? TEST_RUN_CHECK_FORMAT_VERSION) !== TEST_RUN_CHECK_FORMAT_VERSION) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_102\", \"check.formatVersion\", \"unsupported tests-check format version\"),\n );\n }\n\n if (parseTestRunAdmissionId(check.evidence) === undefined) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"check.evidence\",\n \"check evidence must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n }\n\n const candidate = check.candidate;\n checkIdentifier(candidate.application, \"check.candidate.application\", diagnostics);\n checkIdentifier(candidate.environment, \"check.candidate.environment\", diagnostics);\n checkDigest(candidate.contractFingerprint, \"check.candidate.contractFingerprint\", diagnostics);\n checkSourceRevision(candidate.sourceRevision, \"check.candidate.sourceRevision\", diagnostics);\n if (candidate.release.length === 0 || candidate.release.length > 256) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_106\",\n \"check.candidate.release\",\n \"release must be between 1 and 256 characters\",\n ),\n );\n }\n checkDigest(candidate.artifactFingerprint, \"check.candidate.artifactFingerprint\", diagnostics);\n\n checkIdentifier(check.observer, \"check.observer\", diagnostics);\n checkTimestamp(check.observedAt, \"check.observedAt\", diagnostics);\n\n sortDiagnostics(diagnostics);\n return diagnostics;\n}\n\n/**\n * Verifies one retained tests check against the caller's own facts.\n *\n * The caller supplies the candidate it is deciding from its own trusted\n * facts; the retained check must validate structurally, bind that candidate\n * exactly, name an observer independent from the run's runner, and be\n * observed no earlier than the run's completed verification. The referenced\n * record is then admitted exactly like {@link admitTestRun}: canonical\n * fingerprint, candidate bindings, expiry at `now`, verification outcome,\n * and skipped-test accounting all fail closed. Diagnostics, denial codes,\n * and ordering are identical to the native implementation, as pinned by the\n * shared check-decision fixtures.\n */\nexport async function verifyTestRunCheck(\n check: TestRunCheck,\n candidate: TestRunCandidate,\n evidence: TestRunEvidence,\n now: string,\n): Promise<TestRunAdmissionDecision> {\n const diagnostics = validateTestRunCheck(check);\n\n const bindings = [\n [check.candidate.application, candidate.application, \"application\", \"application\"],\n [check.candidate.environment, candidate.environment, \"environment\", \"environment\"],\n [\n check.candidate.contractFingerprint,\n candidate.contractFingerprint,\n \"contractFingerprint\",\n \"contract fingerprint\",\n ],\n [check.candidate.sourceRevision, candidate.sourceRevision, \"sourceRevision\", \"source revision\"],\n [check.candidate.release, candidate.release, \"release\", \"release\"],\n [\n check.candidate.artifactFingerprint,\n candidate.artifactFingerprint,\n \"artifactFingerprint\",\n \"artifact fingerprint\",\n ],\n ] as const;\n for (const [recorded, expected, property, field] of bindings) {\n if (recorded !== expected) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_149\",\n `check.candidate.${property}`,\n `check does not bind the candidate ${field}`,\n ),\n );\n }\n }\n\n if (check.observer === evidence.runner.identity) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_151\",\n \"check.observer\",\n \"check observer must be independent from the run's runner\",\n ),\n );\n }\n if (isStrictTimestamp(check.observedAt) && check.observedAt < evidence.verification.completedAt) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_150\",\n \"check.observedAt\",\n \"observation must not precede the completed verification\",\n ),\n );\n }\n\n diagnostics.push(...(await admitTestRun(evidence, candidate, check.evidence, now)));\n sortDiagnostics(diagnostics);\n const denialCodes: TestRunDenialCode[] = [...new Set(diagnostics.map(testRunDenialCode))].sort();\n return { allowed: diagnostics.length === 0, denialCodes, diagnostics };\n}\n\nfunction sortDiagnostics(diagnostics: MarquetteDiagnostic[]): void {\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n}\n"],"mappings":";;;;;;;;;AAwBA,MAAa,wBAAwB;;;;;;AAOrC,MAAa,gCAAgC;;;;;;;;;;;;AAgD7C,SAAgB,qBAAqB,OAA4C;CAC/E,MAAM,cAAqC,
|
|
1
|
+
{"version":3,"file":"test-run-check.mjs","names":[],"sources":["../src/test-run-check.ts"],"sourcesContent":["import type { TestRunEvidence } from \"./test-run-model.js\";\nimport type { MarquetteDiagnostic } from \"./validate.js\";\nimport {\n admitTestRun,\n testRunDenialCode,\n type TestRunAdmissionDecision,\n type TestRunCandidate,\n type TestRunDenialCode,\n} from \"./test-run-admission.js\";\nimport { parseTestRunAdmissionId } from \"./test-run-canonical.js\";\nimport {\n checkDigest,\n checkIdentifier,\n checkSourceRevision,\n checkTimestamp,\n error,\n isStrictTimestamp,\n} from \"./test-run-validate-rules.js\";\n\n/**\n * Serialized `format` marker for retained tests-check records.\n *\n * Readers must reject any other value before trusting the record.\n */\nexport const TEST_RUN_CHECK_FORMAT = \"vize.test-run.check\";\n\n/**\n * Current serialized tests-check format.\n *\n * Readers must reject a higher value until they explicitly support it.\n */\nexport const TEST_RUN_CHECK_FORMAT_VERSION = 1;\n\n/**\n * Retained, release-bound `tests` check for one deployment decision.\n *\n * The record replaces every generic test-result reference — a summary blob,\n * a report path, or a green workflow label — with the exact\n * `test-run:<sha256>` admission id of an independently verified run, the six\n * candidate facts the run was admitted for, and the identity and instant of\n * the independent observer that recorded the admission. A release decision\n * retaining anything else as its tests evidence cannot pass\n * {@link verifyTestRunCheck}.\n */\nexport interface TestRunCheck {\n /** Serialized format marker; always {@link TEST_RUN_CHECK_FORMAT}. */\n readonly format: typeof TEST_RUN_CHECK_FORMAT;\n /**\n * Serialized format version.\n *\n * Defaults to {@link TEST_RUN_CHECK_FORMAT_VERSION}.\n */\n readonly formatVersion?: typeof TEST_RUN_CHECK_FORMAT_VERSION;\n /** Exact `test-run:<sha256>` admission id of the observed run. */\n readonly evidence: string;\n /** Exact candidate facts the run was admitted for. */\n readonly candidate: TestRunCandidate;\n /**\n * Identity of the independent observer that recorded the admission.\n *\n * The observer is the trusted promotion boundary, never the runner that\n * executed the tests.\n */\n readonly observer: string;\n /** Millisecond-precision UTC instant the admission was observed. */\n readonly observedAt: string;\n}\n\n/**\n * Validates a retained tests-check record structurally.\n *\n * Diagnostics use `check.` paths and are deterministic and sorted by path,\n * code, and message. A generic evidence reference fails here with\n * `VIZE_MARQUETTE_141`: only an exact `test-run:<sha256>` admission id can\n * name retained test evidence. Structural validity never admits anything by\n * itself; {@link verifyTestRunCheck} must confirm the record against the\n * caller's candidate and the retained run. Codes, paths, messages, and\n * ordering are identical to the native implementation.\n */\nexport function validateTestRunCheck(check: TestRunCheck): MarquetteDiagnostic[] {\n const diagnostics: MarquetteDiagnostic[] = [];\n\n if ((check.format as string) !== TEST_RUN_CHECK_FORMAT) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_101\", \"check.format\", \"unsupported tests-check format marker\"),\n );\n }\n if ((check.formatVersion ?? TEST_RUN_CHECK_FORMAT_VERSION) !== TEST_RUN_CHECK_FORMAT_VERSION) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_102\", \"check.formatVersion\", \"unsupported tests-check format version\"),\n );\n }\n\n if (parseTestRunAdmissionId(check.evidence) === undefined) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"check.evidence\",\n \"check evidence must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n }\n\n const candidate = check.candidate;\n checkIdentifier(candidate.application, \"check.candidate.application\", diagnostics);\n checkIdentifier(candidate.environment, \"check.candidate.environment\", diagnostics);\n checkDigest(candidate.contractFingerprint, \"check.candidate.contractFingerprint\", diagnostics);\n checkSourceRevision(candidate.sourceRevision, \"check.candidate.sourceRevision\", diagnostics);\n if (candidate.release.length === 0 || candidate.release.length > 256) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_106\",\n \"check.candidate.release\",\n \"release must be between 1 and 256 characters\",\n ),\n );\n }\n checkDigest(candidate.artifactFingerprint, \"check.candidate.artifactFingerprint\", diagnostics);\n\n checkIdentifier(check.observer, \"check.observer\", diagnostics);\n checkTimestamp(check.observedAt, \"check.observedAt\", diagnostics);\n\n sortDiagnostics(diagnostics);\n return diagnostics;\n}\n\n/**\n * Verifies one retained tests check against the caller's own facts.\n *\n * The caller supplies the candidate it is deciding from its own trusted\n * facts; the retained check must validate structurally, bind that candidate\n * exactly, name an observer independent from the run's runner, and be\n * observed no earlier than the run's completed verification. The referenced\n * record is then admitted exactly like {@link admitTestRun}: canonical\n * fingerprint, candidate bindings, expiry at `now`, verification outcome,\n * and skipped-test accounting all fail closed. Diagnostics, denial codes,\n * and ordering are identical to the native implementation, as pinned by the\n * shared check-decision fixtures.\n */\nexport async function verifyTestRunCheck(\n check: TestRunCheck,\n candidate: TestRunCandidate,\n evidence: TestRunEvidence,\n now: string,\n): Promise<TestRunAdmissionDecision> {\n const diagnostics = validateTestRunCheck(check);\n\n const bindings = [\n [check.candidate.application, candidate.application, \"application\", \"application\"],\n [check.candidate.environment, candidate.environment, \"environment\", \"environment\"],\n [\n check.candidate.contractFingerprint,\n candidate.contractFingerprint,\n \"contractFingerprint\",\n \"contract fingerprint\",\n ],\n [check.candidate.sourceRevision, candidate.sourceRevision, \"sourceRevision\", \"source revision\"],\n [check.candidate.release, candidate.release, \"release\", \"release\"],\n [\n check.candidate.artifactFingerprint,\n candidate.artifactFingerprint,\n \"artifactFingerprint\",\n \"artifact fingerprint\",\n ],\n ] as const;\n for (const [recorded, expected, property, field] of bindings) {\n if (recorded !== expected) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_149\",\n `check.candidate.${property}`,\n `check does not bind the candidate ${field}`,\n ),\n );\n }\n }\n\n if (check.observer === evidence.runner.identity) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_151\",\n \"check.observer\",\n \"check observer must be independent from the run's runner\",\n ),\n );\n }\n if (isStrictTimestamp(check.observedAt) && check.observedAt < evidence.verification.completedAt) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_150\",\n \"check.observedAt\",\n \"observation must not precede the completed verification\",\n ),\n );\n }\n\n diagnostics.push(...(await admitTestRun(evidence, candidate, check.evidence, now)));\n sortDiagnostics(diagnostics);\n const denialCodes: TestRunDenialCode[] = [...new Set(diagnostics.map(testRunDenialCode))].sort();\n return { allowed: diagnostics.length === 0, denialCodes, diagnostics };\n}\n\nfunction sortDiagnostics(diagnostics: MarquetteDiagnostic[]): void {\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n}\n"],"mappings":";;;;;;;;;AAwBA,MAAa,wBAAwB;;;;;;AAOrC,MAAa,gCAAgC;;;;;;;;;;;;AAgD7C,SAAgB,qBAAqB,OAA4C;CAC/E,MAAM,cAAqC,CAAC;CAE5C,IAAK,MAAM,WAAA,uBACT,YAAY,KACV,MAAM,sBAAsB,gBAAgB,uCAAuC,CACrF;CAEF,KAAK,MAAM,iBAAA,OAAA,GACT,YAAY,KACV,MAAM,sBAAsB,uBAAuB,wCAAwC,CAC7F;CAGF,IAAI,wBAAwB,MAAM,QAAQ,MAAM,KAAA,GAC9C,YAAY,KACV,MACE,sBACA,kBACA,kFACF,CACF;CAGF,MAAM,YAAY,MAAM;CACxB,gBAAgB,UAAU,aAAa,+BAA+B,WAAW;CACjF,gBAAgB,UAAU,aAAa,+BAA+B,WAAW;CACjF,YAAY,UAAU,qBAAqB,uCAAuC,WAAW;CAC7F,oBAAoB,UAAU,gBAAgB,kCAAkC,WAAW;CAC3F,IAAI,UAAU,QAAQ,WAAW,KAAK,UAAU,QAAQ,SAAS,KAC/D,YAAY,KACV,MACE,sBACA,2BACA,8CACF,CACF;CAEF,YAAY,UAAU,qBAAqB,uCAAuC,WAAW;CAE7F,gBAAgB,MAAM,UAAU,kBAAkB,WAAW;CAC7D,eAAe,MAAM,YAAY,oBAAoB,WAAW;CAEhE,gBAAgB,WAAW;CAC3B,OAAO;AACT;;;;;;;;;;;;;;AAeA,eAAsB,mBACpB,OACA,WACA,UACA,KACmC;CACnC,MAAM,cAAc,qBAAqB,KAAK;CAE9C,MAAM,WAAW;EACf;GAAC,MAAM,UAAU;GAAa,UAAU;GAAa;GAAe;EAAa;EACjF;GAAC,MAAM,UAAU;GAAa,UAAU;GAAa;GAAe;EAAa;EACjF;GACE,MAAM,UAAU;GAChB,UAAU;GACV;GACA;EACF;EACA;GAAC,MAAM,UAAU;GAAgB,UAAU;GAAgB;GAAkB;EAAiB;EAC9F;GAAC,MAAM,UAAU;GAAS,UAAU;GAAS;GAAW;EAAS;EACjE;GACE,MAAM,UAAU;GAChB,UAAU;GACV;GACA;EACF;CACF;CACA,KAAK,MAAM,CAAC,UAAU,UAAU,UAAU,UAAU,UAClD,IAAI,aAAa,UACf,YAAY,KACV,MACE,sBACA,mBAAmB,YACnB,qCAAqC,OACvC,CACF;CAIJ,IAAI,MAAM,aAAa,SAAS,OAAO,UACrC,YAAY,KACV,MACE,sBACA,kBACA,0DACF,CACF;CAEF,IAAI,kBAAkB,MAAM,UAAU,KAAK,MAAM,aAAa,SAAS,aAAa,aAClF,YAAY,KACV,MACE,sBACA,oBACA,yDACF,CACF;CAGF,YAAY,KAAK,GAAI,MAAM,aAAa,UAAU,WAAW,MAAM,UAAU,GAAG,CAAE;CAClF,gBAAgB,WAAW;CAC3B,MAAM,cAAmC,CAAC,GAAG,IAAI,IAAI,YAAY,IAAI,iBAAiB,CAAC,CAAC,EAAE,KAAK;CAC/F,OAAO;EAAE,SAAS,YAAY,WAAW;EAAG;EAAa;CAAY;AACvE;AAEA,SAAS,gBAAgB,aAA0C;CACjE,YAAY,MAAM,MAAM,UACtB,KAAK,SAAS,MAAM,OAChB,KAAK,OAAO,MAAM,OAChB,KACA,IACF,KAAK,SAAS,MAAM,OAClB,KAAK,OAAO,MAAM,OAChB,KACA,IACF,KAAK,UAAU,MAAM,UACnB,KACA,KAAK,UAAU,MAAM,UACnB,IACA,CACZ;AACF"}
|
|
@@ -178,4 +178,4 @@ type TestRunTargetId<Evidence extends TestRunEvidence> = Evidence["targets"][num
|
|
|
178
178
|
type TestRunSuiteId<Evidence extends TestRunEvidence> = Evidence["suites"][number]["id"];
|
|
179
179
|
//#endregion
|
|
180
180
|
export { TestRunVerificationOutcome as _, TestRunIsolation as a, TestRunSelection as c, TestRunSuiteKind as d, TestRunSuiteOutcome as f, TestRunVerification as g, TestRunTargetKind as h, TestRunEvidence as i, TestRunSuiteExecution as l, TestRunTargetId as m, TEST_RUN_EVIDENCE_FORMAT_VERSION as n, TestRunRetainedEvidence as o, TestRunTargetExecution as p, TestRunArtifact as r, TestRunRunner as s, TEST_RUN_EVIDENCE_FORMAT as t, TestRunSuiteId as u };
|
|
181
|
-
//# sourceMappingURL=test-run-model-
|
|
181
|
+
//# sourceMappingURL=test-run-model-CF2EYJOQ.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-run-model-CF2EYJOQ.d.mts","names":[],"sources":["../src/test-run-model.ts"],"mappings":";;cACa,wBAAA;;cAGA,gCAAA;;KAGD,gBAAA;AAHZ;AAAA,KAMY,iBAAA;;KAGA,gBAAA;AAT8C;AAAA,KAuB9C,mBAAA;;KAGA,0BAAA;;AAvBgB;AAG5B;;;;UA4BiB,uBAAA;EAzBL;EAAA,SA2BD,SAAA;;WAEA,WAAW;AAAA;AAftB;AAAA,UAmBiB,eAAA;;WAEN,EAAA;EArBoB;EAAA,SAuBpB,WAAA;EApB2B;EAAA,SAsB3B,SAAA;AAAA;AAtB2B;AAAA,UA0BrB,aAAA;EAlBuB;EAAA,SAoB7B,QAAA;EAlBA;EAAA,SAoBA,sBAAA,EAAwB,uBAAA;EAdlB;EAAA,SAgBN,SAAA,EAAW,gBAAA;;WAEX,qBAAA;EAhBA;EAAA,SAkBA,mBAAA,EAAqB,uBAAA;EAdrB;EAAA,SAgBA,sBAAA;AAAA;AAZX;;;;;;AAAA,UAqBiB,gBAAA;EAXsC;EAAA,SAgB5C,SAAA,WAAoB,QAAA;EAtBpB;EAAA,SAwBA,QAAA,WAAmB,OAAO;AAAA;;UAIpB,sBAAA;EAtBN;EAAA,SAwBA,EAAA,EAAI,EAAA;EAtBJ;EAAA,SAwBA,IAAA,EAAM,iBAAiB;EAxBD;EAAA,SA0BtB,WAAA;AAAA;;UAIM,qBAAA;EApBf;EAAA,SAyBS,EAAA,EAAI,EAAA;EArBJ;EAAA,SAuBA,QAAA,EAAU,QAAA;EArBV;EAAA,SAuBA,IAAA,EAAM,gBAAA;EAvBoB;EAAA,SAyB1B,UAAA;EArBM;EAAA,SAuBN,UAAA;EAvB4B;EAAA,SAyB5B,OAAA,EAAS,mBAAA;EAzBoB;EAAA,SA2B7B,MAAA;EAzBI;EAAA,SA2BJ,MAAA;EAzBM;EAAA,SA2BN,OAAA;EAzBW;EAAA,SA2BX,OAAA;EAvBM;EAAA,SAyBN,UAAA;EAzB2B;EAAA,SA2B3B,qBAAA;EApBU;EAAA,SAsBV,MAAA,EAAQ,uBAAA;EAdC;EAAA,SAgBT,GAAA,EAAK,uBAAA;AAAA;;UAIC,mBAAA;EAlCf;EAAA,SAoCS,QAAA;EAhCA;EAAA,SAkCA,WAAA;EAhCA;EAAA,SAkCA,OAAA,EAAS,0BAAA;EAhCT;EAAA,SAkCA,WAAA;EAhCA;EAAA,SAkCA,UAAA;EA9BA;EAAA,SAgCA,MAAA;EA9BA;EAAA,SAgCA,MAAA;EA5BA;EAAA,SA8BA,OAAA;EA1BA;EAAA,SA4BA,OAAA;EAxBA;EAAA,SA0BA,QAAA,EAAU,uBAAuB;AAAA;;;AAxBL;AAIvC;;;;;UA+BiB,eAAA;EAzBN;EAAA,SA2BA,MAAA,SAAe,wBAAA;EAzBf;;;;;EAAA,SA+BA,aAAA,UAAuB,gCAAA;EAnBvB;EAAA,SAqBA,EAAA;EArBiC;EAAA,SAuBjC,WAAA;EAZM;EAAA,SAcN,WAAA;;WAEA,mBAAA;EARuB;EAAA,SAUvB,cAAA;EAYQ;EAAA,SAVR,OAAA;EAckB;EAAA,SAZlB,QAAA,EAAU,eAAA;EAgBI;EAAA,SAdd,SAAA;EAciC;EAAA,SAZjC,WAAA;EAxBe;EAAA,SA0Bf,UAAA;EApBuB;EAAA,SAsBvB,MAAA,EAAQ,aAAA;EAlBR;EAAA,SAoBA,SAAA,EAAW,gBAAA;EAhBX;EAAA,SAkBA,OAAA,WAAkB,sBAAA;EAdlB;EAAA,SAgBA,MAAA,WAAiB,qBAAA;EAdP;EAAA,SAgBV,YAAA,EAAc,mBAAA;AAAA;;KAIb,eAAA,kBAAiC,eAAA,IAAmB,QAAQ;;KAG5D,cAAA,kBAAgC,eAAA,IAAmB,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-model-
|
|
1
|
+
{"version":3,"file":"test-run-model-DgJ2nd5p.mjs","names":[],"sources":["../src/test-run-model.ts"],"sourcesContent":["/** Serialized `format` marker for test-run evidence records. */\nexport const TEST_RUN_EVIDENCE_FORMAT = \"vize.test-run.evidence\" as const;\n\n/** Current serialized test-run evidence format version. */\nexport const TEST_RUN_EVIDENCE_FORMAT_VERSION = 1 as const;\n\n/** Isolation level of the runner that executed the tests. */\nexport type TestRunIsolation = \"dedicated\" | \"ephemeral\";\n\n/** Kind of user-visible target a test execution ran against. */\nexport type TestRunTargetKind = \"web\" | \"native\" | \"desktop\" | \"terminal\" | \"server\";\n\n/** Semantic kind of one executed suite. */\nexport type TestRunSuiteKind =\n | \"unit\"\n | \"integration\"\n | \"contract\"\n | \"end-to-end\"\n | \"accessibility\"\n | \"visual\"\n | \"performance\"\n | \"resilience\"\n | \"installation\"\n | \"upgrade\"\n | \"migration\";\n\n/** Final result of one executed suite shard. */\nexport type TestRunSuiteOutcome = \"passed\" | \"failed\" | \"cancelled\";\n\n/** Final result of the independent verification pass. */\nexport type TestRunVerificationOutcome = \"accepted\" | \"rejected\";\n\n/**\n * Immutable, content-addressed reference to retained evidence.\n *\n * The reference names the retained content by SHA-256 so a green label can\n * never drift away from the bytes that were verified.\n */\nexport interface TestRunRetainedEvidence {\n /** Content-addressed retrieval reference in `sha256:<64 hex>` form. */\n readonly reference: string;\n /** Lowercase SHA-256 fingerprint of the retained content. */\n readonly fingerprint: string;\n}\n\n/** Exact release artifact that the recorded test executions exercised. */\nexport interface TestRunArtifact {\n /** Stable artifact identifier. */\n readonly id: string;\n /** Lowercase SHA-256 fingerprint of the artifact bytes. */\n readonly fingerprint: string;\n /** Exact artifact size in bytes; must be at least one byte. */\n readonly sizeBytes: number;\n}\n\n/** Authenticated runner that executed the recorded test run. */\nexport interface TestRunRunner {\n /** Stable runner identity. */\n readonly identity: string;\n /** Retained evidence for the runner's authentication. */\n readonly authenticationEvidence: TestRunRetainedEvidence;\n /** Isolation level the runner guaranteed for this invocation. */\n readonly isolation: TestRunIsolation;\n /** Lowercase SHA-256 fingerprint of the exact invocation. */\n readonly invocationFingerprint: string;\n /** Retained evidence describing the execution environment. */\n readonly environmentEvidence: TestRunRetainedEvidence;\n /** Lowercase SHA-256 fingerprint of the execution environment. */\n readonly environmentFingerprint: string;\n}\n\n/**\n * Complete candidate-selected target and suite coverage for the run.\n *\n * Recorded executions must cover exactly these identifiers; anything less is\n * an undeclared omission and anything more is an undeclared execution.\n */\nexport interface TestRunSelection<\n TargetId extends string = string,\n SuiteId extends string = string,\n> {\n /** Selected target identifiers, unique within the selection. */\n readonly targetIds: readonly TargetId[];\n /** Selected suite identifiers, unique within the selection. */\n readonly suiteIds: readonly SuiteId[];\n}\n\n/** One executed target of the release candidate. */\nexport interface TestRunTargetExecution<Id extends string = string> {\n /** Stable target identifier from the candidate selection. */\n readonly id: Id;\n /** Kind of target that was exercised. */\n readonly kind: TestRunTargetKind;\n /** Environment identifier the target executed in. */\n readonly environment: string;\n}\n\n/** One executed suite shard with its exact recorded results. */\nexport interface TestRunSuiteExecution<\n Id extends string = string,\n TargetId extends string = string,\n> {\n /** Stable suite identifier from the candidate selection. */\n readonly id: Id;\n /** Target this shard executed against. */\n readonly targetId: TargetId;\n /** Semantic suite kind. */\n readonly kind: TestRunSuiteKind;\n /** One-based shard index within `shardCount`. */\n readonly shardIndex: number;\n /** Total number of shards the suite was split into. */\n readonly shardCount: number;\n /** Final shard outcome. */\n readonly outcome: TestRunSuiteOutcome;\n /** Number of passed tests. */\n readonly passed: number;\n /** Number of failed tests. */\n readonly failed: number;\n /** Number of skipped tests. */\n readonly skipped: number;\n /** Number of retried tests; retries must always be declared. */\n readonly retries: number;\n /** Wall-clock shard duration in milliseconds. */\n readonly durationMs: number;\n /** Lowercase SHA-256 fingerprint of the exact shard invocation. */\n readonly invocationFingerprint: string;\n /** Retained machine-readable report evidence. */\n readonly report: TestRunRetainedEvidence;\n /** Retained execution log evidence. */\n readonly log: TestRunRetainedEvidence;\n}\n\n/** Independent verification summary over every recorded execution. */\nexport interface TestRunVerification {\n /** Stable identity of the independent verifier. */\n readonly verifier: string;\n /** Millisecond-precision UTC time the verification completed. */\n readonly completedAt: string;\n /** Final verification outcome. */\n readonly outcome: TestRunVerificationOutcome;\n /** Exact number of recorded target executions. */\n readonly targetCount: number;\n /** Exact number of recorded suite executions. */\n readonly suiteCount: number;\n /** Total passed tests across every recorded suite execution. */\n readonly passed: number;\n /** Total failed tests across every recorded suite execution. */\n readonly failed: number;\n /** Total skipped tests across every recorded suite execution. */\n readonly skipped: number;\n /** Total retried tests across every recorded suite execution. */\n readonly retries: number;\n /** Retained evidence produced by the verification pass. */\n readonly evidence: TestRunRetainedEvidence;\n}\n\n/**\n * Complete, versioned test-run evidence for one release candidate.\n *\n * The record binds application, environment, application contract, source\n * revision, release, and exact artifact fingerprint to bounded target and\n * suite executions, so a `tests` deployment check can only be satisfied by\n * retained, immutable facts instead of a mutable label or path.\n */\nexport interface TestRunEvidence {\n /** Serialized format marker; always {@link TEST_RUN_EVIDENCE_FORMAT}. */\n readonly format: typeof TEST_RUN_EVIDENCE_FORMAT;\n /**\n * Serialized format version.\n *\n * @default 1\n */\n readonly formatVersion?: typeof TEST_RUN_EVIDENCE_FORMAT_VERSION;\n /** Stable record identifier. */\n readonly id: string;\n /** Application the run verified. */\n readonly application: string;\n /** Deployment environment the run verified the candidate for. */\n readonly environment: string;\n /** Lowercase SHA-256 fingerprint of the application contract. */\n readonly contractFingerprint: string;\n /** Exact source revision the candidate was built from. */\n readonly sourceRevision: string;\n /** Release the candidate belongs to. */\n readonly release: string;\n /** Exact artifact the recorded executions exercised. */\n readonly artifact: TestRunArtifact;\n /** Millisecond-precision UTC time the run started. */\n readonly startedAt: string;\n /** Millisecond-precision UTC time the run completed. */\n readonly completedAt: string;\n /** Millisecond-precision UTC time the record expires. */\n readonly validUntil: string;\n /** Authenticated runner that executed the run. */\n readonly runner: TestRunRunner;\n /** Complete candidate-selected target and suite coverage. */\n readonly selection: TestRunSelection;\n /** Recorded target executions. */\n readonly targets: readonly TestRunTargetExecution[];\n /** Recorded suite executions. */\n readonly suites: readonly TestRunSuiteExecution[];\n /** Independent verification summary. */\n readonly verification: TestRunVerification;\n}\n\n/** Target identifiers recorded by one test-run evidence record. */\nexport type TestRunTargetId<Evidence extends TestRunEvidence> = Evidence[\"targets\"][number][\"id\"];\n\n/** Suite identifiers recorded by one test-run evidence record. */\nexport type TestRunSuiteId<Evidence extends TestRunEvidence> = Evidence[\"suites\"][number][\"id\"];\n"],"mappings":";;AACA,MAAa,2BAA2B;;AAGxC,MAAa,mCAAmC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-transition.d.mts","names":[],"sources":["../src/test-run-transition-model.ts","../src/test-run-transition.ts"],"mappings":";;;;;;AAQA;;;cAAa,0BAAA
|
|
1
|
+
{"version":3,"file":"test-run-transition.d.mts","names":[],"sources":["../src/test-run-transition-model.ts","../src/test-run-transition.ts"],"mappings":";;;;;;AAQA;;;cAAa,0BAAA;AAA0B;AAOvC;;;;AAPuC,cAO1B,kCAAA;AAGb;AAAA,cAAa,gCAAA;;;AAAgC;AAQ7C;;;UAAiB,yBAAA;EAEN;EAAA,SAAA,IAAA;EAEU;EAAA,SAAV,QAAA,EAAU,2BAA2B;EAIrC;EAAA,SAFA,IAAA;EAEO;EAAA,SAAP,OAAA;AAAA;;;;;;;;;UAWM,uBAAA;EA0BA;EAAA,SAxBN,OAAA;;WAEA,WAAA,WAAsB,iBAAA;EA8BC;EAAA,SA5BvB,WAAA,WAAsB,yBAAyB;AAAA;;;;;;;;;;;;;;;;;AA6CvC;UAzBF,iBAAA;EAsC6B;EAAA,SApCnC,MAAA,SAAe,0BAAA;EAoCiC;AAAiB;AAsC5E;;;EAtC2D,SA9BhD,aAAA,UAAuB,kCAAA;EAoE6B;EAAA,SAlEpD,QAAA;EAkEwE;EAAA,SAhExE,QAAA;EAgE+E;EAAA,SA9D/E,SAAA;;WAEA,SAAA,EAAW,gBAAA;ECzCN;EAAA,SD2CL,QAAA;;WAEA,QAAA,EAAU,uBAAA;EC7CiC;;;;EAAA,SDkD3C,QAAA;AAAA;;;;;;;;;;;iBAaK,8BAAA,CAA+B,UAA6B,EAAjB,iBAAiB;;;;;AC6CzC;;;iBDPb,4BAAA,CAA6B,UAAA,EAAY,iBAAA,GAAoB,OAAO;;;AA3I1F;;;;AAAuC;AAOvC;;;;AAA+C;AAG/C;AAVA,iBCsCgB,yBAAA,CAA0B,UAAA,EAAY,iBAAA,GAAoB,mBAAmB;;;AD5BhD;AAQ7C;;;;;;;;;;AAQkB;AAWlB;iBC0GsB,uBAAA,CACpB,UAAA,EAAY,iBAAA,EACZ,QAAA,EAAU,iBAAA,UACT,OAAA,CAAQ,wBAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as checkDigest, c as checkSourceRevision, d as isStrictTimestamp, l as checkTimestamp, o as checkIdentifier, s as checkSafeInteger, u as error } from "./test-run-validate-
|
|
1
|
+
import { a as checkDigest, c as checkSourceRevision, d as isStrictTimestamp, l as checkTimestamp, o as checkIdentifier, s as checkSafeInteger, u as error } from "./test-run-validate-DwGMo8ON.mjs";
|
|
2
2
|
import { parseTestRunAdmissionId } from "./test-run-canonical.mjs";
|
|
3
3
|
import { testRunDenialCode } from "./test-run-admission.mjs";
|
|
4
4
|
//#region src/test-run-transition-model.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-transition.mjs","names":[],"sources":["../src/test-run-transition-model.ts","../src/test-run-transition-rules.ts","../src/test-run-transition.ts"],"sourcesContent":["import type { MarquetteDiagnosticSeverity } from \"./validate.js\";\nimport type { TestRunCandidate, TestRunDenialCode } from \"./test-run-admission.js\";\n\n/**\n * Serialized `format` marker for release-transition records.\n *\n * Readers must reject any other value before trusting the record.\n */\nexport const TEST_RUN_TRANSITION_FORMAT = \"vize.test-run.transition\";\n\n/**\n * Current serialized release-transition format.\n *\n * Readers must reject a higher value until they explicitly support it.\n */\nexport const TEST_RUN_TRANSITION_FORMAT_VERSION = 1;\n\n/** Maximum admission ids one transition may carry as accepted state. */\nexport const TEST_RUN_TRANSITION_MAX_ACCEPTED = 4096;\n\n/**\n * One retained diagnostic inside a durable transition record.\n *\n * The shape and serialization match live diagnostics exactly; the retained\n * form is plain data so persisted records can be read back by any host.\n */\nexport interface TestRunRetainedDiagnostic {\n /** Stable machine-readable diagnostic code. */\n readonly code: string;\n /** Severity recorded for the diagnostic; any diagnostic denies. */\n readonly severity: MarquetteDiagnosticSeverity;\n /** JSON-style path into the decided input. */\n readonly path: string;\n /** Human-readable explanation recorded with the decision. */\n readonly message: string;\n}\n\n/**\n * One retained allow-or-deny decision inside a durable transition record.\n *\n * The shape and serialization match live decisions exactly. Validation\n * rejects a retained decision whose `allowed` flag, denial codes, or\n * diagnostic ordering disagree with the published mapping, so a record\n * cannot claim an outcome its own diagnostics contradict.\n */\nexport interface TestRunRetainedDecision {\n /** Whether the release decision admitted the candidate. */\n readonly allowed: boolean;\n /** Deduplicated denial causes sorted lexicographically; empty if allowed. */\n readonly denialCodes: readonly TestRunDenialCode[];\n /** Complete diagnostics in the stable path, code, message order. */\n readonly diagnostics: readonly TestRunRetainedDiagnostic[];\n}\n\n/**\n * One durable atomic release transition.\n *\n * The record binds the decision, the exact candidate and evidence it\n * decided, and the complete accepted anti-replay state after the decision\n * into one canonical document. `sequence` grows by exactly one per\n * transition and `previous` names the predecessor's canonical SHA-256\n * fingerprint, so a chain tip proves the entire decision history and the\n * accepted set can never drift from the decision that produced it.\n *\n * Host durability contract: write the complete canonical bytes to a\n * temporary location, flush them to durable storage, then atomically rename\n * or commit so exactly one complete chain tip exists at every instant; on\n * recovery, verify the tip against its retained predecessor with\n * {@link verifyTestRunTransition} before deciding anything new, and discard\n * — never repair — a torn or partial record.\n */\nexport interface TestRunTransition {\n /** Serialized format marker; always {@link TEST_RUN_TRANSITION_FORMAT}. */\n readonly format: typeof TEST_RUN_TRANSITION_FORMAT;\n /**\n * Serialized format version.\n *\n * Defaults to {@link TEST_RUN_TRANSITION_FORMAT_VERSION}.\n */\n readonly formatVersion?: typeof TEST_RUN_TRANSITION_FORMAT_VERSION;\n /** One-based position of this transition in its chain. */\n readonly sequence: number;\n /** Canonical fingerprint of the predecessor; `null` only at genesis. */\n readonly previous: string | null;\n /** Millisecond-precision UTC instant the decision was made. */\n readonly decidedAt: string;\n /** Exact candidate the decision was made for. */\n readonly candidate: TestRunCandidate;\n /** Exact `test-run:<sha256>` admission id the decision evaluated. */\n readonly evidence: string;\n /** Retained decision exactly as it was produced. */\n readonly decision: TestRunRetainedDecision;\n /**\n * Complete anti-replay state after this transition: every admission id\n * ever accepted in this chain, sorted and unique.\n */\n readonly accepted: readonly string[];\n}\n\n/**\n * Serializes a release transition canonically.\n *\n * Property order matches the record schema and the accepted state sorts\n * lexicographically after deduplication, so equivalent transitions produce\n * byte-identical JSON in every language. These are the exact bytes a host\n * must write atomically and the exact bytes the chain fingerprint covers.\n * Call validation before trusting the record; canonicalization does not\n * make an invalid record valid.\n */\nexport function canonicalTestRunTransitionJson(transition: TestRunTransition): string {\n const canonical = {\n format: transition.format,\n formatVersion: transition.formatVersion ?? TEST_RUN_TRANSITION_FORMAT_VERSION,\n sequence: transition.sequence,\n previous: transition.previous,\n decidedAt: transition.decidedAt,\n candidate: {\n application: transition.candidate.application,\n environment: transition.candidate.environment,\n contractFingerprint: transition.candidate.contractFingerprint,\n sourceRevision: transition.candidate.sourceRevision,\n release: transition.candidate.release,\n artifactFingerprint: transition.candidate.artifactFingerprint,\n },\n evidence: transition.evidence,\n decision: {\n allowed: transition.decision.allowed,\n denialCodes: [...new Set(transition.decision.denialCodes)].sort(),\n diagnostics: transition.decision.diagnostics.map((diagnostic) => ({\n code: diagnostic.code,\n severity: diagnostic.severity,\n path: diagnostic.path,\n message: diagnostic.message,\n })),\n },\n accepted: [...new Set(transition.accepted)].sort(),\n };\n return JSON.stringify(canonical);\n}\n\n/**\n * Returns the lowercase SHA-256 fingerprint of the canonical transition.\n *\n * The fingerprint is the exact value the successor transition must name as\n * `previous`, forming the durable chain. Uses the Web Crypto API available\n * in every supported runtime.\n */\nexport async function testRunTransitionFingerprint(transition: TestRunTransition): Promise<string> {\n const bytes = new TextEncoder().encode(canonicalTestRunTransitionJson(transition));\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", bytes);\n let fingerprint = \"\";\n for (const byte of new Uint8Array(digest)) {\n fingerprint += byte.toString(16).padStart(2, \"0\");\n }\n return fingerprint;\n}\n","import type { MarquetteDiagnostic } from \"./validate.js\";\nimport { testRunDenialCode } from \"./test-run-admission.js\";\nimport { parseTestRunAdmissionId } from \"./test-run-canonical.js\";\nimport {\n TEST_RUN_TRANSITION_MAX_ACCEPTED,\n type TestRunRetainedDiagnostic,\n type TestRunTransition,\n} from \"./test-run-transition-model.js\";\nimport { error } from \"./test-run-validate-rules.js\";\n\n/** Validates the accepted anti-replay state carried by one transition. */\nexport function validateAccepted(\n transition: TestRunTransition,\n diagnostics: MarquetteDiagnostic[],\n): void {\n const accepted = transition.accepted;\n if (accepted.length > TEST_RUN_TRANSITION_MAX_ACCEPTED) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_131\",\n \"transition.accepted\",\n \"transition must accept at most 4096 admission ids\",\n ),\n );\n }\n if (accepted.some((id) => parseTestRunAdmissionId(id) === undefined)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"transition.accepted\",\n \"accepted admission ids must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n }\n if (accepted.some((id, index) => index > 0 && (accepted[index - 1] as string) >= id)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_154\",\n \"transition.accepted\",\n \"accepted admission ids must be sorted and unique\",\n ),\n );\n }\n if (transition.decision.allowed && !accepted.includes(transition.evidence)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_156\",\n \"transition.accepted\",\n \"an allowed transition must accept its own evidence\",\n ),\n );\n }\n}\n\nconst DIAGNOSTIC_CODE = /^VIZE_MARQUETTE_[0-9]{3}$/;\n\n/** Validates the retained decision carried by one transition. */\nexport function validateDecision(\n transition: TestRunTransition,\n diagnostics: MarquetteDiagnostic[],\n): void {\n const decision = transition.decision;\n if (decision.allowed && (decision.denialCodes.length > 0 || decision.diagnostics.length > 0)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"an allowed decision must carry no diagnostics\",\n ),\n );\n }\n if (!decision.allowed && decision.diagnostics.length === 0) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"a denied decision must carry its diagnostics\",\n ),\n );\n }\n if (decision.diagnostics.some((diagnostic) => !DIAGNOSTIC_CODE.test(diagnostic.code))) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"diagnostic codes must be stable VIZE_MARQUETTE codes\",\n ),\n );\n }\n const sorted = decision.diagnostics.every((diagnostic, index) => {\n if (index === 0) {\n return true;\n }\n const left = decision.diagnostics[index - 1] as TestRunRetainedDiagnostic;\n return (\n left.path < diagnostic.path ||\n (left.path === diagnostic.path &&\n (left.code < diagnostic.code ||\n (left.code === diagnostic.code && left.message <= diagnostic.message)))\n );\n });\n if (!sorted) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"decision diagnostics must be sorted by path, code, and message\",\n ),\n );\n }\n const recomputed = [\n ...new Set(\n decision.diagnostics.map((diagnostic) =>\n testRunDenialCode(diagnostic as MarquetteDiagnostic),\n ),\n ),\n ].sort();\n const retained = [...decision.denialCodes];\n if (\n recomputed.length !== retained.length ||\n recomputed.some((code, index) => code !== retained[index])\n ) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"denial codes must match the published diagnostic mapping\",\n ),\n );\n }\n}\n","import type { MarquetteDiagnostic } from \"./validate.js\";\nimport {\n testRunDenialCode,\n type TestRunAdmissionDecision,\n type TestRunDenialCode,\n} from \"./test-run-admission.js\";\nimport { parseTestRunAdmissionId } from \"./test-run-canonical.js\";\nimport {\n TEST_RUN_TRANSITION_FORMAT,\n TEST_RUN_TRANSITION_FORMAT_VERSION,\n testRunTransitionFingerprint,\n type TestRunTransition,\n} from \"./test-run-transition-model.js\";\nimport { validateAccepted, validateDecision } from \"./test-run-transition-rules.js\";\nimport {\n checkDigest,\n checkIdentifier,\n checkSafeInteger,\n checkSourceRevision,\n checkTimestamp,\n error,\n isStrictTimestamp,\n} from \"./test-run-validate-rules.js\";\n\nexport {\n TEST_RUN_TRANSITION_FORMAT,\n TEST_RUN_TRANSITION_FORMAT_VERSION,\n TEST_RUN_TRANSITION_MAX_ACCEPTED,\n canonicalTestRunTransitionJson,\n testRunTransitionFingerprint,\n type TestRunRetainedDecision,\n type TestRunRetainedDiagnostic,\n type TestRunTransition,\n} from \"./test-run-transition-model.js\";\n\n/**\n * Validates one release transition structurally.\n *\n * Diagnostics use `transition.` paths and are deterministic and sorted by\n * path, code, and message. Validation confirms the record alone is\n * internally coherent — grammar, decision consistency against the published\n * diagnostic mapping, and an allowed decision accepting its own evidence —\n * but only {@link verifyTestRunTransition} can confirm the record extends\n * the durable chain. Codes, paths, messages, and ordering are identical to\n * the native implementation.\n */\nexport function validateTestRunTransition(transition: TestRunTransition): MarquetteDiagnostic[] {\n const diagnostics: MarquetteDiagnostic[] = [];\n\n if ((transition.format as string) !== TEST_RUN_TRANSITION_FORMAT) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_101\",\n \"transition.format\",\n \"unsupported release-transition format marker\",\n ),\n );\n }\n const version = transition.formatVersion ?? TEST_RUN_TRANSITION_FORMAT_VERSION;\n if (version !== TEST_RUN_TRANSITION_FORMAT_VERSION) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_102\",\n \"transition.formatVersion\",\n \"unsupported release-transition format version\",\n ),\n );\n }\n\n if (!Number.isInteger(transition.sequence) || transition.sequence < 1) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_152\",\n \"transition.sequence\",\n \"transition sequence must be at least one\",\n ),\n );\n }\n checkSafeInteger(transition.sequence, \"transition.sequence\", diagnostics);\n if (transition.previous !== null && transition.sequence === 1) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_153\",\n \"transition.previous\",\n \"genesis transition must not name a predecessor\",\n ),\n );\n } else if (transition.previous !== null) {\n checkDigest(transition.previous, \"transition.previous\", diagnostics);\n } else if (transition.sequence > 1) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_153\", \"transition.previous\", \"transition must name its predecessor\"),\n );\n }\n checkTimestamp(transition.decidedAt, \"transition.decidedAt\", diagnostics);\n\n const candidate = transition.candidate;\n checkIdentifier(candidate.application, \"transition.candidate.application\", diagnostics);\n checkIdentifier(candidate.environment, \"transition.candidate.environment\", diagnostics);\n checkDigest(\n candidate.contractFingerprint,\n \"transition.candidate.contractFingerprint\",\n diagnostics,\n );\n checkSourceRevision(candidate.sourceRevision, \"transition.candidate.sourceRevision\", diagnostics);\n if (candidate.release.length === 0 || candidate.release.length > 256) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_106\",\n \"transition.candidate.release\",\n \"release must be between 1 and 256 characters\",\n ),\n );\n }\n checkDigest(\n candidate.artifactFingerprint,\n \"transition.candidate.artifactFingerprint\",\n diagnostics,\n );\n\n if (parseTestRunAdmissionId(transition.evidence) === undefined) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"transition.evidence\",\n \"transition evidence must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n }\n validateAccepted(transition, diagnostics);\n validateDecision(transition, diagnostics);\n\n sortDiagnostics(diagnostics);\n return diagnostics;\n}\n\n/**\n * Verifies one release transition against the durable chain tip.\n *\n * `previous` is the retained, already-verified predecessor — `null` only\n * when deciding the very first transition of a chain. The transition must\n * validate structurally, extend the predecessor's sequence, fingerprint,\n * scope, and decision time exactly, never re-accept evidence the\n * predecessor already accepted, and carry an accepted state equal to the\n * predecessor's state plus exactly the newly accepted evidence (unchanged\n * for a denial). Any diagnostic rejects the transition: a conforming host\n * must not persist it, and on recovery must discard a tip this function\n * rejects. Diagnostics, denial codes, and ordering are identical to the\n * native implementation, as pinned by the shared transition-decision\n * fixtures.\n */\nexport async function verifyTestRunTransition(\n transition: TestRunTransition,\n previous: TestRunTransition | null,\n): Promise<TestRunAdmissionDecision> {\n const diagnostics = validateTestRunTransition(transition);\n\n let priorAccepted: readonly string[] = [];\n if (previous === null) {\n if (transition.sequence !== 1) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_157\",\n \"transition.sequence\",\n \"transition requires its predecessor to verify\",\n ),\n );\n }\n } else {\n priorAccepted = previous.accepted;\n if (transition.sequence !== previous.sequence + 1) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_157\",\n \"transition.sequence\",\n \"transition must extend its predecessor's sequence\",\n ),\n );\n }\n if (transition.previous !== (await testRunTransitionFingerprint(previous))) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_157\",\n \"transition.previous\",\n \"transition must name its predecessor's canonical fingerprint\",\n ),\n );\n }\n for (const [recorded, expected, path] of [\n [\n transition.candidate.application,\n previous.candidate.application,\n \"transition.candidate.application\",\n ],\n [\n transition.candidate.environment,\n previous.candidate.environment,\n \"transition.candidate.environment\",\n ],\n ] as const) {\n if (recorded !== expected) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_157\", path, \"transition must stay within its predecessor's scope\"),\n );\n }\n }\n if (\n isStrictTimestamp(transition.decidedAt) &&\n isStrictTimestamp(previous.decidedAt) &&\n transition.decidedAt < previous.decidedAt\n ) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_157\",\n \"transition.decidedAt\",\n \"transition must not predate its predecessor\",\n ),\n );\n }\n if (transition.decision.allowed && previous.accepted.includes(transition.evidence)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_158\",\n \"transition.evidence\",\n \"accepted evidence must not be accepted again\",\n ),\n );\n }\n }\n\n const expected = [\n ...new Set(\n transition.decision.allowed ? [...priorAccepted, transition.evidence] : priorAccepted,\n ),\n ].sort();\n const accepted = transition.accepted;\n if (accepted.length !== expected.length || accepted.some((id, index) => id !== expected[index])) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_159\",\n \"transition.accepted\",\n \"accepted state must equal its predecessor's state plus exactly the newly accepted evidence\",\n ),\n );\n }\n\n sortDiagnostics(diagnostics);\n const denialCodes: TestRunDenialCode[] = [...new Set(diagnostics.map(testRunDenialCode))].sort();\n return { allowed: diagnostics.length === 0, denialCodes, diagnostics };\n}\n\nfunction sortDiagnostics(diagnostics: MarquetteDiagnostic[]): void {\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"test-run-transition.mjs","names":[],"sources":["../src/test-run-transition-model.ts","../src/test-run-transition-rules.ts","../src/test-run-transition.ts"],"sourcesContent":["import type { MarquetteDiagnosticSeverity } from \"./validate.js\";\nimport type { TestRunCandidate, TestRunDenialCode } from \"./test-run-admission.js\";\n\n/**\n * Serialized `format` marker for release-transition records.\n *\n * Readers must reject any other value before trusting the record.\n */\nexport const TEST_RUN_TRANSITION_FORMAT = \"vize.test-run.transition\";\n\n/**\n * Current serialized release-transition format.\n *\n * Readers must reject a higher value until they explicitly support it.\n */\nexport const TEST_RUN_TRANSITION_FORMAT_VERSION = 1;\n\n/** Maximum admission ids one transition may carry as accepted state. */\nexport const TEST_RUN_TRANSITION_MAX_ACCEPTED = 4096;\n\n/**\n * One retained diagnostic inside a durable transition record.\n *\n * The shape and serialization match live diagnostics exactly; the retained\n * form is plain data so persisted records can be read back by any host.\n */\nexport interface TestRunRetainedDiagnostic {\n /** Stable machine-readable diagnostic code. */\n readonly code: string;\n /** Severity recorded for the diagnostic; any diagnostic denies. */\n readonly severity: MarquetteDiagnosticSeverity;\n /** JSON-style path into the decided input. */\n readonly path: string;\n /** Human-readable explanation recorded with the decision. */\n readonly message: string;\n}\n\n/**\n * One retained allow-or-deny decision inside a durable transition record.\n *\n * The shape and serialization match live decisions exactly. Validation\n * rejects a retained decision whose `allowed` flag, denial codes, or\n * diagnostic ordering disagree with the published mapping, so a record\n * cannot claim an outcome its own diagnostics contradict.\n */\nexport interface TestRunRetainedDecision {\n /** Whether the release decision admitted the candidate. */\n readonly allowed: boolean;\n /** Deduplicated denial causes sorted lexicographically; empty if allowed. */\n readonly denialCodes: readonly TestRunDenialCode[];\n /** Complete diagnostics in the stable path, code, message order. */\n readonly diagnostics: readonly TestRunRetainedDiagnostic[];\n}\n\n/**\n * One durable atomic release transition.\n *\n * The record binds the decision, the exact candidate and evidence it\n * decided, and the complete accepted anti-replay state after the decision\n * into one canonical document. `sequence` grows by exactly one per\n * transition and `previous` names the predecessor's canonical SHA-256\n * fingerprint, so a chain tip proves the entire decision history and the\n * accepted set can never drift from the decision that produced it.\n *\n * Host durability contract: write the complete canonical bytes to a\n * temporary location, flush them to durable storage, then atomically rename\n * or commit so exactly one complete chain tip exists at every instant; on\n * recovery, verify the tip against its retained predecessor with\n * {@link verifyTestRunTransition} before deciding anything new, and discard\n * — never repair — a torn or partial record.\n */\nexport interface TestRunTransition {\n /** Serialized format marker; always {@link TEST_RUN_TRANSITION_FORMAT}. */\n readonly format: typeof TEST_RUN_TRANSITION_FORMAT;\n /**\n * Serialized format version.\n *\n * Defaults to {@link TEST_RUN_TRANSITION_FORMAT_VERSION}.\n */\n readonly formatVersion?: typeof TEST_RUN_TRANSITION_FORMAT_VERSION;\n /** One-based position of this transition in its chain. */\n readonly sequence: number;\n /** Canonical fingerprint of the predecessor; `null` only at genesis. */\n readonly previous: string | null;\n /** Millisecond-precision UTC instant the decision was made. */\n readonly decidedAt: string;\n /** Exact candidate the decision was made for. */\n readonly candidate: TestRunCandidate;\n /** Exact `test-run:<sha256>` admission id the decision evaluated. */\n readonly evidence: string;\n /** Retained decision exactly as it was produced. */\n readonly decision: TestRunRetainedDecision;\n /**\n * Complete anti-replay state after this transition: every admission id\n * ever accepted in this chain, sorted and unique.\n */\n readonly accepted: readonly string[];\n}\n\n/**\n * Serializes a release transition canonically.\n *\n * Property order matches the record schema and the accepted state sorts\n * lexicographically after deduplication, so equivalent transitions produce\n * byte-identical JSON in every language. These are the exact bytes a host\n * must write atomically and the exact bytes the chain fingerprint covers.\n * Call validation before trusting the record; canonicalization does not\n * make an invalid record valid.\n */\nexport function canonicalTestRunTransitionJson(transition: TestRunTransition): string {\n const canonical = {\n format: transition.format,\n formatVersion: transition.formatVersion ?? TEST_RUN_TRANSITION_FORMAT_VERSION,\n sequence: transition.sequence,\n previous: transition.previous,\n decidedAt: transition.decidedAt,\n candidate: {\n application: transition.candidate.application,\n environment: transition.candidate.environment,\n contractFingerprint: transition.candidate.contractFingerprint,\n sourceRevision: transition.candidate.sourceRevision,\n release: transition.candidate.release,\n artifactFingerprint: transition.candidate.artifactFingerprint,\n },\n evidence: transition.evidence,\n decision: {\n allowed: transition.decision.allowed,\n denialCodes: [...new Set(transition.decision.denialCodes)].sort(),\n diagnostics: transition.decision.diagnostics.map((diagnostic) => ({\n code: diagnostic.code,\n severity: diagnostic.severity,\n path: diagnostic.path,\n message: diagnostic.message,\n })),\n },\n accepted: [...new Set(transition.accepted)].sort(),\n };\n return JSON.stringify(canonical);\n}\n\n/**\n * Returns the lowercase SHA-256 fingerprint of the canonical transition.\n *\n * The fingerprint is the exact value the successor transition must name as\n * `previous`, forming the durable chain. Uses the Web Crypto API available\n * in every supported runtime.\n */\nexport async function testRunTransitionFingerprint(transition: TestRunTransition): Promise<string> {\n const bytes = new TextEncoder().encode(canonicalTestRunTransitionJson(transition));\n const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", bytes);\n let fingerprint = \"\";\n for (const byte of new Uint8Array(digest)) {\n fingerprint += byte.toString(16).padStart(2, \"0\");\n }\n return fingerprint;\n}\n","import type { MarquetteDiagnostic } from \"./validate.js\";\nimport { testRunDenialCode } from \"./test-run-admission.js\";\nimport { parseTestRunAdmissionId } from \"./test-run-canonical.js\";\nimport {\n TEST_RUN_TRANSITION_MAX_ACCEPTED,\n type TestRunRetainedDiagnostic,\n type TestRunTransition,\n} from \"./test-run-transition-model.js\";\nimport { error } from \"./test-run-validate-rules.js\";\n\n/** Validates the accepted anti-replay state carried by one transition. */\nexport function validateAccepted(\n transition: TestRunTransition,\n diagnostics: MarquetteDiagnostic[],\n): void {\n const accepted = transition.accepted;\n if (accepted.length > TEST_RUN_TRANSITION_MAX_ACCEPTED) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_131\",\n \"transition.accepted\",\n \"transition must accept at most 4096 admission ids\",\n ),\n );\n }\n if (accepted.some((id) => parseTestRunAdmissionId(id) === undefined)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"transition.accepted\",\n \"accepted admission ids must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n }\n if (accepted.some((id, index) => index > 0 && (accepted[index - 1] as string) >= id)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_154\",\n \"transition.accepted\",\n \"accepted admission ids must be sorted and unique\",\n ),\n );\n }\n if (transition.decision.allowed && !accepted.includes(transition.evidence)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_156\",\n \"transition.accepted\",\n \"an allowed transition must accept its own evidence\",\n ),\n );\n }\n}\n\nconst DIAGNOSTIC_CODE = /^VIZE_MARQUETTE_[0-9]{3}$/;\n\n/** Validates the retained decision carried by one transition. */\nexport function validateDecision(\n transition: TestRunTransition,\n diagnostics: MarquetteDiagnostic[],\n): void {\n const decision = transition.decision;\n if (decision.allowed && (decision.denialCodes.length > 0 || decision.diagnostics.length > 0)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"an allowed decision must carry no diagnostics\",\n ),\n );\n }\n if (!decision.allowed && decision.diagnostics.length === 0) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"a denied decision must carry its diagnostics\",\n ),\n );\n }\n if (decision.diagnostics.some((diagnostic) => !DIAGNOSTIC_CODE.test(diagnostic.code))) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"diagnostic codes must be stable VIZE_MARQUETTE codes\",\n ),\n );\n }\n const sorted = decision.diagnostics.every((diagnostic, index) => {\n if (index === 0) {\n return true;\n }\n const left = decision.diagnostics[index - 1] as TestRunRetainedDiagnostic;\n return (\n left.path < diagnostic.path ||\n (left.path === diagnostic.path &&\n (left.code < diagnostic.code ||\n (left.code === diagnostic.code && left.message <= diagnostic.message)))\n );\n });\n if (!sorted) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"decision diagnostics must be sorted by path, code, and message\",\n ),\n );\n }\n const recomputed = [\n ...new Set(\n decision.diagnostics.map((diagnostic) =>\n testRunDenialCode(diagnostic as MarquetteDiagnostic),\n ),\n ),\n ].sort();\n const retained = [...decision.denialCodes];\n if (\n recomputed.length !== retained.length ||\n recomputed.some((code, index) => code !== retained[index])\n ) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_155\",\n \"transition.decision\",\n \"denial codes must match the published diagnostic mapping\",\n ),\n );\n }\n}\n","import type { MarquetteDiagnostic } from \"./validate.js\";\nimport {\n testRunDenialCode,\n type TestRunAdmissionDecision,\n type TestRunDenialCode,\n} from \"./test-run-admission.js\";\nimport { parseTestRunAdmissionId } from \"./test-run-canonical.js\";\nimport {\n TEST_RUN_TRANSITION_FORMAT,\n TEST_RUN_TRANSITION_FORMAT_VERSION,\n testRunTransitionFingerprint,\n type TestRunTransition,\n} from \"./test-run-transition-model.js\";\nimport { validateAccepted, validateDecision } from \"./test-run-transition-rules.js\";\nimport {\n checkDigest,\n checkIdentifier,\n checkSafeInteger,\n checkSourceRevision,\n checkTimestamp,\n error,\n isStrictTimestamp,\n} from \"./test-run-validate-rules.js\";\n\nexport {\n TEST_RUN_TRANSITION_FORMAT,\n TEST_RUN_TRANSITION_FORMAT_VERSION,\n TEST_RUN_TRANSITION_MAX_ACCEPTED,\n canonicalTestRunTransitionJson,\n testRunTransitionFingerprint,\n type TestRunRetainedDecision,\n type TestRunRetainedDiagnostic,\n type TestRunTransition,\n} from \"./test-run-transition-model.js\";\n\n/**\n * Validates one release transition structurally.\n *\n * Diagnostics use `transition.` paths and are deterministic and sorted by\n * path, code, and message. Validation confirms the record alone is\n * internally coherent — grammar, decision consistency against the published\n * diagnostic mapping, and an allowed decision accepting its own evidence —\n * but only {@link verifyTestRunTransition} can confirm the record extends\n * the durable chain. Codes, paths, messages, and ordering are identical to\n * the native implementation.\n */\nexport function validateTestRunTransition(transition: TestRunTransition): MarquetteDiagnostic[] {\n const diagnostics: MarquetteDiagnostic[] = [];\n\n if ((transition.format as string) !== TEST_RUN_TRANSITION_FORMAT) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_101\",\n \"transition.format\",\n \"unsupported release-transition format marker\",\n ),\n );\n }\n const version = transition.formatVersion ?? TEST_RUN_TRANSITION_FORMAT_VERSION;\n if (version !== TEST_RUN_TRANSITION_FORMAT_VERSION) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_102\",\n \"transition.formatVersion\",\n \"unsupported release-transition format version\",\n ),\n );\n }\n\n if (!Number.isInteger(transition.sequence) || transition.sequence < 1) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_152\",\n \"transition.sequence\",\n \"transition sequence must be at least one\",\n ),\n );\n }\n checkSafeInteger(transition.sequence, \"transition.sequence\", diagnostics);\n if (transition.previous !== null && transition.sequence === 1) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_153\",\n \"transition.previous\",\n \"genesis transition must not name a predecessor\",\n ),\n );\n } else if (transition.previous !== null) {\n checkDigest(transition.previous, \"transition.previous\", diagnostics);\n } else if (transition.sequence > 1) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_153\", \"transition.previous\", \"transition must name its predecessor\"),\n );\n }\n checkTimestamp(transition.decidedAt, \"transition.decidedAt\", diagnostics);\n\n const candidate = transition.candidate;\n checkIdentifier(candidate.application, \"transition.candidate.application\", diagnostics);\n checkIdentifier(candidate.environment, \"transition.candidate.environment\", diagnostics);\n checkDigest(\n candidate.contractFingerprint,\n \"transition.candidate.contractFingerprint\",\n diagnostics,\n );\n checkSourceRevision(candidate.sourceRevision, \"transition.candidate.sourceRevision\", diagnostics);\n if (candidate.release.length === 0 || candidate.release.length > 256) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_106\",\n \"transition.candidate.release\",\n \"release must be between 1 and 256 characters\",\n ),\n );\n }\n checkDigest(\n candidate.artifactFingerprint,\n \"transition.candidate.artifactFingerprint\",\n diagnostics,\n );\n\n if (parseTestRunAdmissionId(transition.evidence) === undefined) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"transition.evidence\",\n \"transition evidence must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n }\n validateAccepted(transition, diagnostics);\n validateDecision(transition, diagnostics);\n\n sortDiagnostics(diagnostics);\n return diagnostics;\n}\n\n/**\n * Verifies one release transition against the durable chain tip.\n *\n * `previous` is the retained, already-verified predecessor — `null` only\n * when deciding the very first transition of a chain. The transition must\n * validate structurally, extend the predecessor's sequence, fingerprint,\n * scope, and decision time exactly, never re-accept evidence the\n * predecessor already accepted, and carry an accepted state equal to the\n * predecessor's state plus exactly the newly accepted evidence (unchanged\n * for a denial). Any diagnostic rejects the transition: a conforming host\n * must not persist it, and on recovery must discard a tip this function\n * rejects. Diagnostics, denial codes, and ordering are identical to the\n * native implementation, as pinned by the shared transition-decision\n * fixtures.\n */\nexport async function verifyTestRunTransition(\n transition: TestRunTransition,\n previous: TestRunTransition | null,\n): Promise<TestRunAdmissionDecision> {\n const diagnostics = validateTestRunTransition(transition);\n\n let priorAccepted: readonly string[] = [];\n if (previous === null) {\n if (transition.sequence !== 1) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_157\",\n \"transition.sequence\",\n \"transition requires its predecessor to verify\",\n ),\n );\n }\n } else {\n priorAccepted = previous.accepted;\n if (transition.sequence !== previous.sequence + 1) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_157\",\n \"transition.sequence\",\n \"transition must extend its predecessor's sequence\",\n ),\n );\n }\n if (transition.previous !== (await testRunTransitionFingerprint(previous))) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_157\",\n \"transition.previous\",\n \"transition must name its predecessor's canonical fingerprint\",\n ),\n );\n }\n for (const [recorded, expected, path] of [\n [\n transition.candidate.application,\n previous.candidate.application,\n \"transition.candidate.application\",\n ],\n [\n transition.candidate.environment,\n previous.candidate.environment,\n \"transition.candidate.environment\",\n ],\n ] as const) {\n if (recorded !== expected) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_157\", path, \"transition must stay within its predecessor's scope\"),\n );\n }\n }\n if (\n isStrictTimestamp(transition.decidedAt) &&\n isStrictTimestamp(previous.decidedAt) &&\n transition.decidedAt < previous.decidedAt\n ) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_157\",\n \"transition.decidedAt\",\n \"transition must not predate its predecessor\",\n ),\n );\n }\n if (transition.decision.allowed && previous.accepted.includes(transition.evidence)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_158\",\n \"transition.evidence\",\n \"accepted evidence must not be accepted again\",\n ),\n );\n }\n }\n\n const expected = [\n ...new Set(\n transition.decision.allowed ? [...priorAccepted, transition.evidence] : priorAccepted,\n ),\n ].sort();\n const accepted = transition.accepted;\n if (accepted.length !== expected.length || accepted.some((id, index) => id !== expected[index])) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_159\",\n \"transition.accepted\",\n \"accepted state must equal its predecessor's state plus exactly the newly accepted evidence\",\n ),\n );\n }\n\n sortDiagnostics(diagnostics);\n const denialCodes: TestRunDenialCode[] = [...new Set(diagnostics.map(testRunDenialCode))].sort();\n return { allowed: diagnostics.length === 0, denialCodes, diagnostics };\n}\n\nfunction sortDiagnostics(diagnostics: MarquetteDiagnostic[]): void {\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./test-run-model-
|
|
1
|
+
import "./test-run-model-DgJ2nd5p.mjs";
|
|
2
2
|
const IDENTIFIER = /^[a-z0-9][a-z0-9._-]*$/;
|
|
3
3
|
const DIGEST = /^[a-f0-9]{64}$/;
|
|
4
4
|
const SOURCE_REVISION = /^[a-f0-9]{40,128}$/;
|
|
@@ -226,4 +226,4 @@ function validateTestRunEvidence(evidence) {
|
|
|
226
226
|
//#endregion
|
|
227
227
|
export { checkDigest as a, checkSourceRevision as c, isStrictTimestamp as d, TEST_RUN_MAX_TARGETS as i, checkTimestamp as l, TEST_RUN_MAX_SHARDS as n, checkIdentifier as o, TEST_RUN_MAX_SUITES as r, checkSafeInteger as s, validateTestRunEvidence as t, error as u };
|
|
228
228
|
|
|
229
|
-
//# sourceMappingURL=test-run-validate-
|
|
229
|
+
//# sourceMappingURL=test-run-validate-DwGMo8ON.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-validate-C_KR031E.mjs","names":[],"sources":["../src/test-run-validate-rules.ts","../src/test-run-validate-executions.ts","../src/test-run-validate.ts"],"sourcesContent":["import type { MarquetteDiagnostic } from \"./validate.js\";\nimport type { TestRunRetainedEvidence } from \"./test-run-model.js\";\n\n/** Largest integer every consuming language can represent exactly. */\nexport const MAX_SAFE_EVIDENCE_INTEGER = 9007199254740991;\n\nconst IDENTIFIER = /^[a-z0-9][a-z0-9._-]*$/;\nconst DIGEST = /^[a-f0-9]{64}$/;\nconst SOURCE_REVISION = /^[a-f0-9]{40,128}$/;\nconst TIMESTAMP =\n /^([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\\.[0-9]{3}Z$/;\n\n/** Builds the stable diagnostic path for a named collection member. */\nexport function evidencePath(collection: string, id: string): string {\n return `${collection}.${id}`;\n}\n\n/** Creates one error diagnostic. */\nexport function error(\n code: MarquetteDiagnostic[\"code\"],\n path: string,\n message: string,\n): MarquetteDiagnostic {\n return { code, severity: \"error\", path, message };\n}\n\n/** Validates the shared identifier grammar and the schema length bound. */\nexport function checkIdentifier(\n id: string,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (!IDENTIFIER.test(id)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_103\",\n path,\n \"identifier must use lowercase ASCII letters, digits, dash, underscore, or dot\",\n ),\n );\n }\n if (id.length === 0 || id.length > 128) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_103\", path, \"identifier must be between 1 and 128 characters\"),\n );\n }\n}\n\n/** Validates a lowercase 64-character SHA-256 fingerprint. */\nexport function checkDigest(value: string, path: string, diagnostics: MarquetteDiagnostic[]): void {\n if (!DIGEST.test(value)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_104\", path, \"fingerprint must be 64 lowercase hexadecimal characters\"),\n );\n }\n}\n\n/** Validates an exact source revision digest. */\nexport function checkSourceRevision(\n value: string,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (!SOURCE_REVISION.test(value)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_105\",\n path,\n \"source revision must be 40 to 128 lowercase hexadecimal characters\",\n ),\n );\n }\n}\n\n/** Returns whether `value` is a millisecond-precision UTC calendar instant. */\nexport function isStrictTimestamp(value: string): boolean {\n const match = TIMESTAMP.exec(value);\n if (match === null) {\n return false;\n }\n const year = Number(match[1]);\n const month = Number(match[2]);\n const day = Number(match[3]);\n const maxDay =\n month === 2\n ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)\n ? 29\n : 28\n : month === 4 || month === 6 || month === 9 || month === 11\n ? 30\n : 31;\n return day <= maxDay;\n}\n\n/** Validates a millisecond-precision UTC timestamp. */\nexport function checkTimestamp(\n value: string,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (!isStrictTimestamp(value)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_107\",\n path,\n \"timestamp must be a millisecond-precision UTC instant like 2026-01-01T00:00:00.000Z\",\n ),\n );\n }\n}\n\n/** Rejects integers that lose precision in a consuming language. */\nexport function checkSafeInteger(\n value: number,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (value > MAX_SAFE_EVIDENCE_INTEGER) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_111\",\n path,\n \"value must not exceed the largest exactly-representable integer\",\n ),\n );\n }\n}\n\n/**\n * Validates an immutable retained-evidence binding.\n *\n * The retrieval reference must be content-addressed and name exactly the\n * fingerprinted bytes; anything else would let retained evidence mutate\n * behind a stable-looking record.\n */\nexport function checkRetainedEvidence(\n retained: TestRunRetainedEvidence,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n checkDigest(retained.fingerprint, evidencePath(path, \"fingerprint\"), diagnostics);\n const suffix = retained.reference.startsWith(\"sha256:\")\n ? retained.reference.slice(\"sha256:\".length)\n : undefined;\n if (suffix === undefined || !DIGEST.test(suffix)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_108\",\n evidencePath(path, \"reference\"),\n \"evidence reference must be sha256: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n } else if (suffix !== retained.fingerprint) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_109\",\n evidencePath(path, \"reference\"),\n \"content-addressed reference must name the fingerprinted content\",\n ),\n );\n }\n}\n","import type { MarquetteDiagnostic } from \"./validate.js\";\nimport type { TestRunEvidence, TestRunSuiteExecution } from \"./test-run-model.js\";\nimport {\n checkDigest,\n checkIdentifier,\n checkRetainedEvidence,\n checkSafeInteger,\n checkTimestamp,\n error,\n evidencePath,\n} from \"./test-run-validate-rules.js\";\n\n/** Maximum recorded target executions and selected target identifiers. */\nexport const TEST_RUN_MAX_TARGETS = 32;\n\n/** Maximum recorded suite executions and selected suite identifiers. */\nexport const TEST_RUN_MAX_SUITES = 512;\n\n/** Maximum shard index and shard count for one suite execution. */\nexport const TEST_RUN_MAX_SHARDS = 1024;\n\n/** Validates recorded target executions against the candidate selection. */\nexport function validateTargets(\n evidence: TestRunEvidence,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (evidence.targets.length === 0 || evidence.targets.length > TEST_RUN_MAX_TARGETS) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_131\",\n \"targets\",\n \"record must include between 1 and 32 target executions\",\n ),\n );\n }\n\n const seen = new Set<string>();\n const selected = new Set(evidence.selection.targetIds);\n for (const target of evidence.targets) {\n const path = evidencePath(\"targets\", target.id);\n checkIdentifier(target.id, path, diagnostics);\n checkIdentifier(target.environment, evidencePath(path, \"environment\"), diagnostics);\n if (seen.has(target.id)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_117\", path, \"target execution is recorded more than once\"),\n );\n }\n seen.add(target.id);\n if (!selected.has(target.id)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_118\", path, \"target execution was not selected for this candidate\"),\n );\n }\n }\n for (const id of evidence.selection.targetIds) {\n if (!seen.has(id)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_119\",\n evidencePath(\"selection.targetIds\", id),\n \"selected target has no recorded execution\",\n ),\n );\n }\n }\n}\n\ninterface ShardGroup {\n readonly shardCount: number;\n readonly kind: TestRunSuiteExecution[\"kind\"];\n readonly targetId: string;\n readonly indexes: Set<number>;\n kindsDisagree: boolean;\n targetsDisagree: boolean;\n countsDisagree: boolean;\n}\n\n/** Validates recorded suite executions, shards, and their consistency. */\nexport function validateSuites(\n evidence: TestRunEvidence,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (evidence.suites.length === 0 || evidence.suites.length > TEST_RUN_MAX_SUITES) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_131\",\n \"suites\",\n \"record must include between 1 and 512 suite executions\",\n ),\n );\n }\n\n const shards = new Map<string, ShardGroup>();\n const selected = new Set(evidence.selection.suiteIds);\n const recordedTargets = new Set(evidence.targets.map((target) => target.id));\n for (const suite of evidence.suites) {\n const path = evidencePath(\"suites\", suite.id);\n let group = shards.get(suite.id);\n if (group === undefined) {\n group = {\n shardCount: suite.shardCount,\n kind: suite.kind,\n targetId: suite.targetId,\n indexes: new Set(),\n kindsDisagree: false,\n targetsDisagree: false,\n countsDisagree: false,\n };\n shards.set(suite.id, group);\n // Suite-id-scoped invariants are shard-independent, so evaluate them\n // once per unique suite id. Running them per shard would emit the same\n // code/path/message diagnostic once for every shard of the suite.\n checkIdentifier(suite.id, path, diagnostics);\n if (!selected.has(suite.id)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_121\", path, \"suite execution was not selected for this candidate\"),\n );\n }\n if (!recordedTargets.has(suite.targetId)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_123\",\n evidencePath(path, \"targetId\"),\n \"suite target has no recorded target execution\",\n ),\n );\n }\n }\n group.kindsDisagree ||= group.kind !== suite.kind;\n group.targetsDisagree ||= group.targetId !== suite.targetId;\n group.countsDisagree ||= group.shardCount !== suite.shardCount;\n if (group.indexes.has(suite.shardIndex)) {\n diagnostics.push(error(\"VIZE_MARQUETTE_120\", path, \"suite shard is recorded more than once\"));\n }\n group.indexes.add(suite.shardIndex);\n if (\n suite.shardCount === 0 ||\n suite.shardCount > TEST_RUN_MAX_SHARDS ||\n suite.shardIndex === 0 ||\n suite.shardIndex > suite.shardCount\n ) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_124\",\n evidencePath(path, \"shardIndex\"),\n \"shard index must fall within a shard count between 1 and 1024\",\n ),\n );\n }\n checkSafeInteger(suite.durationMs, evidencePath(path, \"durationMs\"), diagnostics);\n checkDigest(\n suite.invocationFingerprint,\n evidencePath(path, \"invocationFingerprint\"),\n diagnostics,\n );\n checkRetainedEvidence(suite.report, evidencePath(path, \"report\"), diagnostics);\n checkRetainedEvidence(suite.log, evidencePath(path, \"log\"), diagnostics);\n\n const executed = suite.passed + suite.failed + suite.skipped;\n const consistent =\n suite.outcome === \"passed\"\n ? suite.failed === 0 && executed > 0\n : suite.outcome === \"failed\"\n ? suite.failed > 0\n : true;\n if (!consistent) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_130\",\n evidencePath(path, \"outcome\"),\n \"suite outcome does not match its recorded counts\",\n ),\n );\n }\n }\n\n for (const id of evidence.selection.suiteIds) {\n if (!shards.has(id)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_122\",\n evidencePath(\"selection.suiteIds\", id),\n \"selected suite has no recorded execution\",\n ),\n );\n }\n }\n\n for (const [id, group] of shards) {\n const path = evidencePath(\"suites\", id);\n if (group.kindsDisagree || group.targetsDisagree || group.countsDisagree) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_126\",\n path,\n \"every shard of one suite must share its kind, target, and shard count\",\n ),\n );\n } else if (\n group.shardCount !== 0 &&\n group.shardCount <= TEST_RUN_MAX_SHARDS &&\n group.indexes.size !== group.shardCount\n ) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_125\",\n path,\n `suite must record every shard from 1 to ${group.shardCount}`,\n ),\n );\n }\n }\n}\n\n/** Validates the independent verification summary. */\nexport function validateVerification(\n evidence: TestRunEvidence,\n diagnostics: MarquetteDiagnostic[],\n): void {\n const verification = evidence.verification;\n checkIdentifier(verification.verifier, \"verification.verifier\", diagnostics);\n checkTimestamp(verification.completedAt, \"verification.completedAt\", diagnostics);\n checkRetainedEvidence(verification.evidence, \"verification.evidence\", diagnostics);\n if (verification.completedAt < evidence.completedAt) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_114\",\n \"verification.completedAt\",\n \"verification must complete after the run completes\",\n ),\n );\n }\n if (verification.targetCount !== evidence.targets.length) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_127\",\n \"verification.targetCount\",\n \"verified target count must equal the recorded target executions\",\n ),\n );\n }\n if (verification.suiteCount !== evidence.suites.length) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_127\",\n \"verification.suiteCount\",\n \"verified suite count must equal the recorded suite executions\",\n ),\n );\n }\n\n const totals = { passed: 0, failed: 0, skipped: 0, retries: 0 };\n for (const suite of evidence.suites) {\n totals.passed += suite.passed;\n totals.failed += suite.failed;\n totals.skipped += suite.skipped;\n totals.retries += suite.retries;\n }\n const recorded = [\n [totals.passed, verification.passed, \"verification.passed\", \"passed\"],\n [totals.failed, verification.failed, \"verification.failed\", \"failed\"],\n [totals.skipped, verification.skipped, \"verification.skipped\", \"skipped\"],\n [totals.retries, verification.retries, \"verification.retries\", \"retried\"],\n ] as const;\n for (const [total, value, path, name] of recorded) {\n if (total !== value) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_128\",\n path,\n `verified ${name} total must equal the sum over suite executions`,\n ),\n );\n }\n }\n\n if (verification.outcome === \"accepted\") {\n const clean = evidence.suites.every(\n (suite) => suite.outcome === \"passed\" && suite.failed === 0,\n );\n if (!clean || verification.failed > 0) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_129\",\n \"verification.outcome\",\n \"verification cannot accept a run with failed or cancelled executions\",\n ),\n );\n }\n }\n}\n","import type { MarquetteDiagnostic } from \"./validate.js\";\nimport type { TestRunEvidence } from \"./test-run-model.js\";\nimport { TEST_RUN_EVIDENCE_FORMAT, TEST_RUN_EVIDENCE_FORMAT_VERSION } from \"./test-run-model.js\";\nimport {\n checkDigest,\n checkIdentifier,\n checkRetainedEvidence,\n checkSafeInteger,\n checkSourceRevision,\n checkTimestamp,\n error,\n evidencePath,\n} from \"./test-run-validate-rules.js\";\nimport {\n TEST_RUN_MAX_SUITES,\n TEST_RUN_MAX_TARGETS,\n validateSuites,\n validateTargets,\n validateVerification,\n} from \"./test-run-validate-executions.js\";\n\nexport {\n TEST_RUN_MAX_SHARDS,\n TEST_RUN_MAX_SUITES,\n TEST_RUN_MAX_TARGETS,\n} from \"./test-run-validate-executions.js\";\nexport type { MarquetteDiagnostic, MarquetteDiagnosticSeverity } from \"./validate.js\";\n\n/**\n * Validates a complete test-run evidence record.\n *\n * Diagnostics are deterministic and sorted by path, code, and message so the\n * same record produces identical CLI, promotion, test, and CI output in\n * every consuming language. A record with any error diagnostic must never\n * satisfy a deployment check.\n */\nexport function validateTestRunEvidence(evidence: TestRunEvidence): MarquetteDiagnostic[] {\n const diagnostics: MarquetteDiagnostic[] = [];\n\n if (evidence.format !== TEST_RUN_EVIDENCE_FORMAT) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_101\", \"format\", \"unsupported test-run evidence format marker\"),\n );\n }\n if ((evidence.formatVersion ?? 1) !== TEST_RUN_EVIDENCE_FORMAT_VERSION) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_102\", \"formatVersion\", \"unsupported test-run evidence format version\"),\n );\n }\n\n checkIdentifier(evidence.id, \"id\", diagnostics);\n checkIdentifier(evidence.application, \"application\", diagnostics);\n checkIdentifier(evidence.environment, \"environment\", diagnostics);\n checkDigest(evidence.contractFingerprint, \"contractFingerprint\", diagnostics);\n checkSourceRevision(evidence.sourceRevision, \"sourceRevision\", diagnostics);\n if (evidence.release.length === 0 || evidence.release.length > 256) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_106\", \"release\", \"release must be between 1 and 256 characters\"),\n );\n }\n\n checkIdentifier(evidence.artifact.id, \"artifact.id\", diagnostics);\n checkDigest(evidence.artifact.fingerprint, \"artifact.fingerprint\", diagnostics);\n if (evidence.artifact.sizeBytes === 0) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_110\", \"artifact.sizeBytes\", \"artifact size must be at least one byte\"),\n );\n }\n checkSafeInteger(evidence.artifact.sizeBytes, \"artifact.sizeBytes\", diagnostics);\n\n checkTimestamp(evidence.startedAt, \"startedAt\", diagnostics);\n checkTimestamp(evidence.completedAt, \"completedAt\", diagnostics);\n checkTimestamp(evidence.validUntil, \"validUntil\", diagnostics);\n if (evidence.completedAt < evidence.startedAt) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_112\", \"completedAt\", \"run completion must not precede its start\"),\n );\n }\n if (evidence.validUntil <= evidence.completedAt) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_113\", \"validUntil\", \"record expiry must come after run completion\"),\n );\n }\n\n const runner = evidence.runner;\n checkIdentifier(runner.identity, \"runner.identity\", diagnostics);\n checkRetainedEvidence(\n runner.authenticationEvidence,\n \"runner.authenticationEvidence\",\n diagnostics,\n );\n checkDigest(runner.invocationFingerprint, \"runner.invocationFingerprint\", diagnostics);\n checkRetainedEvidence(runner.environmentEvidence, \"runner.environmentEvidence\", diagnostics);\n checkDigest(runner.environmentFingerprint, \"runner.environmentFingerprint\", diagnostics);\n\n const selection = evidence.selection;\n if (selection.targetIds.length === 0 || selection.targetIds.length > TEST_RUN_MAX_TARGETS) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_115\",\n \"selection.targetIds\",\n \"selection must include between 1 and 32 targets\",\n ),\n );\n }\n if (selection.suiteIds.length === 0 || selection.suiteIds.length > TEST_RUN_MAX_SUITES) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_115\",\n \"selection.suiteIds\",\n \"selection must include between 1 and 512 suites\",\n ),\n );\n }\n for (const id of selection.targetIds) {\n checkIdentifier(id, evidencePath(\"selection.targetIds\", id), diagnostics);\n }\n for (const id of selection.suiteIds) {\n checkIdentifier(id, evidencePath(\"selection.suiteIds\", id), diagnostics);\n }\n\n validateTargets(evidence, diagnostics);\n validateSuites(evidence, diagnostics);\n validateVerification(evidence, diagnostics);\n\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n return diagnostics;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"test-run-validate-DwGMo8ON.mjs","names":[],"sources":["../src/test-run-validate-rules.ts","../src/test-run-validate-executions.ts","../src/test-run-validate.ts"],"sourcesContent":["import type { MarquetteDiagnostic } from \"./validate.js\";\nimport type { TestRunRetainedEvidence } from \"./test-run-model.js\";\n\n/** Largest integer every consuming language can represent exactly. */\nexport const MAX_SAFE_EVIDENCE_INTEGER = 9007199254740991;\n\nconst IDENTIFIER = /^[a-z0-9][a-z0-9._-]*$/;\nconst DIGEST = /^[a-f0-9]{64}$/;\nconst SOURCE_REVISION = /^[a-f0-9]{40,128}$/;\nconst TIMESTAMP =\n /^([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\\.[0-9]{3}Z$/;\n\n/** Builds the stable diagnostic path for a named collection member. */\nexport function evidencePath(collection: string, id: string): string {\n return `${collection}.${id}`;\n}\n\n/** Creates one error diagnostic. */\nexport function error(\n code: MarquetteDiagnostic[\"code\"],\n path: string,\n message: string,\n): MarquetteDiagnostic {\n return { code, severity: \"error\", path, message };\n}\n\n/** Validates the shared identifier grammar and the schema length bound. */\nexport function checkIdentifier(\n id: string,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (!IDENTIFIER.test(id)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_103\",\n path,\n \"identifier must use lowercase ASCII letters, digits, dash, underscore, or dot\",\n ),\n );\n }\n if (id.length === 0 || id.length > 128) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_103\", path, \"identifier must be between 1 and 128 characters\"),\n );\n }\n}\n\n/** Validates a lowercase 64-character SHA-256 fingerprint. */\nexport function checkDigest(value: string, path: string, diagnostics: MarquetteDiagnostic[]): void {\n if (!DIGEST.test(value)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_104\", path, \"fingerprint must be 64 lowercase hexadecimal characters\"),\n );\n }\n}\n\n/** Validates an exact source revision digest. */\nexport function checkSourceRevision(\n value: string,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (!SOURCE_REVISION.test(value)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_105\",\n path,\n \"source revision must be 40 to 128 lowercase hexadecimal characters\",\n ),\n );\n }\n}\n\n/** Returns whether `value` is a millisecond-precision UTC calendar instant. */\nexport function isStrictTimestamp(value: string): boolean {\n const match = TIMESTAMP.exec(value);\n if (match === null) {\n return false;\n }\n const year = Number(match[1]);\n const month = Number(match[2]);\n const day = Number(match[3]);\n const maxDay =\n month === 2\n ? year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)\n ? 29\n : 28\n : month === 4 || month === 6 || month === 9 || month === 11\n ? 30\n : 31;\n return day <= maxDay;\n}\n\n/** Validates a millisecond-precision UTC timestamp. */\nexport function checkTimestamp(\n value: string,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (!isStrictTimestamp(value)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_107\",\n path,\n \"timestamp must be a millisecond-precision UTC instant like 2026-01-01T00:00:00.000Z\",\n ),\n );\n }\n}\n\n/** Rejects integers that lose precision in a consuming language. */\nexport function checkSafeInteger(\n value: number,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (value > MAX_SAFE_EVIDENCE_INTEGER) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_111\",\n path,\n \"value must not exceed the largest exactly-representable integer\",\n ),\n );\n }\n}\n\n/**\n * Validates an immutable retained-evidence binding.\n *\n * The retrieval reference must be content-addressed and name exactly the\n * fingerprinted bytes; anything else would let retained evidence mutate\n * behind a stable-looking record.\n */\nexport function checkRetainedEvidence(\n retained: TestRunRetainedEvidence,\n path: string,\n diagnostics: MarquetteDiagnostic[],\n): void {\n checkDigest(retained.fingerprint, evidencePath(path, \"fingerprint\"), diagnostics);\n const suffix = retained.reference.startsWith(\"sha256:\")\n ? retained.reference.slice(\"sha256:\".length)\n : undefined;\n if (suffix === undefined || !DIGEST.test(suffix)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_108\",\n evidencePath(path, \"reference\"),\n \"evidence reference must be sha256: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n } else if (suffix !== retained.fingerprint) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_109\",\n evidencePath(path, \"reference\"),\n \"content-addressed reference must name the fingerprinted content\",\n ),\n );\n }\n}\n","import type { MarquetteDiagnostic } from \"./validate.js\";\nimport type { TestRunEvidence, TestRunSuiteExecution } from \"./test-run-model.js\";\nimport {\n checkDigest,\n checkIdentifier,\n checkRetainedEvidence,\n checkSafeInteger,\n checkTimestamp,\n error,\n evidencePath,\n} from \"./test-run-validate-rules.js\";\n\n/** Maximum recorded target executions and selected target identifiers. */\nexport const TEST_RUN_MAX_TARGETS = 32;\n\n/** Maximum recorded suite executions and selected suite identifiers. */\nexport const TEST_RUN_MAX_SUITES = 512;\n\n/** Maximum shard index and shard count for one suite execution. */\nexport const TEST_RUN_MAX_SHARDS = 1024;\n\n/** Validates recorded target executions against the candidate selection. */\nexport function validateTargets(\n evidence: TestRunEvidence,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (evidence.targets.length === 0 || evidence.targets.length > TEST_RUN_MAX_TARGETS) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_131\",\n \"targets\",\n \"record must include between 1 and 32 target executions\",\n ),\n );\n }\n\n const seen = new Set<string>();\n const selected = new Set(evidence.selection.targetIds);\n for (const target of evidence.targets) {\n const path = evidencePath(\"targets\", target.id);\n checkIdentifier(target.id, path, diagnostics);\n checkIdentifier(target.environment, evidencePath(path, \"environment\"), diagnostics);\n if (seen.has(target.id)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_117\", path, \"target execution is recorded more than once\"),\n );\n }\n seen.add(target.id);\n if (!selected.has(target.id)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_118\", path, \"target execution was not selected for this candidate\"),\n );\n }\n }\n for (const id of evidence.selection.targetIds) {\n if (!seen.has(id)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_119\",\n evidencePath(\"selection.targetIds\", id),\n \"selected target has no recorded execution\",\n ),\n );\n }\n }\n}\n\ninterface ShardGroup {\n readonly shardCount: number;\n readonly kind: TestRunSuiteExecution[\"kind\"];\n readonly targetId: string;\n readonly indexes: Set<number>;\n kindsDisagree: boolean;\n targetsDisagree: boolean;\n countsDisagree: boolean;\n}\n\n/** Validates recorded suite executions, shards, and their consistency. */\nexport function validateSuites(\n evidence: TestRunEvidence,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (evidence.suites.length === 0 || evidence.suites.length > TEST_RUN_MAX_SUITES) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_131\",\n \"suites\",\n \"record must include between 1 and 512 suite executions\",\n ),\n );\n }\n\n const shards = new Map<string, ShardGroup>();\n const selected = new Set(evidence.selection.suiteIds);\n const recordedTargets = new Set(evidence.targets.map((target) => target.id));\n for (const suite of evidence.suites) {\n const path = evidencePath(\"suites\", suite.id);\n let group = shards.get(suite.id);\n if (group === undefined) {\n group = {\n shardCount: suite.shardCount,\n kind: suite.kind,\n targetId: suite.targetId,\n indexes: new Set(),\n kindsDisagree: false,\n targetsDisagree: false,\n countsDisagree: false,\n };\n shards.set(suite.id, group);\n // Suite-id-scoped invariants are shard-independent, so evaluate them\n // once per unique suite id. Running them per shard would emit the same\n // code/path/message diagnostic once for every shard of the suite.\n checkIdentifier(suite.id, path, diagnostics);\n if (!selected.has(suite.id)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_121\", path, \"suite execution was not selected for this candidate\"),\n );\n }\n if (!recordedTargets.has(suite.targetId)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_123\",\n evidencePath(path, \"targetId\"),\n \"suite target has no recorded target execution\",\n ),\n );\n }\n }\n group.kindsDisagree ||= group.kind !== suite.kind;\n group.targetsDisagree ||= group.targetId !== suite.targetId;\n group.countsDisagree ||= group.shardCount !== suite.shardCount;\n if (group.indexes.has(suite.shardIndex)) {\n diagnostics.push(error(\"VIZE_MARQUETTE_120\", path, \"suite shard is recorded more than once\"));\n }\n group.indexes.add(suite.shardIndex);\n if (\n suite.shardCount === 0 ||\n suite.shardCount > TEST_RUN_MAX_SHARDS ||\n suite.shardIndex === 0 ||\n suite.shardIndex > suite.shardCount\n ) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_124\",\n evidencePath(path, \"shardIndex\"),\n \"shard index must fall within a shard count between 1 and 1024\",\n ),\n );\n }\n checkSafeInteger(suite.durationMs, evidencePath(path, \"durationMs\"), diagnostics);\n checkDigest(\n suite.invocationFingerprint,\n evidencePath(path, \"invocationFingerprint\"),\n diagnostics,\n );\n checkRetainedEvidence(suite.report, evidencePath(path, \"report\"), diagnostics);\n checkRetainedEvidence(suite.log, evidencePath(path, \"log\"), diagnostics);\n\n const executed = suite.passed + suite.failed + suite.skipped;\n const consistent =\n suite.outcome === \"passed\"\n ? suite.failed === 0 && executed > 0\n : suite.outcome === \"failed\"\n ? suite.failed > 0\n : true;\n if (!consistent) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_130\",\n evidencePath(path, \"outcome\"),\n \"suite outcome does not match its recorded counts\",\n ),\n );\n }\n }\n\n for (const id of evidence.selection.suiteIds) {\n if (!shards.has(id)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_122\",\n evidencePath(\"selection.suiteIds\", id),\n \"selected suite has no recorded execution\",\n ),\n );\n }\n }\n\n for (const [id, group] of shards) {\n const path = evidencePath(\"suites\", id);\n if (group.kindsDisagree || group.targetsDisagree || group.countsDisagree) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_126\",\n path,\n \"every shard of one suite must share its kind, target, and shard count\",\n ),\n );\n } else if (\n group.shardCount !== 0 &&\n group.shardCount <= TEST_RUN_MAX_SHARDS &&\n group.indexes.size !== group.shardCount\n ) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_125\",\n path,\n `suite must record every shard from 1 to ${group.shardCount}`,\n ),\n );\n }\n }\n}\n\n/** Validates the independent verification summary. */\nexport function validateVerification(\n evidence: TestRunEvidence,\n diagnostics: MarquetteDiagnostic[],\n): void {\n const verification = evidence.verification;\n checkIdentifier(verification.verifier, \"verification.verifier\", diagnostics);\n checkTimestamp(verification.completedAt, \"verification.completedAt\", diagnostics);\n checkRetainedEvidence(verification.evidence, \"verification.evidence\", diagnostics);\n if (verification.completedAt < evidence.completedAt) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_114\",\n \"verification.completedAt\",\n \"verification must complete after the run completes\",\n ),\n );\n }\n if (verification.targetCount !== evidence.targets.length) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_127\",\n \"verification.targetCount\",\n \"verified target count must equal the recorded target executions\",\n ),\n );\n }\n if (verification.suiteCount !== evidence.suites.length) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_127\",\n \"verification.suiteCount\",\n \"verified suite count must equal the recorded suite executions\",\n ),\n );\n }\n\n const totals = { passed: 0, failed: 0, skipped: 0, retries: 0 };\n for (const suite of evidence.suites) {\n totals.passed += suite.passed;\n totals.failed += suite.failed;\n totals.skipped += suite.skipped;\n totals.retries += suite.retries;\n }\n const recorded = [\n [totals.passed, verification.passed, \"verification.passed\", \"passed\"],\n [totals.failed, verification.failed, \"verification.failed\", \"failed\"],\n [totals.skipped, verification.skipped, \"verification.skipped\", \"skipped\"],\n [totals.retries, verification.retries, \"verification.retries\", \"retried\"],\n ] as const;\n for (const [total, value, path, name] of recorded) {\n if (total !== value) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_128\",\n path,\n `verified ${name} total must equal the sum over suite executions`,\n ),\n );\n }\n }\n\n if (verification.outcome === \"accepted\") {\n const clean = evidence.suites.every(\n (suite) => suite.outcome === \"passed\" && suite.failed === 0,\n );\n if (!clean || verification.failed > 0) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_129\",\n \"verification.outcome\",\n \"verification cannot accept a run with failed or cancelled executions\",\n ),\n );\n }\n }\n}\n","import type { MarquetteDiagnostic } from \"./validate.js\";\nimport type { TestRunEvidence } from \"./test-run-model.js\";\nimport { TEST_RUN_EVIDENCE_FORMAT, TEST_RUN_EVIDENCE_FORMAT_VERSION } from \"./test-run-model.js\";\nimport {\n checkDigest,\n checkIdentifier,\n checkRetainedEvidence,\n checkSafeInteger,\n checkSourceRevision,\n checkTimestamp,\n error,\n evidencePath,\n} from \"./test-run-validate-rules.js\";\nimport {\n TEST_RUN_MAX_SUITES,\n TEST_RUN_MAX_TARGETS,\n validateSuites,\n validateTargets,\n validateVerification,\n} from \"./test-run-validate-executions.js\";\n\nexport {\n TEST_RUN_MAX_SHARDS,\n TEST_RUN_MAX_SUITES,\n TEST_RUN_MAX_TARGETS,\n} from \"./test-run-validate-executions.js\";\nexport type { MarquetteDiagnostic, MarquetteDiagnosticSeverity } from \"./validate.js\";\n\n/**\n * Validates a complete test-run evidence record.\n *\n * Diagnostics are deterministic and sorted by path, code, and message so the\n * same record produces identical CLI, promotion, test, and CI output in\n * every consuming language. A record with any error diagnostic must never\n * satisfy a deployment check.\n */\nexport function validateTestRunEvidence(evidence: TestRunEvidence): MarquetteDiagnostic[] {\n const diagnostics: MarquetteDiagnostic[] = [];\n\n if (evidence.format !== TEST_RUN_EVIDENCE_FORMAT) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_101\", \"format\", \"unsupported test-run evidence format marker\"),\n );\n }\n if ((evidence.formatVersion ?? 1) !== TEST_RUN_EVIDENCE_FORMAT_VERSION) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_102\", \"formatVersion\", \"unsupported test-run evidence format version\"),\n );\n }\n\n checkIdentifier(evidence.id, \"id\", diagnostics);\n checkIdentifier(evidence.application, \"application\", diagnostics);\n checkIdentifier(evidence.environment, \"environment\", diagnostics);\n checkDigest(evidence.contractFingerprint, \"contractFingerprint\", diagnostics);\n checkSourceRevision(evidence.sourceRevision, \"sourceRevision\", diagnostics);\n if (evidence.release.length === 0 || evidence.release.length > 256) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_106\", \"release\", \"release must be between 1 and 256 characters\"),\n );\n }\n\n checkIdentifier(evidence.artifact.id, \"artifact.id\", diagnostics);\n checkDigest(evidence.artifact.fingerprint, \"artifact.fingerprint\", diagnostics);\n if (evidence.artifact.sizeBytes === 0) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_110\", \"artifact.sizeBytes\", \"artifact size must be at least one byte\"),\n );\n }\n checkSafeInteger(evidence.artifact.sizeBytes, \"artifact.sizeBytes\", diagnostics);\n\n checkTimestamp(evidence.startedAt, \"startedAt\", diagnostics);\n checkTimestamp(evidence.completedAt, \"completedAt\", diagnostics);\n checkTimestamp(evidence.validUntil, \"validUntil\", diagnostics);\n if (evidence.completedAt < evidence.startedAt) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_112\", \"completedAt\", \"run completion must not precede its start\"),\n );\n }\n if (evidence.validUntil <= evidence.completedAt) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_113\", \"validUntil\", \"record expiry must come after run completion\"),\n );\n }\n\n const runner = evidence.runner;\n checkIdentifier(runner.identity, \"runner.identity\", diagnostics);\n checkRetainedEvidence(\n runner.authenticationEvidence,\n \"runner.authenticationEvidence\",\n diagnostics,\n );\n checkDigest(runner.invocationFingerprint, \"runner.invocationFingerprint\", diagnostics);\n checkRetainedEvidence(runner.environmentEvidence, \"runner.environmentEvidence\", diagnostics);\n checkDigest(runner.environmentFingerprint, \"runner.environmentFingerprint\", diagnostics);\n\n const selection = evidence.selection;\n if (selection.targetIds.length === 0 || selection.targetIds.length > TEST_RUN_MAX_TARGETS) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_115\",\n \"selection.targetIds\",\n \"selection must include between 1 and 32 targets\",\n ),\n );\n }\n if (selection.suiteIds.length === 0 || selection.suiteIds.length > TEST_RUN_MAX_SUITES) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_115\",\n \"selection.suiteIds\",\n \"selection must include between 1 and 512 suites\",\n ),\n );\n }\n for (const id of selection.targetIds) {\n checkIdentifier(id, evidencePath(\"selection.targetIds\", id), diagnostics);\n }\n for (const id of selection.suiteIds) {\n checkIdentifier(id, evidencePath(\"selection.suiteIds\", id), diagnostics);\n }\n\n validateTargets(evidence, diagnostics);\n validateSuites(evidence, diagnostics);\n validateVerification(evidence, diagnostics);\n\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n return diagnostics;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as TestRunEvidence } from "./test-run-model-
|
|
1
|
+
import { i as TestRunEvidence } from "./test-run-model-CF2EYJOQ.mjs";
|
|
2
2
|
import { MarquetteDiagnostic, MarquetteDiagnosticSeverity } from "./validate.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/test-run-validate-executions.d.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-validate.d.mts","names":[],"sources":["../src/test-run-validate-executions.ts","../src/test-run-validate.ts"],"mappings":";;;;;cAaa,oBAAA;AAAb;AAAA,cAGa,mBAAA;;cAGA,mBAAA;;;AANb
|
|
1
|
+
{"version":3,"file":"test-run-validate.d.mts","names":[],"sources":["../src/test-run-validate-executions.ts","../src/test-run-validate.ts"],"mappings":";;;;;cAaa,oBAAA;AAAb;AAAA,cAGa,mBAAA;;cAGA,mBAAA;;;AANb;;;;AAAiC;AAGjC;;;AAHA,iBCuBgB,uBAAA,CAAwB,QAAA,EAAU,eAAA,GAAkB,mBAAmB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as TEST_RUN_MAX_TARGETS, n as TEST_RUN_MAX_SHARDS, r as TEST_RUN_MAX_SUITES, t as validateTestRunEvidence } from "./test-run-validate-
|
|
1
|
+
import { i as TEST_RUN_MAX_TARGETS, n as TEST_RUN_MAX_SHARDS, r as TEST_RUN_MAX_SUITES, t as validateTestRunEvidence } from "./test-run-validate-DwGMo8ON.mjs";
|
|
2
2
|
export { TEST_RUN_MAX_SHARDS, TEST_RUN_MAX_SUITES, TEST_RUN_MAX_TARGETS, validateTestRunEvidence };
|
package/dist/test-run.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as TestRunVerificationOutcome, a as TestRunIsolation, c as TestRunSelection, d as TestRunSuiteKind, f as TestRunSuiteOutcome, g as TestRunVerification, h as TestRunTargetKind, i as TestRunEvidence, l as TestRunSuiteExecution, m as TestRunTargetId, n as TEST_RUN_EVIDENCE_FORMAT_VERSION, o as TestRunRetainedEvidence, p as TestRunTargetExecution, r as TestRunArtifact, s as TestRunRunner, t as TEST_RUN_EVIDENCE_FORMAT, u as TestRunSuiteId } from "./test-run-model-
|
|
1
|
+
import { _ as TestRunVerificationOutcome, a as TestRunIsolation, c as TestRunSelection, d as TestRunSuiteKind, f as TestRunSuiteOutcome, g as TestRunVerification, h as TestRunTargetKind, i as TestRunEvidence, l as TestRunSuiteExecution, m as TestRunTargetId, n as TEST_RUN_EVIDENCE_FORMAT_VERSION, o as TestRunRetainedEvidence, p as TestRunTargetExecution, r as TestRunArtifact, s as TestRunRunner, t as TEST_RUN_EVIDENCE_FORMAT, u as TestRunSuiteId } from "./test-run-model-CF2EYJOQ.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/test-run.d.ts
|
|
4
4
|
/**
|
package/dist/test-run.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run.d.mts","names":[],"sources":["../src/test-run.ts"],"mappings":";;;;AAiCA;;;;;KAAY,2BAAA,kBAA6C,eAAA;EAAA,SAC9C,SAAA;IAAA,SACE,SAAA,WAAoB,eAAA,CAAgB,QAAA;IAAA,SACpC,QAAA,WAAmB,cAAA,CAAe,QAAA;EAAA;EAAA,SAEpC,MAAA,aAAmB,qBAAA,SAA8B,eAAA,CAAgB,QAAA;IAAA,SAC/D,QAAA,EAAU,eAAA,CAAgB,QAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;iBAsBvB,qBAAA,wBAA6C,eAAA,
|
|
1
|
+
{"version":3,"file":"test-run.d.mts","names":[],"sources":["../src/test-run.ts"],"mappings":";;;;AAiCA;;;;;KAAY,2BAAA,kBAA6C,eAAA;EAAA,SAC9C,SAAA;IAAA,SACE,SAAA,WAAoB,eAAA,CAAgB,QAAA;IAAA,SACpC,QAAA,WAAmB,cAAA,CAAe,QAAA;EAAA;EAAA,SAEpC,MAAA,aAAmB,qBAAA,SAA8B,eAAA,CAAgB,QAAA;IAAA,SAC/D,QAAA,EAAU,eAAA,CAAgB,QAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;iBAsBvB,qBAAA,wBAA6C,eAAA,EAC3D,QAAA,EAAU,QAAA,GAAW,2BAAA,CAA4B,OAAA,CAAQ,QAAA,KACxD,QAAA"}
|
package/dist/test-run.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as TEST_RUN_EVIDENCE_FORMAT_VERSION, t as TEST_RUN_EVIDENCE_FORMAT } from "./test-run-model-
|
|
1
|
+
import { n as TEST_RUN_EVIDENCE_FORMAT_VERSION, t as TEST_RUN_EVIDENCE_FORMAT } from "./test-run-model-DgJ2nd5p.mjs";
|
|
2
2
|
//#region src/test-run.ts
|
|
3
3
|
/**
|
|
4
4
|
* Defines a test-run evidence record while preserving every authored literal.
|
package/dist/test-run.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run.mjs","names":[],"sources":["../src/test-run.ts"],"sourcesContent":["import type {\n TestRunEvidence,\n TestRunSuiteExecution,\n TestRunSuiteId,\n TestRunTargetId,\n} from \"./test-run-model.js\";\n\nexport {\n TEST_RUN_EVIDENCE_FORMAT,\n TEST_RUN_EVIDENCE_FORMAT_VERSION,\n type TestRunArtifact,\n type TestRunEvidence,\n type TestRunIsolation,\n type TestRunRetainedEvidence,\n type TestRunRunner,\n type TestRunSelection,\n type TestRunSuiteExecution,\n type TestRunSuiteId,\n type TestRunSuiteKind,\n type TestRunSuiteOutcome,\n type TestRunTargetExecution,\n type TestRunTargetId,\n type TestRunTargetKind,\n type TestRunVerification,\n type TestRunVerificationOutcome,\n} from \"./test-run-model.js\";\n\n/**\n * Cross-reference constraints derived from the literals in one record.\n *\n * Keeping this type separate makes editor diagnostics point at the authored\n * reference instead of widening every identifier to `string`.\n */\nexport type TestRunReferenceConstraints<Evidence extends TestRunEvidence> = {\n readonly selection?: {\n readonly targetIds: readonly TestRunTargetId<Evidence>[];\n readonly suiteIds: readonly TestRunSuiteId<Evidence>[];\n };\n readonly suites?: readonly (TestRunSuiteExecution<string, TestRunTargetId<Evidence>> & {\n readonly targetId: TestRunTargetId<Evidence>;\n })[];\n};\n\n/**\n * Defines a test-run evidence record while preserving every authored literal.\n *\n * The candidate selection and every suite's target reference are checked\n * against identifiers recorded in the same object. The function returns its\n * input without allocation or runtime work; use the `/test-run/validate`\n * entry before trusting a record.\n *\n * @example\n * ```ts\n * const evidence = defineTestRunEvidence({\n * format: \"vize.test-run.evidence\",\n * id: \"run-1\",\n * application: \"shop\",\n * // ...bindings, runner, selection, targets, suites, verification\n * });\n * ```\n */\nexport function defineTestRunEvidence<const Evidence extends TestRunEvidence>(\n evidence: Evidence & TestRunReferenceConstraints<NoInfer<Evidence>>,\n): Evidence {\n return evidence;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA6DA,SAAgB,sBACd,UACU;CACV,OAAO"}
|
|
1
|
+
{"version":3,"file":"test-run.mjs","names":[],"sources":["../src/test-run.ts"],"sourcesContent":["import type {\n TestRunEvidence,\n TestRunSuiteExecution,\n TestRunSuiteId,\n TestRunTargetId,\n} from \"./test-run-model.js\";\n\nexport {\n TEST_RUN_EVIDENCE_FORMAT,\n TEST_RUN_EVIDENCE_FORMAT_VERSION,\n type TestRunArtifact,\n type TestRunEvidence,\n type TestRunIsolation,\n type TestRunRetainedEvidence,\n type TestRunRunner,\n type TestRunSelection,\n type TestRunSuiteExecution,\n type TestRunSuiteId,\n type TestRunSuiteKind,\n type TestRunSuiteOutcome,\n type TestRunTargetExecution,\n type TestRunTargetId,\n type TestRunTargetKind,\n type TestRunVerification,\n type TestRunVerificationOutcome,\n} from \"./test-run-model.js\";\n\n/**\n * Cross-reference constraints derived from the literals in one record.\n *\n * Keeping this type separate makes editor diagnostics point at the authored\n * reference instead of widening every identifier to `string`.\n */\nexport type TestRunReferenceConstraints<Evidence extends TestRunEvidence> = {\n readonly selection?: {\n readonly targetIds: readonly TestRunTargetId<Evidence>[];\n readonly suiteIds: readonly TestRunSuiteId<Evidence>[];\n };\n readonly suites?: readonly (TestRunSuiteExecution<string, TestRunTargetId<Evidence>> & {\n readonly targetId: TestRunTargetId<Evidence>;\n })[];\n};\n\n/**\n * Defines a test-run evidence record while preserving every authored literal.\n *\n * The candidate selection and every suite's target reference are checked\n * against identifiers recorded in the same object. The function returns its\n * input without allocation or runtime work; use the `/test-run/validate`\n * entry before trusting a record.\n *\n * @example\n * ```ts\n * const evidence = defineTestRunEvidence({\n * format: \"vize.test-run.evidence\",\n * id: \"run-1\",\n * application: \"shop\",\n * // ...bindings, runner, selection, targets, suites, verification\n * });\n * ```\n */\nexport function defineTestRunEvidence<const Evidence extends TestRunEvidence>(\n evidence: Evidence & TestRunReferenceConstraints<NoInfer<Evidence>>,\n): Evidence {\n return evidence;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA6DA,SAAgB,sBACd,UACU;CACV,OAAO;AACT"}
|
package/dist/validate.d.mts
CHANGED
package/dist/validate.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.mts","names":[],"sources":["../src/validate.ts"],"mappings":";;;;KAUY,2BAAA;AAAZ;AAAA,UAGiB,mBAAA;;WAEN,IAAA;EAL4B;EAAA,SAO5B,QAAA,EAAU,
|
|
1
|
+
{"version":3,"file":"validate.d.mts","names":[],"sources":["../src/validate.ts"],"mappings":";;;;KAUY,2BAAA;AAAZ;AAAA,UAGiB,mBAAA;;WAEN,IAAA;EAL4B;EAAA,SAO5B,QAAA,EAAU,2BAA2B;EAJZ;EAAA,SAMzB,IAAA;EAFqC;EAAA,SAIrC,OAAA;AAAA;;;;;AAAO;AAelB;;;;iBAAgB,4BAAA,CACd,SAAA,EAAW,oBAAA,GACV,mBAAmB"}
|
package/dist/validate.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.mjs","names":[],"sources":["../src/validate.ts"],"sourcesContent":["import type {\n ApplicationMarquette,\n MARQUETTE_FORMAT_VERSION,\n MarquetteEnvironment,\n MarquetteRoute,\n MarquetteTarget,\n RenderingMode,\n} from \"./model.js\";\n\n/** Severity of an application-marquette diagnostic. */\nexport type MarquetteDiagnosticSeverity = \"error\" | \"warning\";\n\n/** Stable, source-addressable application-marquette diagnostic. */\nexport interface MarquetteDiagnostic {\n /** Stable machine-readable diagnostic code. */\n readonly code: `VIZE_MARQUETTE_${string}`;\n /** Severity used by CLI, editor, test, and CI consumers. */\n readonly severity: MarquetteDiagnosticSeverity;\n /** JSON-style path into the authored marquette. */\n readonly path: string;\n /** Human-readable explanation and next action. */\n readonly message: string;\n}\n\nconst IDENTIFIER = /^[a-z0-9][a-z0-9._-]*$/;\nconst SUPPORTED_FORMAT_VERSION: typeof MARQUETTE_FORMAT_VERSION = 1;\n\n/**\n * Validates a complete application marquette.\n *\n * Diagnostics cover structural, reference, capability, target, and dependency\n * invariants. Results are sorted by path, code, and message so identical input\n * produces stable editor, CLI, test, and CI output.\n *\n * This function does not mutate the authored object and does not throw.\n */\nexport function validateApplicationMarquette(\n marquette: ApplicationMarquette,\n): MarquetteDiagnostic[] {\n const diagnostics: MarquetteDiagnostic[] = [];\n const environments = marquette.environments ?? [];\n const backends = marquette.backends ?? [];\n const protocols = marquette.protocols ?? [];\n const routes = marquette.routes ?? [];\n const targets = new Set(marquette.targets ?? []);\n const capabilityIds = new Set(Object.keys(marquette.capabilities ?? {}));\n\n if ((marquette.formatVersion ?? SUPPORTED_FORMAT_VERSION) !== SUPPORTED_FORMAT_VERSION) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_001\", \"formatVersion\", \"unsupported marquette format version\"),\n );\n }\n\n validateIdentifier(marquette.application, \"application\", \"VIZE_MARQUETTE_002\", diagnostics);\n\n for (const [key, capability] of Object.entries(marquette.capabilities ?? {})) {\n const path = contractPath(\"capabilities\", key);\n validateIdentifier(key, path, \"VIZE_MARQUETTE_003\", diagnostics);\n if (key !== capability.id) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_004\", path, \"capability map key must equal capability.id\"),\n );\n }\n const version = capability.version ?? 1;\n if (!Number.isInteger(version) || version < 1) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_005\", path, \"capability version must be greater than zero\"),\n );\n }\n if (capability.description.trim().length === 0) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_024\", path, \"capability description must not be empty\"),\n );\n }\n }\n\n const environmentIds = collectUniqueIds(\"environments\", environments, diagnostics);\n const backendIds = collectUniqueIds(\"backends\", backends, diagnostics);\n const protocolIds = collectUniqueIds(\"protocols\", protocols, diagnostics);\n collectUniqueIds(\"routes\", routes, diagnostics);\n\n for (const environment of environments) {\n const path = contractPath(\"environments\", environment.id);\n if (!targets.has(environment.target)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_007\", path, \"environment target must be declared in targets\"),\n );\n }\n for (const dependency of [...new Set(environment.dependsOn ?? [])].sort()) {\n if (dependency === environment.id) {\n diagnostics.push(error(\"VIZE_MARQUETTE_008\", path, \"environment cannot depend on itself\"));\n } else if (!environmentIds.has(dependency)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_009\", path, \"environment dependency does not exist\"),\n );\n }\n }\n validateCapabilities(path, environment.capabilities, capabilityIds, diagnostics);\n\n if (\n environment.consumer === \"client\" &&\n (environment.runtime === \"rust\" ||\n environment.runtime === \"go\" ||\n environment.runtime === \"jvm\")\n ) {\n diagnostics.push(\n warning(\n \"VIZE_MARQUETTE_010\",\n path,\n \"client environment uses a server-oriented runtime; declare an adapter capability if this is intentional\",\n ),\n );\n }\n }\n\n validateEnvironmentCycles(environments, diagnostics);\n\n for (const backend of backends) {\n const path = contractPath(\"backends\", backend.id);\n if (backend.environment != null) {\n const environment = environments.find((candidate) => candidate.id === backend.environment);\n if (environment == null) {\n diagnostics.push(error(\"VIZE_MARQUETTE_011\", path, \"backend environment does not exist\"));\n } else if (environment.consumer !== \"server\") {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_012\", path, \"backend environment must be a server consumer\"),\n );\n }\n }\n validateCapabilities(path, backend.capabilities, capabilityIds, diagnostics);\n }\n\n for (const protocol of protocols) {\n const path = contractPath(\"protocols\", protocol.id);\n if (!backendIds.has(protocol.backend)) {\n diagnostics.push(error(\"VIZE_MARQUETTE_013\", path, \"protocol backend does not exist\"));\n }\n validateCapabilities(path, protocol.capabilities, capabilityIds, diagnostics);\n }\n\n const routePaths = new Map<string, string>();\n for (const route of routes) {\n const path = contractPath(\"routes\", route.id);\n if (!route.path.startsWith(\"/\")) {\n diagnostics.push(error(\"VIZE_MARQUETTE_014\", path, \"route path must start with /\"));\n }\n const environment = environments.find((candidate) => candidate.id === route.environment);\n if (environment == null) {\n diagnostics.push(error(\"VIZE_MARQUETTE_015\", path, \"route environment does not exist\"));\n }\n if (route.backend != null && !backendIds.has(route.backend)) {\n diagnostics.push(error(\"VIZE_MARQUETTE_016\", path, \"route backend does not exist\"));\n }\n if (route.protocol != null) {\n const protocol = protocols.find((candidate) => candidate.id === route.protocol);\n if (!protocolIds.has(route.protocol) || protocol == null) {\n diagnostics.push(error(\"VIZE_MARQUETTE_017\", path, \"route protocol does not exist\"));\n } else if (route.backend != null && protocol.backend !== route.backend) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_018\",\n path,\n \"route protocol and backend must refer to the same service\",\n ),\n );\n }\n }\n if (environment != null && !renderingMatchesTarget(route, environment.target)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_023\",\n path,\n \"rendering mode is not compatible with the route environment target\",\n ),\n );\n }\n validateCapabilities(path, route.capabilities, capabilityIds, diagnostics);\n\n const routeKey = `${route.environment}\\u0000${route.path}`;\n const previous = routePaths.get(routeKey);\n if (previous != null) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_019\", path, `route path is already used by route \"${previous}\"`),\n );\n }\n routePaths.set(routeKey, route.id);\n }\n\n for (const target of targets) {\n if (!environments.some((environment) => environment.target === target)) {\n diagnostics.push(\n warning(\"VIZE_MARQUETTE_020\", \"targets\", \"declared target has no environment\"),\n );\n }\n }\n\n return diagnostics.sort(compareDiagnostics);\n}\n\nfunction collectUniqueIds(\n collection: string,\n values: readonly { readonly id: string }[],\n diagnostics: MarquetteDiagnostic[],\n): Set<string> {\n const ids = new Set<string>();\n for (const value of values) {\n const path = contractPath(collection, value.id);\n validateIdentifier(value.id, path, \"VIZE_MARQUETTE_006\", diagnostics);\n if (ids.has(value.id)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_006\", path, \"identifier must be unique within its collection\"),\n );\n }\n ids.add(value.id);\n }\n return ids;\n}\n\nfunction validateIdentifier(\n id: string,\n path: string,\n code: `VIZE_MARQUETTE_${string}`,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (!IDENTIFIER.test(id)) {\n diagnostics.push(\n error(\n code,\n path,\n \"identifier must use lowercase ASCII letters, digits, dash, underscore, or dot\",\n ),\n );\n }\n}\n\nfunction validateCapabilities(\n path: string,\n capabilities: readonly string[] | undefined,\n declared: ReadonlySet<string>,\n diagnostics: MarquetteDiagnostic[],\n): void {\n for (const capability of [...new Set(capabilities ?? [])].sort()) {\n if (!declared.has(capability)) {\n diagnostics.push(error(\"VIZE_MARQUETTE_021\", path, \"referenced capability is not declared\"));\n }\n }\n}\n\nfunction validateEnvironmentCycles(\n environments: readonly MarquetteEnvironment[],\n diagnostics: MarquetteDiagnostic[],\n): void {\n const graph = new Map(environments.map((value) => [value.id, value.dependsOn ?? []]));\n const visiting = new Set<string>();\n const visited = new Set<string>();\n\n for (const id of [...graph.keys()].sort()) {\n if (hasCycle(id, graph, visiting, visited)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_022\",\n contractPath(\"environments\", id),\n \"environment dependency graph must be acyclic\",\n ),\n );\n }\n }\n}\n\nfunction hasCycle(\n id: string,\n graph: ReadonlyMap<string, readonly string[]>,\n visiting: Set<string>,\n visited: Set<string>,\n): boolean {\n if (visited.has(id)) return false;\n if (visiting.has(id)) return true;\n visiting.add(id);\n const cyclic = [...(graph.get(id) ?? [])]\n .sort()\n .some((dependency) => graph.has(dependency) && hasCycle(dependency, graph, visiting, visited));\n visiting.delete(id);\n visited.add(id);\n return cyclic;\n}\n\nfunction renderingMatchesTarget(route: MarquetteRoute, target: MarquetteTarget): boolean {\n const targetRendering: Partial<Record<RenderingMode, MarquetteTarget>> = {\n native: \"native\",\n desktop: \"desktop\",\n terminal: \"terminal\",\n };\n return (targetRendering[route.rendering] ?? \"web\") === target;\n}\n\nfunction compareDiagnostics(left: MarquetteDiagnostic, right: MarquetteDiagnostic): number {\n return (\n compareText(left.path, right.path) ||\n compareText(left.code, right.code) ||\n compareText(left.message, right.message)\n );\n}\n\nfunction compareText(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction contractPath(collection: string, id: string): string {\n return `${collection}.${id}`;\n}\n\nfunction error(\n code: `VIZE_MARQUETTE_${string}`,\n path: string,\n message: string,\n): MarquetteDiagnostic {\n return { code, severity: \"error\", path, message };\n}\n\nfunction warning(\n code: `VIZE_MARQUETTE_${string}`,\n path: string,\n message: string,\n): MarquetteDiagnostic {\n return { code, severity: \"warning\", path, message };\n}\n"],"mappings":";AAwBA,MAAM,aAAa;AACnB,MAAM,2BAA4D;;;;;;;;;;AAWlE,SAAgB,6BACd,WACuB;CACvB,MAAM,cAAqC,EAAE;CAC7C,MAAM,eAAe,UAAU,gBAAgB,EAAE;CACjD,MAAM,WAAW,UAAU,YAAY,EAAE;CACzC,MAAM,YAAY,UAAU,aAAa,EAAE;CAC3C,MAAM,SAAS,UAAU,UAAU,EAAE;CACrC,MAAM,UAAU,IAAI,IAAI,UAAU,WAAW,EAAE,CAAC;CAChD,MAAM,gBAAgB,IAAI,IAAI,OAAO,KAAK,UAAU,gBAAgB,EAAE,CAAC,CAAC;CAExE,KAAK,UAAU,iBAAiB,8BAA8B,0BAC5D,YAAY,KACV,MAAM,sBAAsB,iBAAiB,uCAAuC,CACrF;CAGH,mBAAmB,UAAU,aAAa,eAAe,sBAAsB,YAAY;CAE3F,KAAK,MAAM,CAAC,KAAK,eAAe,OAAO,QAAQ,UAAU,gBAAgB,EAAE,CAAC,EAAE;EAC5E,MAAM,OAAO,aAAa,gBAAgB,IAAI;EAC9C,mBAAmB,KAAK,MAAM,sBAAsB,YAAY;EAChE,IAAI,QAAQ,WAAW,IACrB,YAAY,KACV,MAAM,sBAAsB,MAAM,8CAA8C,CACjF;EAEH,MAAM,UAAU,WAAW,WAAW;EACtC,IAAI,CAAC,OAAO,UAAU,QAAQ,IAAI,UAAU,GAC1C,YAAY,KACV,MAAM,sBAAsB,MAAM,+CAA+C,CAClF;EAEH,IAAI,WAAW,YAAY,MAAM,CAAC,WAAW,GAC3C,YAAY,KACV,MAAM,sBAAsB,MAAM,2CAA2C,CAC9E;;CAIL,MAAM,iBAAiB,iBAAiB,gBAAgB,cAAc,YAAY;CAClF,MAAM,aAAa,iBAAiB,YAAY,UAAU,YAAY;CACtE,MAAM,cAAc,iBAAiB,aAAa,WAAW,YAAY;CACzE,iBAAiB,UAAU,QAAQ,YAAY;CAE/C,KAAK,MAAM,eAAe,cAAc;EACtC,MAAM,OAAO,aAAa,gBAAgB,YAAY,GAAG;EACzD,IAAI,CAAC,QAAQ,IAAI,YAAY,OAAO,EAClC,YAAY,KACV,MAAM,sBAAsB,MAAM,iDAAiD,CACpF;EAEH,KAAK,MAAM,cAAc,CAAC,GAAG,IAAI,IAAI,YAAY,aAAa,EAAE,CAAC,CAAC,CAAC,MAAM,EACvE,IAAI,eAAe,YAAY,IAC7B,YAAY,KAAK,MAAM,sBAAsB,MAAM,sCAAsC,CAAC;OACrF,IAAI,CAAC,eAAe,IAAI,WAAW,EACxC,YAAY,KACV,MAAM,sBAAsB,MAAM,wCAAwC,CAC3E;EAGL,qBAAqB,MAAM,YAAY,cAAc,eAAe,YAAY;EAEhF,IACE,YAAY,aAAa,aACxB,YAAY,YAAY,UACvB,YAAY,YAAY,QACxB,YAAY,YAAY,QAE1B,YAAY,KACV,QACE,sBACA,MACA,0GACD,CACF;;CAIL,0BAA0B,cAAc,YAAY;CAEpD,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,OAAO,aAAa,YAAY,QAAQ,GAAG;EACjD,IAAI,QAAQ,eAAe,MAAM;GAC/B,MAAM,cAAc,aAAa,MAAM,cAAc,UAAU,OAAO,QAAQ,YAAY;GAC1F,IAAI,eAAe,MACjB,YAAY,KAAK,MAAM,sBAAsB,MAAM,qCAAqC,CAAC;QACpF,IAAI,YAAY,aAAa,UAClC,YAAY,KACV,MAAM,sBAAsB,MAAM,gDAAgD,CACnF;;EAGL,qBAAqB,MAAM,QAAQ,cAAc,eAAe,YAAY;;CAG9E,KAAK,MAAM,YAAY,WAAW;EAChC,MAAM,OAAO,aAAa,aAAa,SAAS,GAAG;EACnD,IAAI,CAAC,WAAW,IAAI,SAAS,QAAQ,EACnC,YAAY,KAAK,MAAM,sBAAsB,MAAM,kCAAkC,CAAC;EAExF,qBAAqB,MAAM,SAAS,cAAc,eAAe,YAAY;;CAG/E,MAAM,6BAAa,IAAI,KAAqB;CAC5C,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,aAAa,UAAU,MAAM,GAAG;EAC7C,IAAI,CAAC,MAAM,KAAK,WAAW,IAAI,EAC7B,YAAY,KAAK,MAAM,sBAAsB,MAAM,+BAA+B,CAAC;EAErF,MAAM,cAAc,aAAa,MAAM,cAAc,UAAU,OAAO,MAAM,YAAY;EACxF,IAAI,eAAe,MACjB,YAAY,KAAK,MAAM,sBAAsB,MAAM,mCAAmC,CAAC;EAEzF,IAAI,MAAM,WAAW,QAAQ,CAAC,WAAW,IAAI,MAAM,QAAQ,EACzD,YAAY,KAAK,MAAM,sBAAsB,MAAM,+BAA+B,CAAC;EAErF,IAAI,MAAM,YAAY,MAAM;GAC1B,MAAM,WAAW,UAAU,MAAM,cAAc,UAAU,OAAO,MAAM,SAAS;GAC/E,IAAI,CAAC,YAAY,IAAI,MAAM,SAAS,IAAI,YAAY,MAClD,YAAY,KAAK,MAAM,sBAAsB,MAAM,gCAAgC,CAAC;QAC/E,IAAI,MAAM,WAAW,QAAQ,SAAS,YAAY,MAAM,SAC7D,YAAY,KACV,MACE,sBACA,MACA,4DACD,CACF;;EAGL,IAAI,eAAe,QAAQ,CAAC,uBAAuB,OAAO,YAAY,OAAO,EAC3E,YAAY,KACV,MACE,sBACA,MACA,qEACD,CACF;EAEH,qBAAqB,MAAM,MAAM,cAAc,eAAe,YAAY;EAE1E,MAAM,WAAW,GAAG,MAAM,YAAY,QAAQ,MAAM;EACpD,MAAM,WAAW,WAAW,IAAI,SAAS;EACzC,IAAI,YAAY,MACd,YAAY,KACV,MAAM,sBAAsB,MAAM,wCAAwC,SAAS,GAAG,CACvF;EAEH,WAAW,IAAI,UAAU,MAAM,GAAG;;CAGpC,KAAK,MAAM,UAAU,SACnB,IAAI,CAAC,aAAa,MAAM,gBAAgB,YAAY,WAAW,OAAO,EACpE,YAAY,KACV,QAAQ,sBAAsB,WAAW,qCAAqC,CAC/E;CAIL,OAAO,YAAY,KAAK,mBAAmB;;AAG7C,SAAS,iBACP,YACA,QACA,aACa;CACb,MAAM,sBAAM,IAAI,KAAa;CAC7B,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,aAAa,YAAY,MAAM,GAAG;EAC/C,mBAAmB,MAAM,IAAI,MAAM,sBAAsB,YAAY;EACrE,IAAI,IAAI,IAAI,MAAM,GAAG,EACnB,YAAY,KACV,MAAM,sBAAsB,MAAM,kDAAkD,CACrF;EAEH,IAAI,IAAI,MAAM,GAAG;;CAEnB,OAAO;;AAGT,SAAS,mBACP,IACA,MACA,MACA,aACM;CACN,IAAI,CAAC,WAAW,KAAK,GAAG,EACtB,YAAY,KACV,MACE,MACA,MACA,gFACD,CACF;;AAIL,SAAS,qBACP,MACA,cACA,UACA,aACM;CACN,KAAK,MAAM,cAAc,CAAC,GAAG,IAAI,IAAI,gBAAgB,EAAE,CAAC,CAAC,CAAC,MAAM,EAC9D,IAAI,CAAC,SAAS,IAAI,WAAW,EAC3B,YAAY,KAAK,MAAM,sBAAsB,MAAM,wCAAwC,CAAC;;AAKlG,SAAS,0BACP,cACA,aACM;CACN,MAAM,QAAQ,IAAI,IAAI,aAAa,KAAK,UAAU,CAAC,MAAM,IAAI,MAAM,aAAa,EAAE,CAAC,CAAC,CAAC;CACrF,MAAM,2BAAW,IAAI,KAAa;CAClC,MAAM,0BAAU,IAAI,KAAa;CAEjC,KAAK,MAAM,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,MAAM,EACvC,IAAI,SAAS,IAAI,OAAO,UAAU,QAAQ,EACxC,YAAY,KACV,MACE,sBACA,aAAa,gBAAgB,GAAG,EAChC,+CACD,CACF;;AAKP,SAAS,SACP,IACA,OACA,UACA,SACS;CACT,IAAI,QAAQ,IAAI,GAAG,EAAE,OAAO;CAC5B,IAAI,SAAS,IAAI,GAAG,EAAE,OAAO;CAC7B,SAAS,IAAI,GAAG;CAChB,MAAM,SAAS,CAAC,GAAI,MAAM,IAAI,GAAG,IAAI,EAAE,CAAE,CACtC,MAAM,CACN,MAAM,eAAe,MAAM,IAAI,WAAW,IAAI,SAAS,YAAY,OAAO,UAAU,QAAQ,CAAC;CAChG,SAAS,OAAO,GAAG;CACnB,QAAQ,IAAI,GAAG;CACf,OAAO;;AAGT,SAAS,uBAAuB,OAAuB,QAAkC;CAMvF,QAAQ;EAJN,QAAQ;EACR,SAAS;EACT,UAAU;EAEW,CAAC,MAAM,cAAc,WAAW;;AAGzD,SAAS,mBAAmB,MAA2B,OAAoC;CACzF,OACE,YAAY,KAAK,MAAM,MAAM,KAAK,IAClC,YAAY,KAAK,MAAM,MAAM,KAAK,IAClC,YAAY,KAAK,SAAS,MAAM,QAAQ;;AAI5C,SAAS,YAAY,MAAc,OAAuB;CACxD,OAAO,OAAO,QAAQ,KAAK,OAAO,QAAQ,IAAI;;AAGhD,SAAS,aAAa,YAAoB,IAAoB;CAC5D,OAAO,GAAG,WAAW,GAAG;;AAG1B,SAAS,MACP,MACA,MACA,SACqB;CACrB,OAAO;EAAE;EAAM,UAAU;EAAS;EAAM;EAAS;;AAGnD,SAAS,QACP,MACA,MACA,SACqB;CACrB,OAAO;EAAE;EAAM,UAAU;EAAW;EAAM;EAAS"}
|
|
1
|
+
{"version":3,"file":"validate.mjs","names":[],"sources":["../src/validate.ts"],"sourcesContent":["import type {\n ApplicationMarquette,\n MARQUETTE_FORMAT_VERSION,\n MarquetteEnvironment,\n MarquetteRoute,\n MarquetteTarget,\n RenderingMode,\n} from \"./model.js\";\n\n/** Severity of an application-marquette diagnostic. */\nexport type MarquetteDiagnosticSeverity = \"error\" | \"warning\";\n\n/** Stable, source-addressable application-marquette diagnostic. */\nexport interface MarquetteDiagnostic {\n /** Stable machine-readable diagnostic code. */\n readonly code: `VIZE_MARQUETTE_${string}`;\n /** Severity used by CLI, editor, test, and CI consumers. */\n readonly severity: MarquetteDiagnosticSeverity;\n /** JSON-style path into the authored marquette. */\n readonly path: string;\n /** Human-readable explanation and next action. */\n readonly message: string;\n}\n\nconst IDENTIFIER = /^[a-z0-9][a-z0-9._-]*$/;\nconst SUPPORTED_FORMAT_VERSION: typeof MARQUETTE_FORMAT_VERSION = 1;\n\n/**\n * Validates a complete application marquette.\n *\n * Diagnostics cover structural, reference, capability, target, and dependency\n * invariants. Results are sorted by path, code, and message so identical input\n * produces stable editor, CLI, test, and CI output.\n *\n * This function does not mutate the authored object and does not throw.\n */\nexport function validateApplicationMarquette(\n marquette: ApplicationMarquette,\n): MarquetteDiagnostic[] {\n const diagnostics: MarquetteDiagnostic[] = [];\n const environments = marquette.environments ?? [];\n const backends = marquette.backends ?? [];\n const protocols = marquette.protocols ?? [];\n const routes = marquette.routes ?? [];\n const targets = new Set(marquette.targets ?? []);\n const capabilityIds = new Set(Object.keys(marquette.capabilities ?? {}));\n\n if ((marquette.formatVersion ?? SUPPORTED_FORMAT_VERSION) !== SUPPORTED_FORMAT_VERSION) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_001\", \"formatVersion\", \"unsupported marquette format version\"),\n );\n }\n\n validateIdentifier(marquette.application, \"application\", \"VIZE_MARQUETTE_002\", diagnostics);\n\n for (const [key, capability] of Object.entries(marquette.capabilities ?? {})) {\n const path = contractPath(\"capabilities\", key);\n validateIdentifier(key, path, \"VIZE_MARQUETTE_003\", diagnostics);\n if (key !== capability.id) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_004\", path, \"capability map key must equal capability.id\"),\n );\n }\n const version = capability.version ?? 1;\n if (!Number.isInteger(version) || version < 1) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_005\", path, \"capability version must be greater than zero\"),\n );\n }\n if (capability.description.trim().length === 0) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_024\", path, \"capability description must not be empty\"),\n );\n }\n }\n\n const environmentIds = collectUniqueIds(\"environments\", environments, diagnostics);\n const backendIds = collectUniqueIds(\"backends\", backends, diagnostics);\n const protocolIds = collectUniqueIds(\"protocols\", protocols, diagnostics);\n collectUniqueIds(\"routes\", routes, diagnostics);\n\n for (const environment of environments) {\n const path = contractPath(\"environments\", environment.id);\n if (!targets.has(environment.target)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_007\", path, \"environment target must be declared in targets\"),\n );\n }\n for (const dependency of [...new Set(environment.dependsOn ?? [])].sort()) {\n if (dependency === environment.id) {\n diagnostics.push(error(\"VIZE_MARQUETTE_008\", path, \"environment cannot depend on itself\"));\n } else if (!environmentIds.has(dependency)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_009\", path, \"environment dependency does not exist\"),\n );\n }\n }\n validateCapabilities(path, environment.capabilities, capabilityIds, diagnostics);\n\n if (\n environment.consumer === \"client\" &&\n (environment.runtime === \"rust\" ||\n environment.runtime === \"go\" ||\n environment.runtime === \"jvm\")\n ) {\n diagnostics.push(\n warning(\n \"VIZE_MARQUETTE_010\",\n path,\n \"client environment uses a server-oriented runtime; declare an adapter capability if this is intentional\",\n ),\n );\n }\n }\n\n validateEnvironmentCycles(environments, diagnostics);\n\n for (const backend of backends) {\n const path = contractPath(\"backends\", backend.id);\n if (backend.environment != null) {\n const environment = environments.find((candidate) => candidate.id === backend.environment);\n if (environment == null) {\n diagnostics.push(error(\"VIZE_MARQUETTE_011\", path, \"backend environment does not exist\"));\n } else if (environment.consumer !== \"server\") {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_012\", path, \"backend environment must be a server consumer\"),\n );\n }\n }\n validateCapabilities(path, backend.capabilities, capabilityIds, diagnostics);\n }\n\n for (const protocol of protocols) {\n const path = contractPath(\"protocols\", protocol.id);\n if (!backendIds.has(protocol.backend)) {\n diagnostics.push(error(\"VIZE_MARQUETTE_013\", path, \"protocol backend does not exist\"));\n }\n validateCapabilities(path, protocol.capabilities, capabilityIds, diagnostics);\n }\n\n const routePaths = new Map<string, string>();\n for (const route of routes) {\n const path = contractPath(\"routes\", route.id);\n if (!route.path.startsWith(\"/\")) {\n diagnostics.push(error(\"VIZE_MARQUETTE_014\", path, \"route path must start with /\"));\n }\n const environment = environments.find((candidate) => candidate.id === route.environment);\n if (environment == null) {\n diagnostics.push(error(\"VIZE_MARQUETTE_015\", path, \"route environment does not exist\"));\n }\n if (route.backend != null && !backendIds.has(route.backend)) {\n diagnostics.push(error(\"VIZE_MARQUETTE_016\", path, \"route backend does not exist\"));\n }\n if (route.protocol != null) {\n const protocol = protocols.find((candidate) => candidate.id === route.protocol);\n if (!protocolIds.has(route.protocol) || protocol == null) {\n diagnostics.push(error(\"VIZE_MARQUETTE_017\", path, \"route protocol does not exist\"));\n } else if (route.backend != null && protocol.backend !== route.backend) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_018\",\n path,\n \"route protocol and backend must refer to the same service\",\n ),\n );\n }\n }\n if (environment != null && !renderingMatchesTarget(route, environment.target)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_023\",\n path,\n \"rendering mode is not compatible with the route environment target\",\n ),\n );\n }\n validateCapabilities(path, route.capabilities, capabilityIds, diagnostics);\n\n const routeKey = `${route.environment}\\u0000${route.path}`;\n const previous = routePaths.get(routeKey);\n if (previous != null) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_019\", path, `route path is already used by route \"${previous}\"`),\n );\n }\n routePaths.set(routeKey, route.id);\n }\n\n for (const target of targets) {\n if (!environments.some((environment) => environment.target === target)) {\n diagnostics.push(\n warning(\"VIZE_MARQUETTE_020\", \"targets\", \"declared target has no environment\"),\n );\n }\n }\n\n return diagnostics.sort(compareDiagnostics);\n}\n\nfunction collectUniqueIds(\n collection: string,\n values: readonly { readonly id: string }[],\n diagnostics: MarquetteDiagnostic[],\n): Set<string> {\n const ids = new Set<string>();\n for (const value of values) {\n const path = contractPath(collection, value.id);\n validateIdentifier(value.id, path, \"VIZE_MARQUETTE_006\", diagnostics);\n if (ids.has(value.id)) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_006\", path, \"identifier must be unique within its collection\"),\n );\n }\n ids.add(value.id);\n }\n return ids;\n}\n\nfunction validateIdentifier(\n id: string,\n path: string,\n code: `VIZE_MARQUETTE_${string}`,\n diagnostics: MarquetteDiagnostic[],\n): void {\n if (!IDENTIFIER.test(id)) {\n diagnostics.push(\n error(\n code,\n path,\n \"identifier must use lowercase ASCII letters, digits, dash, underscore, or dot\",\n ),\n );\n }\n}\n\nfunction validateCapabilities(\n path: string,\n capabilities: readonly string[] | undefined,\n declared: ReadonlySet<string>,\n diagnostics: MarquetteDiagnostic[],\n): void {\n for (const capability of [...new Set(capabilities ?? [])].sort()) {\n if (!declared.has(capability)) {\n diagnostics.push(error(\"VIZE_MARQUETTE_021\", path, \"referenced capability is not declared\"));\n }\n }\n}\n\nfunction validateEnvironmentCycles(\n environments: readonly MarquetteEnvironment[],\n diagnostics: MarquetteDiagnostic[],\n): void {\n const graph = new Map(environments.map((value) => [value.id, value.dependsOn ?? []]));\n const visiting = new Set<string>();\n const visited = new Set<string>();\n\n for (const id of [...graph.keys()].sort()) {\n if (hasCycle(id, graph, visiting, visited)) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_022\",\n contractPath(\"environments\", id),\n \"environment dependency graph must be acyclic\",\n ),\n );\n }\n }\n}\n\nfunction hasCycle(\n id: string,\n graph: ReadonlyMap<string, readonly string[]>,\n visiting: Set<string>,\n visited: Set<string>,\n): boolean {\n if (visited.has(id)) return false;\n if (visiting.has(id)) return true;\n visiting.add(id);\n const cyclic = [...(graph.get(id) ?? [])]\n .sort()\n .some((dependency) => graph.has(dependency) && hasCycle(dependency, graph, visiting, visited));\n visiting.delete(id);\n visited.add(id);\n return cyclic;\n}\n\nfunction renderingMatchesTarget(route: MarquetteRoute, target: MarquetteTarget): boolean {\n const targetRendering: Partial<Record<RenderingMode, MarquetteTarget>> = {\n native: \"native\",\n desktop: \"desktop\",\n terminal: \"terminal\",\n };\n return (targetRendering[route.rendering] ?? \"web\") === target;\n}\n\nfunction compareDiagnostics(left: MarquetteDiagnostic, right: MarquetteDiagnostic): number {\n return (\n compareText(left.path, right.path) ||\n compareText(left.code, right.code) ||\n compareText(left.message, right.message)\n );\n}\n\nfunction compareText(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction contractPath(collection: string, id: string): string {\n return `${collection}.${id}`;\n}\n\nfunction error(\n code: `VIZE_MARQUETTE_${string}`,\n path: string,\n message: string,\n): MarquetteDiagnostic {\n return { code, severity: \"error\", path, message };\n}\n\nfunction warning(\n code: `VIZE_MARQUETTE_${string}`,\n path: string,\n message: string,\n): MarquetteDiagnostic {\n return { code, severity: \"warning\", path, message };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizejs/marquette",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.351.0",
|
|
4
4
|
"description": "Typed application marquettes for every Vize target",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"application-contract",
|
|
@@ -84,9 +84,9 @@
|
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@types/node": "25.9.2",
|
|
86
86
|
"@typescript/native-preview": "7.0.0-dev.20260603.1",
|
|
87
|
-
"tsx": "4.
|
|
87
|
+
"tsx": "4.23.12",
|
|
88
88
|
"typescript": "6.0.3",
|
|
89
|
-
"vite-plus": "0.1.
|
|
89
|
+
"vite-plus": "0.1.24"
|
|
90
90
|
},
|
|
91
91
|
"engines": {
|
|
92
92
|
"node": ">=22"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"model-DWcxWbC1.d.mts","names":[],"sources":["../src/model.ts"],"mappings":";;cACa,wBAAA;;KAGD,eAAA;;KAGA,aAAA;AAHZ;AAAA,KAeY,mBAAA;;KAGA,aAAA;;KAGA,cAAA;;KAGA,aAAA;;UAYK,oBAAA;EArBL;EAAA,SAuBD,EAAA;;WAEA,WAAA;EAzBoB;AAG/B;;;;EAH+B,SA+BpB,OAAA;AAAA;;UAIM,oBAAA;EA7BS;EAAA,SAkCf,EAAA,EAAI,EAAA;EA/BH;EAAA,SAiCD,MAAA,EAAQ,eAAA;;WAER,QAAA,EAAU,mBAAA;EAnCI;EAAA,SAqCd,OAAA,EAAS,aAAA;EAzBiB;;;;;EAAA,SA+B1B,KAAA;EArBO;;AAIlB;;;EAJkB,SA2BP,SAAA,YAAqB,YAAA;EAhBb;;;;;EAAA,SAsBR,YAAA;AAAA;;UAIM,gBAAA;EA5BF;EAAA,SAiCJ,EAAA,EAAI,EAAA;EA/BI;EAAA,SAiCR,MAAA,EAAQ,aAAA;EA/BE;;;;;EAAA,SAqCV,WAAA,GAAc,aAAA;EAjBd;;;AAIX;;EAJW,SAuBA,YAAA;AAAA;;UAIM,iBAAA;EAVqB;EAAA,SAY3B,EAAA,EAAI,EAAA;EAxBb;EAAA,SA0BS,MAAA,EAAQ,cAAA;EAtBR;EAAA,SAwBA,OAAA,EAAS,SAAA;EAtBT;;;;;EAAA,SA4BA,YAAA;AAAA;AAZX;AAAA,UAgBiB,cAAA;EAhBiB;EAAA,SAuBvB,EAAA,EAAI,EAAA;EAnBI;EAAA,SAqBR,IAAA;EAnBkB;EAAA,SAqBlB,WAAA,EAAa,aAAA;EA3BW;EAAA,SA6BxB,SAAA,EAAW,aAAA;EA3BX;;;;;EAAA,SAiCA,OAAA,GAAU,SAAA;EAvBV;;;AAIX;;EAJW,SA6BA,QAAA,GAAW,UAAA;EAlBP;;;;;EAAA,SAwBJ,YAAA;AAAA;;UAIM,oBAAA,+BACe,oBAAA,cAAkC,oBAAA,8BACtC,gBAAA,cAA8B,gBAAA,+BAC7B,iBAAA,cAA+B,iBAAA,4BAClC,cAAA,cAA4B,cAAA;EApCpD;;;;;EAAA,SA2CS,aAAA,UAAuB,wBAAA;EAnCV;EAAA,SAqCb,WAAA;EAnCW;;;;;EAAA,SAyCX,OAAA,YAAmB,eAAA;EAvBP;;AAIvB;;;EAJuB,SA6BZ,YAAA,GAAe,QAAA,CAAS,MAAA,SAAe,oBAAA;EAxBgB;;;;;EAAA,SA8BvD,YAAA,GAAe,YAAA;EA3B4B;;;;;EAAA,SAiC3C,QAAA,GAAW,QAAA;EANI;;;;;EAAA,SAYf,SAAA,GAAY,SAAA;EA1CrB;;;;;EAAA,SAgDS,MAAA,GAAS,MAAA;AAAA;;KAIf,UAAA,+BAAyC,WAAA,IAAe,SAAA,4BAC1C,UAAA;EAAA,SAAiC,EAAA;AAAA,QAEhD,EAAA;;KAIQ,aAAA,mBAAgC,oBAAA,IAAwB,UAAA,CAClE,SAAA;;KAKU,mBAAA,mBAAsC,oBAAA,IAAwB,SAAA;EAAA,SAC/D,YAAA;AAAA,IAEP,WAAA;EAAA,SACW,QAAA;EAAA,SACA,EAAA;AAAA,IAET,EAAA;;KAKM,SAAA,mBAA4B,oBAAA,IAAwB,UAAA,CAAW,SAAA;;KAG/D,UAAA,mBAA6B,oBAAA,IAAwB,UAAA,CAAW,SAAA;;KAGhE,OAAA,mBAA0B,oBAAA,IAAwB,UAAA,CAAW,SAAA;;KAG7D,QAAA,mBAA2B,oBAAA,IAAwB,SAAA;EAAA,SACpD,OAAA,iCAAwC,eAAA;AAAA,IAE/C,MAAA;;KAIQ,YAAA,mBAA+B,oBAAA,IAAwB,SAAA;EAAA,SACxD,YAAA,EAAc,QAAA,CAAS,MAAA,0BAAgC,oBAAA;AAAA,IAE9D,EAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-canonical-CB-KzZxk.d.mts","names":[],"sources":["../src/test-run-canonical.ts"],"mappings":";;;;cAQa,yBAAA;AAAb;;;;;AAwCA;;;;AAxCA,iBAwCgB,oBAAA,CAAqB,QAAA,EAAU,eAAA;AA6D/C;;;;;;;AAAA,iBAAsB,kBAAA,CAAmB,QAAA,EAAU,eAAA,GAAkB,OAAA;;iBAW/C,kBAAA,CAAmB,QAAA,EAAU,eAAA,GAAkB,OAAA;;;;;;;iBAUrD,uBAAA,CAAwB,EAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-model-DkllmEsY.d.mts","names":[],"sources":["../src/test-run-model.ts"],"mappings":";;cACa,wBAAA;;cAGA,gCAAA;;KAGD,gBAAA;AAHZ;AAAA,KAMY,iBAAA;;KAGA,gBAAA;;KAcA,mBAAA;;KAGA,0BAAA;;;AApBZ;;;;UA4BiB,uBAAA;EAzBL;EAAA,SA2BD,SAAA;;WAEA,WAAA;AAAA;AAfX;AAAA,UAmBiB,eAAA;;WAEN,EAAA;EArBoB;EAAA,SAuBpB,WAAA;EApB2B;EAAA,SAsB3B,SAAA;AAAA;;UAIM,aAAA;EAlBuB;EAAA,SAoB7B,QAAA;EAlBA;EAAA,SAoBA,sBAAA,EAAwB,uBAAA;EAdlB;EAAA,SAgBN,SAAA,EAAW,gBAAA;;WAEX,qBAAA;EAhBA;EAAA,SAkBA,mBAAA,EAAqB,uBAAA;EAdrB;EAAA,SAgBA,sBAAA;AAAA;AAZX;;;;;;AAAA,UAqBiB,gBAAA;EAXsC;EAAA,SAgB5C,SAAA,WAAoB,QAAA;EAtBpB;EAAA,SAwBA,QAAA,WAAmB,OAAA;AAAA;;UAIb,sBAAA;EAtBN;EAAA,SAwBA,EAAA,EAAI,EAAA;EAtBJ;EAAA,SAwBA,IAAA,EAAM,iBAAA;EAxBgB;EAAA,SA0BtB,WAAA;AAAA;;UAIM,qBAAA;EApBf;EAAA,SAyBS,EAAA,EAAI,EAAA;EArBJ;EAAA,SAuBA,QAAA,EAAU,QAAA;EArBV;EAAA,SAuBA,IAAA,EAAM,gBAAA;EAvBoB;EAAA,SAyB1B,UAAA;EArBM;EAAA,SAuBN,UAAA;EAvB4B;EAAA,SAyB5B,OAAA,EAAS,mBAAA;EAzBoB;EAAA,SA2B7B,MAAA;EAzBI;EAAA,SA2BJ,MAAA;EAzBM;EAAA,SA2BN,OAAA;EAzBW;EAAA,SA2BX,OAAA;EAvBM;EAAA,SAyBN,UAAA;EAzB2B;EAAA,SA2B3B,qBAAA;EApBU;EAAA,SAsBV,MAAA,EAAQ,uBAAA;EAdC;EAAA,SAgBT,GAAA,EAAK,uBAAA;AAAA;;UAIC,mBAAA;EAlCf;EAAA,SAoCS,QAAA;EAhCA;EAAA,SAkCA,WAAA;EAhCA;EAAA,SAkCA,OAAA,EAAS,0BAAA;EAhCT;EAAA,SAkCA,WAAA;EAhCA;EAAA,SAkCA,UAAA;EA9BA;EAAA,SAgCA,MAAA;EA9BA;EAAA,SAgCA,MAAA;EA5BA;EAAA,SA8BA,OAAA;EA1BA;EAAA,SA4BA,OAAA;EAxBA;EAAA,SA0BA,QAAA,EAAU,uBAAA;AAAA;;;;AApBrB;;;;;UA+BiB,eAAA;EAzBN;EAAA,SA2BA,MAAA,SAAe,wBAAA;EAzBf;;;;;EAAA,SA+BA,aAAA,UAAuB,gCAAA;EAnBvB;EAAA,SAqBA,EAAA;EArBiC;EAAA,SAuBjC,WAAA;EAZM;EAAA,SAcN,WAAA;;WAEA,mBAAA;EARuB;EAAA,SAUvB,cAAA;EAYQ;EAAA,SAVR,OAAA;EAckB;EAAA,SAZlB,QAAA,EAAU,eAAA;EAgBI;EAAA,SAdd,SAAA;EAciC;EAAA,SAZjC,WAAA;EAxBe;EAAA,SA0Bf,UAAA;EApBuB;EAAA,SAsBvB,MAAA,EAAQ,aAAA;EAlBR;EAAA,SAoBA,SAAA,EAAW,gBAAA;EAhBX;EAAA,SAkBA,OAAA,WAAkB,sBAAA;EAdlB;EAAA,SAgBA,MAAA,WAAiB,qBAAA;EAdP;EAAA,SAgBV,YAAA,EAAc,mBAAA;AAAA;;KAIb,eAAA,kBAAiC,eAAA,IAAmB,QAAA;;KAGpD,cAAA,kBAAgC,eAAA,IAAmB,QAAA"}
|