@vibe-engineer/artifacts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,8 @@
1
+ # @vibe-engineer/artifacts
2
+
3
+ Package-owned implementation for DL-02 canonical artifact contracts.
4
+
5
+ - Canonical carriers are UTF-8 JSON files validated against committed JSON Schema 2020-12 schemas in `schemas/*.schema.json`.
6
+ - Runtime APIs treat boundary data as `unknown` and fail closed with typed validation errors.
7
+ - Generated TypeScript declarations in `src/generated/types.d.ts` are derived from the committed schemas by `scripts/generate-types.mjs`; `scripts/check-generated-types.mjs` is the drift witness.
8
+ - Markdown, YAML, frontmatter, chat text, and TypeScript object literals are not accepted as canonical carriers.
@@ -0,0 +1,52 @@
1
+ // src/schema-registry.js
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ var SUPPORTED_SCHEMA_VERSION = "1.0.0";
6
+ var ARTIFACT_KINDS = [
7
+ "work_brief",
8
+ "implementation_plan",
9
+ "verification_delta",
10
+ "build_result",
11
+ "ship_packet",
12
+ "evidence_packet",
13
+ "agent_registry_entry",
14
+ "context_file_header",
15
+ "schematic_manifest",
16
+ "skill_manifest"
17
+ ];
18
+ var SCHEMA_FILES = {
19
+ "work_brief": "schemas/work-brief.schema.json",
20
+ "implementation_plan": "schemas/implementation-plan.schema.json",
21
+ "verification_delta": "schemas/verification-delta.schema.json",
22
+ "build_result": "schemas/build-result.schema.json",
23
+ "ship_packet": "schemas/ship-packet.schema.json",
24
+ "evidence_packet": "schemas/evidence-packet.schema.json",
25
+ "agent_registry_entry": "schemas/agent-registry-entry.schema.json",
26
+ "context_file_header": "schemas/context-file-header.schema.json",
27
+ "schematic_manifest": "schemas/schematic-manifest.schema.json",
28
+ "skill_manifest": "schemas/skill-manifest.schema.json"
29
+ };
30
+ var packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
31
+ function schemaPathForKind(kind) {
32
+ const relative = SCHEMA_FILES[kind];
33
+ if (!relative) return void 0;
34
+ return path.join(packageRoot, relative);
35
+ }
36
+ function loadSchema(kind) {
37
+ const schemaPath = schemaPathForKind(kind);
38
+ if (!schemaPath) return void 0;
39
+ return JSON.parse(fs.readFileSync(schemaPath, "utf8"));
40
+ }
41
+ function loadAllSchemas() {
42
+ return Object.fromEntries(ARTIFACT_KINDS.map((kind) => [kind, loadSchema(kind)]));
43
+ }
44
+
45
+ export {
46
+ SUPPORTED_SCHEMA_VERSION,
47
+ ARTIFACT_KINDS,
48
+ SCHEMA_FILES,
49
+ schemaPathForKind,
50
+ loadSchema,
51
+ loadAllSchemas
52
+ };
package/dist/index.js ADDED
@@ -0,0 +1,389 @@
1
+ import {
2
+ ARTIFACT_KINDS,
3
+ SCHEMA_FILES,
4
+ SUPPORTED_SCHEMA_VERSION,
5
+ loadAllSchemas,
6
+ loadSchema,
7
+ schemaPathForKind
8
+ } from "./chunk-XQLK7EDB.js";
9
+
10
+ // src/errors.js
11
+ var ValidationErrorCode = Object.freeze({
12
+ CARRIER_NOT_JSON: "CARRIER_NOT_JSON",
13
+ JSON_PARSE_ERROR: "JSON_PARSE_ERROR",
14
+ NOT_OBJECT: "NOT_OBJECT",
15
+ REQUIRED: "REQUIRED",
16
+ UNKNOWN_FIELD: "UNKNOWN_FIELD",
17
+ TYPE: "TYPE",
18
+ CONST: "CONST",
19
+ ENUM: "ENUM",
20
+ PATTERN: "PATTERN",
21
+ MIN_LENGTH: "MIN_LENGTH",
22
+ MIN_ITEMS: "MIN_ITEMS",
23
+ MIN_PROPERTIES: "MIN_PROPERTIES",
24
+ PROPERTY_NAME: "PROPERTY_NAME",
25
+ SCHEMA_VALIDATION: "SCHEMA_VALIDATION",
26
+ UNSUPPORTED_VERSION: "UNSUPPORTED_VERSION",
27
+ ARTIFACT_KIND_MISMATCH: "ARTIFACT_KIND_MISMATCH",
28
+ BAD_LINK: "BAD_LINK",
29
+ BAD_STATUS_HANDOFF: "BAD_STATUS_HANDOFF",
30
+ EVIDENCE_FOR_REQUIRED: "EVIDENCE_FOR_REQUIRED",
31
+ EVIDENCE_LINK_REQUIRED: "EVIDENCE_LINK_REQUIRED",
32
+ VERIFICATION_CATALOG_INCOMPLETE: "VERIFICATION_CATALOG_INCOMPLETE",
33
+ BLOCKED_ITEM_METADATA_REQUIRED: "BLOCKED_ITEM_METADATA_REQUIRED",
34
+ NOT_APPLICABLE_RATIONALE_REQUIRED: "NOT_APPLICABLE_RATIONALE_REQUIRED",
35
+ MALFORMED_EXTENSION: "MALFORMED_EXTENSION",
36
+ SCHEMA_INTERNAL_ERROR: "SCHEMA_INTERNAL_ERROR"
37
+ });
38
+ function makeValidationError({ artifactPath, artifactKind, schemaId, schemaVersion, pointer, code, message }) {
39
+ return {
40
+ artifactPath: artifactPath ?? null,
41
+ artifactKind: artifactKind ?? null,
42
+ schemaId: schemaId ?? null,
43
+ schemaVersion: schemaVersion ?? null,
44
+ pointer: pointer || "",
45
+ code,
46
+ message
47
+ };
48
+ }
49
+
50
+ // src/validation.js
51
+ import fs from "fs";
52
+ import path from "path";
53
+ import Ajv2020 from "ajv/dist/2020.js";
54
+ var CORE_FIELDS = /* @__PURE__ */ new Set(["schemaVersion", "artifactKind", "artifactId", "title", "createdAt", "updatedAt", "producer", "status", "ownership", "links", "extensions", "description", "tags", "sourceRefs", "approvedBy", "supersessionReason", "retention"]);
55
+ var VERIFICATION_CATALOG_LAYERS = Object.freeze([
56
+ "safety_hooks",
57
+ "typecheck",
58
+ "lint_format",
59
+ "mechanical_gate",
60
+ "unit",
61
+ "integration",
62
+ "contract_adapter",
63
+ "e2e",
64
+ "ui_verification",
65
+ "ai_eval",
66
+ "build_package",
67
+ "context_drift",
68
+ "observability",
69
+ "advisory_review",
70
+ "final_dod",
71
+ "schema_validation"
72
+ ]);
73
+ function pointerJoin(base, token) {
74
+ const escaped = String(token).replaceAll("~", "~0").replaceAll("/", "~1");
75
+ return `${base}/${escaped}`;
76
+ }
77
+ function isObject(value) {
78
+ return typeof value === "object" && value !== null && !Array.isArray(value);
79
+ }
80
+ function addError(errors, ctx, pointer, code, message) {
81
+ errors.push(makeValidationError({ ...ctx, pointer, code, message }));
82
+ }
83
+ function createContext(data, artifactPath, schema) {
84
+ return {
85
+ artifactPath: artifactPath ?? null,
86
+ artifactKind: isObject(data) ? data.artifactKind ?? null : null,
87
+ schemaVersion: isObject(data) ? data.schemaVersion ?? null : null,
88
+ schemaId: schema?.$id ?? null
89
+ };
90
+ }
91
+ function ajvErrorPointer(error) {
92
+ if (error.keyword === "required" && error.params?.missingProperty) return pointerJoin(error.instancePath || "", error.params.missingProperty);
93
+ if (error.keyword === "additionalProperties" && error.params?.additionalProperty) return pointerJoin(error.instancePath || "", error.params.additionalProperty);
94
+ if (error.keyword === "propertyNames" && error.params?.propertyName) return pointerJoin(error.instancePath || "", error.params.propertyName);
95
+ return error.instancePath || "";
96
+ }
97
+ function ajvErrorCode(error) {
98
+ switch (error.keyword) {
99
+ case "required":
100
+ return ValidationErrorCode.REQUIRED;
101
+ case "additionalProperties":
102
+ return ValidationErrorCode.UNKNOWN_FIELD;
103
+ case "type":
104
+ return ValidationErrorCode.TYPE;
105
+ case "const":
106
+ return ValidationErrorCode.CONST;
107
+ case "enum":
108
+ return ValidationErrorCode.ENUM;
109
+ case "pattern":
110
+ return ValidationErrorCode.PATTERN;
111
+ case "minLength":
112
+ return ValidationErrorCode.MIN_LENGTH;
113
+ case "minItems":
114
+ return ValidationErrorCode.MIN_ITEMS;
115
+ case "minProperties":
116
+ return ValidationErrorCode.MIN_PROPERTIES;
117
+ case "propertyNames":
118
+ return ValidationErrorCode.PROPERTY_NAME;
119
+ default:
120
+ return ValidationErrorCode.SCHEMA_VALIDATION;
121
+ }
122
+ }
123
+ function normalizeAjvErrors(ajvErrors, ctx) {
124
+ return (ajvErrors ?? []).map((error) => makeValidationError({
125
+ ...ctx,
126
+ pointer: ajvErrorPointer(error),
127
+ code: ajvErrorCode(error),
128
+ message: error.message ?? `Schema validation failed for keyword ${error.keyword}.`
129
+ }));
130
+ }
131
+ function createAjv() {
132
+ return new Ajv2020({
133
+ strict: true,
134
+ allErrors: true,
135
+ validateSchema: true,
136
+ messages: true
137
+ });
138
+ }
139
+ function compileSchemaRegistry() {
140
+ const ajv = createAjv();
141
+ const schemas = loadAllSchemas();
142
+ const compiled = /* @__PURE__ */ new Map();
143
+ for (const kind of ARTIFACT_KINDS) {
144
+ const schema = schemas[kind];
145
+ if (!schema || schema.$schema !== "https://json-schema.org/draft/2020-12/schema") {
146
+ throw new Error(`Schema ${kind} must be committed as JSON Schema 2020-12.`);
147
+ }
148
+ compiled.set(kind, ajv.compile(schema));
149
+ }
150
+ return { ajv, compiled };
151
+ }
152
+ var compiledRegistry = compileSchemaRegistry();
153
+ function compileAllArtifactSchemas() {
154
+ return Object.freeze({
155
+ schemaVersion: SUPPORTED_SCHEMA_VERSION,
156
+ kinds: Object.freeze([...compiledRegistry.compiled.keys()])
157
+ });
158
+ }
159
+ function validateVersionAndKind(data, expectedKind, errors, ctx) {
160
+ if (!isObject(data)) {
161
+ addError(errors, ctx, "", ValidationErrorCode.NOT_OBJECT, "Artifact must be a JSON object.");
162
+ return;
163
+ }
164
+ if (typeof data.schemaVersion !== "string" || !/^\d+\.\d+\.\d+$/.test(data.schemaVersion)) {
165
+ addError(errors, ctx, "/schemaVersion", ValidationErrorCode.UNSUPPORTED_VERSION, "schemaVersion must be semantic version 1.0.0.");
166
+ } else if (data.schemaVersion !== SUPPORTED_SCHEMA_VERSION) {
167
+ addError(errors, ctx, "/schemaVersion", ValidationErrorCode.UNSUPPORTED_VERSION, `Unsupported artifact schemaVersion ${data.schemaVersion}.`);
168
+ }
169
+ if (!ARTIFACT_KINDS.includes(data.artifactKind)) {
170
+ addError(errors, ctx, "/artifactKind", ValidationErrorCode.ARTIFACT_KIND_MISMATCH, "Unknown artifactKind.");
171
+ }
172
+ if (expectedKind && data.artifactKind !== expectedKind) {
173
+ addError(errors, ctx, "/artifactKind", ValidationErrorCode.ARTIFACT_KIND_MISMATCH, `Expected artifactKind ${expectedKind}.`);
174
+ }
175
+ }
176
+ function linkMatches(link, rel, artifactKind, requiredStatus) {
177
+ return isObject(link) && link.rel === rel && link.artifactKind === artifactKind && (!requiredStatus || link.statusAtLinkTime === requiredStatus) && link.required === true;
178
+ }
179
+ function evidencePacketLinkMatches(link) {
180
+ return linkMatches(link, "evidence_for", "evidence_packet");
181
+ }
182
+ function requireLink(errors, ctx, pointer, link, rel, artifactKind, requiredStatus) {
183
+ if (!linkMatches(link, rel, artifactKind, requiredStatus)) {
184
+ const statusText = requiredStatus ? ` with statusAtLinkTime ${requiredStatus}` : "";
185
+ addError(errors, ctx, pointer, ValidationErrorCode.BAD_LINK, `Required ${rel} link to ${artifactKind}${statusText} is missing or malformed.`);
186
+ }
187
+ }
188
+ function requireEvidencePacketRef(errors, ctx, pointer, link) {
189
+ if (!evidencePacketLinkMatches(link)) {
190
+ addError(errors, ctx, pointer, ValidationErrorCode.EVIDENCE_LINK_REQUIRED, "Verification evidence references must be required evidence_for links to evidence_packet artifacts.");
191
+ }
192
+ }
193
+ function validateVerificationCatalog(data, errors, ctx, basePointer = "") {
194
+ if (!Array.isArray(data.requiredItems)) return;
195
+ const seen = /* @__PURE__ */ new Set();
196
+ data.requiredItems.forEach((item, index) => {
197
+ if (!isObject(item)) return;
198
+ if (typeof item.layer === "string") seen.add(item.layer);
199
+ if (item.action === "not_applicable" && (typeof item.rationale !== "string" || item.rationale.trim() === "")) {
200
+ addError(errors, ctx, `${basePointer}/requiredItems/${index}/rationale`, ValidationErrorCode.NOT_APPLICABLE_RATIONALE_REQUIRED, "not_applicable verification catalog items require a rationale.");
201
+ }
202
+ if (item.action === "blocked") {
203
+ if (typeof item.blockedBy !== "string" || item.blockedBy.trim() === "") {
204
+ addError(errors, ctx, `${basePointer}/requiredItems/${index}/blockedBy`, ValidationErrorCode.BLOCKED_ITEM_METADATA_REQUIRED, "blocked verification catalog items require blockedBy.");
205
+ }
206
+ if (typeof item.unblockCondition !== "string" || item.unblockCondition.trim() === "") {
207
+ addError(errors, ctx, `${basePointer}/requiredItems/${index}/unblockCondition`, ValidationErrorCode.BLOCKED_ITEM_METADATA_REQUIRED, "blocked verification catalog items require unblockCondition.");
208
+ }
209
+ }
210
+ });
211
+ for (const layer of VERIFICATION_CATALOG_LAYERS) {
212
+ if (!seen.has(layer)) {
213
+ addError(errors, ctx, `${basePointer}/requiredItems`, ValidationErrorCode.VERIFICATION_CATALOG_INCOMPLETE, `Verification Delta must include catalog layer ${layer}.`);
214
+ }
215
+ }
216
+ }
217
+ function requireEvidenceForLinks(data, errors, ctx) {
218
+ if (!Array.isArray(data.links) || !data.links.some((link) => isObject(link) && link.rel === "evidence_for" && link.required === true)) {
219
+ addError(errors, ctx, "/links", ValidationErrorCode.EVIDENCE_FOR_REQUIRED, "Evidence Packet must include a required top-level evidence_for link.");
220
+ }
221
+ if (!Array.isArray(data.subjectRefs) || data.subjectRefs.length === 0) return;
222
+ data.subjectRefs.forEach((ref, index) => {
223
+ if (!isObject(ref) || ref.rel !== "evidence_for" || ref.required !== true) {
224
+ addError(errors, ctx, `/subjectRefs/${index}`, ValidationErrorCode.EVIDENCE_FOR_REQUIRED, "Evidence Packet subjectRefs must use required evidence_for links.");
225
+ }
226
+ });
227
+ }
228
+ function semanticChecks(kind, data, errors, ctx) {
229
+ if (!isObject(data)) return;
230
+ if (isObject(data.extensions)) {
231
+ for (const [namespace, value] of Object.entries(data.extensions)) {
232
+ if (!isObject(value) || typeof value.schemaVersion !== "string") {
233
+ addError(errors, ctx, `/extensions/${namespace}`, ValidationErrorCode.MALFORMED_EXTENSION, "Extension value must be an object with schemaVersion.");
234
+ continue;
235
+ }
236
+ for (const key of Object.keys(value)) {
237
+ if (key !== "schemaVersion" && CORE_FIELDS.has(key)) {
238
+ addError(errors, ctx, `/extensions/${namespace}/${key}`, ValidationErrorCode.MALFORMED_EXTENSION, `Extension ${namespace} must not redefine core field ${key}.`);
239
+ }
240
+ }
241
+ }
242
+ }
243
+ switch (kind) {
244
+ case "work_brief": {
245
+ if (!Array.isArray(data.links) || !data.links.some((link) => linkMatches(link, "raw_intent", "work_brief"))) {
246
+ addError(errors, ctx, "/links", ValidationErrorCode.BAD_LINK, "Work Brief must include a required raw_intent link.");
247
+ }
248
+ break;
249
+ }
250
+ case "implementation_plan": {
251
+ requireLink(errors, ctx, "/workBriefRef", data.workBriefRef, "derived_from", "work_brief", "ready");
252
+ if (data.status === "approved" && Array.isArray(data.openBlockers) && data.openBlockers.some((b) => b?.blocking === true)) {
253
+ addError(errors, ctx, "/openBlockers", ValidationErrorCode.BAD_STATUS_HANDOFF, "Approved Implementation Plan cannot contain unresolved blocking openBlockers.");
254
+ }
255
+ if (data.verificationDelta?.artifactKind !== "verification_delta") {
256
+ addError(errors, ctx, "/verificationDelta/artifactKind", ValidationErrorCode.BAD_LINK, "Implementation Plan must embed a Verification Delta artifact.");
257
+ } else {
258
+ validateVerificationCatalog(data.verificationDelta, errors, ctx, "/verificationDelta");
259
+ }
260
+ break;
261
+ }
262
+ case "verification_delta": {
263
+ requireLink(errors, ctx, "/implementationPlanRef", data.implementationPlanRef, "verification_delta_of", "implementation_plan");
264
+ if (!Array.isArray(data.requiredItems) || data.requiredItems.length === 0) {
265
+ addError(errors, ctx, "/requiredItems", ValidationErrorCode.REQUIRED, "Verification Delta must include required catalog items.");
266
+ }
267
+ validateVerificationCatalog(data, errors, ctx);
268
+ break;
269
+ }
270
+ case "build_result": {
271
+ requireLink(errors, ctx, "/implementationPlanRef", data.implementationPlanRef, "implements", "implementation_plan", "approved");
272
+ if (!Array.isArray(data.verificationRuns) || data.verificationRuns.length === 0) {
273
+ addError(errors, ctx, "/verificationRuns", ValidationErrorCode.REQUIRED, "Build Result must reference Evidence Packets for verification claims.");
274
+ } else {
275
+ data.verificationRuns.forEach((run, index) => requireEvidencePacketRef(errors, ctx, `/verificationRuns/${index}/evidencePacketRef`, run?.evidencePacketRef));
276
+ }
277
+ if (Array.isArray(data.testsAndVerificationChanged)) {
278
+ data.testsAndVerificationChanged.forEach((item, index) => requireEvidencePacketRef(errors, ctx, `/testsAndVerificationChanged/${index}/evidenceRef`, item?.evidenceRef));
279
+ }
280
+ if (Array.isArray(data.warningsAndBlockers)) {
281
+ data.warningsAndBlockers.forEach((item, index) => requireEvidencePacketRef(errors, ctx, `/warningsAndBlockers/${index}/evidenceRef`, item?.evidenceRef));
282
+ }
283
+ if (data.status === "passed" && Array.isArray(data.warningsAndBlockers) && data.warningsAndBlockers.some((item) => item?.blocking === true && ["critical", "major-local"].includes(item?.severity))) {
284
+ addError(errors, ctx, "/warningsAndBlockers", ValidationErrorCode.BAD_STATUS_HANDOFF, "Passed Build Result cannot contain blocking critical or major-local evidence.");
285
+ }
286
+ break;
287
+ }
288
+ case "ship_packet": {
289
+ requireLink(errors, ctx, "/buildResultRef", data.buildResultRef, "ship_packet_of", "build_result", "passed");
290
+ if (Array.isArray(data.finalVerification)) {
291
+ data.finalVerification.forEach((item, index) => requireEvidencePacketRef(errors, ctx, `/finalVerification/${index}/evidencePacketRef`, item?.evidencePacketRef));
292
+ }
293
+ if (isObject(data.contextPreservation) && Array.isArray(data.contextPreservation.driftCheckEvidenceRefs)) {
294
+ data.contextPreservation.driftCheckEvidenceRefs.forEach((ref, index) => requireEvidencePacketRef(errors, ctx, `/contextPreservation/driftCheckEvidenceRefs/${index}`, ref));
295
+ }
296
+ if (data.noPushWithoutApproval !== true || data.commitPreparation?.commitPerformedByAgent !== false || data.prPreparation?.prOpenedByAgent !== false) {
297
+ addError(errors, ctx, "/noPushWithoutApproval", ValidationErrorCode.BAD_STATUS_HANDOFF, "Ship Packet must preserve no-push/no-PR-without-approval policy.");
298
+ }
299
+ break;
300
+ }
301
+ case "evidence_packet": {
302
+ requireEvidenceForLinks(data, errors, ctx);
303
+ if (["fail", "blocked"].includes(data.result) && !isObject(data.failureDetails)) {
304
+ addError(errors, ctx, "/failureDetails", ValidationErrorCode.REQUIRED, "Failure or blocked evidence requires failureDetails.");
305
+ }
306
+ if (data.evidenceClass === "advisory" && data.blocking === true && data.result !== "advisory") {
307
+ addError(errors, ctx, "/blocking", ValidationErrorCode.BAD_STATUS_HANDOFF, "Advisory evidence cannot masquerade as deterministic blocker.");
308
+ }
309
+ break;
310
+ }
311
+ case "agent_registry_entry": {
312
+ if (!Array.isArray(data.inputSchemas) || data.inputSchemas.length === 0 || !Array.isArray(data.outputSchemas) || data.outputSchemas.length === 0) {
313
+ addError(errors, ctx, "/inputSchemas", ValidationErrorCode.REQUIRED, "Agent registry entries require input and output schema refs.");
314
+ }
315
+ break;
316
+ }
317
+ case "context_file_header": {
318
+ if (!isObject(data.driftMetadata) || !isObject(data.updateMetadata)) {
319
+ addError(errors, ctx, "/driftMetadata", ValidationErrorCode.REQUIRED, "Context headers require update and drift metadata.");
320
+ }
321
+ break;
322
+ }
323
+ case "schematic_manifest": {
324
+ if (!Array.isArray(data.inputs) || data.inputs.length === 0 || !Array.isArray(data.generatedPaths) || data.generatedPaths.length === 0) {
325
+ addError(errors, ctx, "/inputs", ValidationErrorCode.REQUIRED, "Schematic manifests require input schema refs and generated path policy.");
326
+ }
327
+ break;
328
+ }
329
+ case "skill_manifest": {
330
+ if (!Array.isArray(data.outputArtifactSchemas) || data.outputArtifactSchemas.length === 0 || !Array.isArray(data.forbiddenActions) || data.forbiddenActions.length === 0) {
331
+ addError(errors, ctx, "/outputArtifactSchemas", ValidationErrorCode.REQUIRED, "Skill manifests require output schema refs and forbidden action policy.");
332
+ }
333
+ break;
334
+ }
335
+ }
336
+ }
337
+ function validateArtifactKind(kind, data, options = {}) {
338
+ const schema = loadSchema(kind);
339
+ const ctx = createContext(data, options.artifactPath, schema);
340
+ const errors = [];
341
+ const validator = compiledRegistry.compiled.get(kind);
342
+ if (!schema || !validator) {
343
+ addError(errors, ctx, "", ValidationErrorCode.SCHEMA_INTERNAL_ERROR, `No schema registered for kind ${kind}.`);
344
+ return { ok: false, errors };
345
+ }
346
+ validateVersionAndKind(data, kind, errors, ctx);
347
+ const schemaOk = validator(data);
348
+ if (!schemaOk) errors.push(...normalizeAjvErrors(validator.errors, ctx));
349
+ semanticChecks(kind, data, errors, ctx);
350
+ return errors.length === 0 ? { ok: true, artifact: data, kind, schemaId: schema.$id, schemaVersion: SUPPORTED_SCHEMA_VERSION } : { ok: false, errors };
351
+ }
352
+ function validateArtifact(data, options = {}) {
353
+ if (!isObject(data)) {
354
+ const ctx = { artifactPath: options.artifactPath ?? null, artifactKind: null, schemaVersion: null, schemaId: null };
355
+ return { ok: false, errors: [makeValidationError({ ...ctx, pointer: "", code: ValidationErrorCode.NOT_OBJECT, message: "Artifact must be a JSON object." })] };
356
+ }
357
+ const kind = data.artifactKind;
358
+ if (!ARTIFACT_KINDS.includes(kind)) {
359
+ const ctx = createContext(data, options.artifactPath, null);
360
+ return { ok: false, errors: [makeValidationError({ ...ctx, pointer: "/artifactKind", code: ValidationErrorCode.ARTIFACT_KIND_MISMATCH, message: "Unknown artifactKind." })] };
361
+ }
362
+ return validateArtifactKind(kind, data, options);
363
+ }
364
+ function validateArtifactFile(filePath, options = {}) {
365
+ const artifactPath = options.artifactPath ?? filePath;
366
+ if (path.extname(filePath) !== ".json") {
367
+ return { ok: false, errors: [makeValidationError({ artifactPath, artifactKind: options.kind ?? null, schemaId: null, schemaVersion: null, pointer: "", code: ValidationErrorCode.CARRIER_NOT_JSON, message: "Canonical artifact carriers must be UTF-8 JSON files; Markdown, YAML, frontmatter, and chat text are not accepted." })] };
368
+ }
369
+ let parsed;
370
+ try {
371
+ parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
372
+ } catch (error) {
373
+ return { ok: false, errors: [makeValidationError({ artifactPath, artifactKind: options.kind ?? null, schemaId: null, schemaVersion: null, pointer: "", code: ValidationErrorCode.JSON_PARSE_ERROR, message: `Invalid JSON artifact: ${error.message}` })] };
374
+ }
375
+ return options.kind ? validateArtifactKind(options.kind, parsed, { artifactPath }) : validateArtifact(parsed, { artifactPath });
376
+ }
377
+ export {
378
+ ARTIFACT_KINDS,
379
+ SCHEMA_FILES,
380
+ SUPPORTED_SCHEMA_VERSION,
381
+ ValidationErrorCode,
382
+ compileAllArtifactSchemas,
383
+ loadAllSchemas,
384
+ loadSchema,
385
+ schemaPathForKind,
386
+ validateArtifact,
387
+ validateArtifactFile,
388
+ validateArtifactKind
389
+ };
@@ -0,0 +1,16 @@
1
+ import {
2
+ ARTIFACT_KINDS,
3
+ SCHEMA_FILES,
4
+ SUPPORTED_SCHEMA_VERSION,
5
+ loadAllSchemas,
6
+ loadSchema,
7
+ schemaPathForKind
8
+ } from "./chunk-XQLK7EDB.js";
9
+ export {
10
+ ARTIFACT_KINDS,
11
+ SCHEMA_FILES,
12
+ SUPPORTED_SCHEMA_VERSION,
13
+ loadAllSchemas,
14
+ loadSchema,
15
+ schemaPathForKind
16
+ };