@dogfood-lab/ingest 1.2.2 → 1.2.3

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.
@@ -1,83 +1,83 @@
1
- /**
2
- * Persisted-record schema validator.
3
- *
4
- * Enforces dogfood-record.schema.json at write time. The submission validator
5
- * in @dogfood-lab/verify covers the inbound payload; this is the symmetric
6
- * gate on the outbound payload — the central verifier assembles the record
7
- * and the persist layer must not write anything that violates the contract.
8
- *
9
- * Mirrors the Ajv idiom in @dogfood-lab/verify/validators/schema.js — same
10
- * Ajv2020 + ajv-formats setup, lazy compile, cached compiled validator.
11
- * Kept in this package (not extracted to a shared util) because the two
12
- * call sites have different error shapes and lifecycles: submission validation
13
- * returns { valid, errors } so the verifier can build a rejection record;
14
- * record validation throws because a malformed record reaching the write
15
- * path is a programming error, not user input.
16
- */
17
-
18
- import Ajv2020 from 'ajv/dist/2020.js';
19
- import addFormats from 'ajv-formats';
20
- import { readFileSync } from 'node:fs';
21
- import { createRequire } from 'node:module';
22
-
23
- const require = createRequire(import.meta.url);
24
- const SCHEMA_PATH = require.resolve('@dogfood-lab/schemas/json/dogfood-record.schema.json');
25
-
26
- let _validator = null;
27
- let _loadError = null;
28
-
29
- function getValidator() {
30
- if (_validator) return _validator;
31
- if (_loadError) throw _loadError;
32
-
33
- try {
34
- const ajv = new Ajv2020({ allErrors: true, strict: false });
35
- addFormats(ajv);
36
- const schema = JSON.parse(readFileSync(SCHEMA_PATH, 'utf-8'));
37
- _validator = ajv.compile(schema);
38
- return _validator;
39
- } catch (e) {
40
- _loadError = new Error(`record schema load failed: ${e.message}`);
41
- throw _loadError;
42
- }
43
- }
44
-
45
- /**
46
- * Structured error thrown when a persisted record violates the schema.
47
- * Caller code can `instanceof RecordValidationError` to distinguish from
48
- * IO/path errors.
49
- */
50
- export class RecordValidationError extends Error {
51
- constructor(errors) {
52
- const summary = errors
53
- .map(e => `${e.path || '/'} ${e.message}`)
54
- .join('; ');
55
- super(`persisted record failed schema validation: ${summary}`);
56
- this.name = 'RecordValidationError';
57
- this.code = 'RECORD_SCHEMA_INVALID';
58
- this.errors = errors;
59
- }
60
- }
61
-
62
- /**
63
- * Validate a persisted record against dogfood-record.schema.json.
64
- * Throws RecordValidationError on failure; returns the record on success
65
- * so callers can chain (`writeFile(validateRecord(r))`).
66
- *
67
- * @param {object} record
68
- * @returns {object} the same record reference, unchanged
69
- * @throws {RecordValidationError}
70
- */
71
- export function validateRecord(record) {
72
- const validate = getValidator();
73
- const valid = validate(record);
74
- if (valid) return record;
75
-
76
- const errors = (validate.errors || []).map(err => ({
77
- path: err.instancePath || '/',
78
- keyword: err.keyword,
79
- message: err.message,
80
- params: err.params
81
- }));
82
- throw new RecordValidationError(errors);
83
- }
1
+ /**
2
+ * Persisted-record schema validator.
3
+ *
4
+ * Enforces dogfood-record.schema.json at write time. The submission validator
5
+ * in @dogfood-lab/verify covers the inbound payload; this is the symmetric
6
+ * gate on the outbound payload — the central verifier assembles the record
7
+ * and the persist layer must not write anything that violates the contract.
8
+ *
9
+ * Mirrors the Ajv idiom in @dogfood-lab/verify/validators/schema.js — same
10
+ * Ajv2020 + ajv-formats setup, lazy compile, cached compiled validator.
11
+ * Kept in this package (not extracted to a shared util) because the two
12
+ * call sites have different error shapes and lifecycles: submission validation
13
+ * returns { valid, errors } so the verifier can build a rejection record;
14
+ * record validation throws because a malformed record reaching the write
15
+ * path is a programming error, not user input.
16
+ */
17
+
18
+ import Ajv2020 from 'ajv/dist/2020.js';
19
+ import addFormats from 'ajv-formats';
20
+ import { readFileSync } from 'node:fs';
21
+ import { createRequire } from 'node:module';
22
+
23
+ const require = createRequire(import.meta.url);
24
+ const SCHEMA_PATH = require.resolve('@dogfood-lab/schemas/json/dogfood-record.schema.json');
25
+
26
+ let _validator = null;
27
+ let _loadError = null;
28
+
29
+ function getValidator() {
30
+ if (_validator) return _validator;
31
+ if (_loadError) throw _loadError;
32
+
33
+ try {
34
+ const ajv = new Ajv2020({ allErrors: true, strict: false });
35
+ addFormats(ajv);
36
+ const schema = JSON.parse(readFileSync(SCHEMA_PATH, 'utf-8'));
37
+ _validator = ajv.compile(schema);
38
+ return _validator;
39
+ } catch (e) {
40
+ _loadError = new Error(`record schema load failed: ${e.message}`);
41
+ throw _loadError;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Structured error thrown when a persisted record violates the schema.
47
+ * Caller code can `instanceof RecordValidationError` to distinguish from
48
+ * IO/path errors.
49
+ */
50
+ export class RecordValidationError extends Error {
51
+ constructor(errors) {
52
+ const summary = errors
53
+ .map(e => `${e.path || '/'} ${e.message}`)
54
+ .join('; ');
55
+ super(`persisted record failed schema validation: ${summary}`);
56
+ this.name = 'RecordValidationError';
57
+ this.code = 'RECORD_SCHEMA_INVALID';
58
+ this.errors = errors;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Validate a persisted record against dogfood-record.schema.json.
64
+ * Throws RecordValidationError on failure; returns the record on success
65
+ * so callers can chain (`writeFile(validateRecord(r))`).
66
+ *
67
+ * @param {object} record
68
+ * @returns {object} the same record reference, unchanged
69
+ * @throws {RecordValidationError}
70
+ */
71
+ export function validateRecord(record) {
72
+ const validate = getValidator();
73
+ const valid = validate(record);
74
+ if (valid) return record;
75
+
76
+ const errors = (validate.errors || []).map(err => ({
77
+ path: err.instancePath || '/',
78
+ keyword: err.keyword,
79
+ message: err.message,
80
+ params: err.params
81
+ }));
82
+ throw new RecordValidationError(errors);
83
+ }