@altopelago/aeos-core 0.9.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 +143 -0
- package/dist/bin/aeos-validator.d.ts +16 -0
- package/dist/bin/aeos-validator.d.ts.map +1 -0
- package/dist/bin/aeos-validator.js +77 -0
- package/dist/bin/aeos-validator.js.map +1 -0
- package/dist/diag/codes.d.ts +55 -0
- package/dist/diag/codes.d.ts.map +1 -0
- package/dist/diag/codes.js +69 -0
- package/dist/diag/codes.js.map +1 -0
- package/dist/diag/emit.d.ts +34 -0
- package/dist/diag/emit.d.ts.map +1 -0
- package/dist/diag/emit.js +45 -0
- package/dist/diag/emit.js.map +1 -0
- package/dist/diag/index.d.ts +6 -0
- package/dist/diag/index.d.ts.map +1 -0
- package/dist/diag/index.js +6 -0
- package/dist/diag/index.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/index.d.ts +10 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +10 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/numericForm.d.ts +29 -0
- package/dist/rules/numericForm.d.ts.map +1 -0
- package/dist/rules/numericForm.js +74 -0
- package/dist/rules/numericForm.js.map +1 -0
- package/dist/rules/presence.d.ts +20 -0
- package/dist/rules/presence.d.ts.map +1 -0
- package/dist/rules/presence.js +29 -0
- package/dist/rules/presence.js.map +1 -0
- package/dist/rules/referenceForm.d.ts +17 -0
- package/dist/rules/referenceForm.d.ts.map +1 -0
- package/dist/rules/referenceForm.js +78 -0
- package/dist/rules/referenceForm.js.map +1 -0
- package/dist/rules/schemaIndex.d.ts +34 -0
- package/dist/rules/schemaIndex.d.ts.map +1 -0
- package/dist/rules/schemaIndex.js +167 -0
- package/dist/rules/schemaIndex.js.map +1 -0
- package/dist/rules/stringForm.d.ts +48 -0
- package/dist/rules/stringForm.d.ts.map +1 -0
- package/dist/rules/stringForm.js +96 -0
- package/dist/rules/stringForm.js.map +1 -0
- package/dist/rules/typeCheck.d.ts +29 -0
- package/dist/rules/typeCheck.d.ts.map +1 -0
- package/dist/rules/typeCheck.js +99 -0
- package/dist/rules/typeCheck.js.map +1 -0
- package/dist/types/aes.d.ts +14 -0
- package/dist/types/aes.d.ts.map +1 -0
- package/dist/types/aes.js +8 -0
- package/dist/types/aes.js.map +1 -0
- package/dist/types/envelope.d.ts +47 -0
- package/dist/types/envelope.d.ts.map +1 -0
- package/dist/types/envelope.js +29 -0
- package/dist/types/envelope.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/schema.d.ts +81 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +41 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/types/spans.d.ts +28 -0
- package/dist/types/spans.d.ts.map +1 -0
- package/dist/types/spans.js +16 -0
- package/dist/types/spans.js.map +1 -0
- package/dist/util/digits.d.ts +34 -0
- package/dist/util/digits.d.ts.map +1 -0
- package/dist/util/digits.js +66 -0
- package/dist/util/digits.js.map +1 -0
- package/dist/util/index.d.ts +5 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +5 -0
- package/dist/util/index.js.map +1 -0
- package/dist/validate.d.ts +46 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +633 -0
- package/dist/validate.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @altopelago/aeos-core - Rules: Numeric Form
|
|
3
|
+
*
|
|
4
|
+
* Phase 6: Numeric form constraints (sign, digit count).
|
|
5
|
+
*/
|
|
6
|
+
import { createDiag, emitError } from '../diag/emit.js';
|
|
7
|
+
import { ErrorCodes } from '../diag/codes.js';
|
|
8
|
+
import { countIntegerDigits, isNegative } from '../util/digits.js';
|
|
9
|
+
/**
|
|
10
|
+
* Check numeric form constraints for events matching schema rules.
|
|
11
|
+
*
|
|
12
|
+
* For each event with numeric form constraints (sign, min_digits, max_digits),
|
|
13
|
+
* verify the literal's lexical representation satisfies the constraints.
|
|
14
|
+
*
|
|
15
|
+
* @param ruleIndex - Schema rule index (path → rule)
|
|
16
|
+
* @param events - Map of path → numeric value info
|
|
17
|
+
* @param ctx - Diagnostic context
|
|
18
|
+
*/
|
|
19
|
+
export function checkNumericForm(ruleIndex, events, ctx) {
|
|
20
|
+
for (const [path, rule] of ruleIndex) {
|
|
21
|
+
const { sign, min_digits, max_digits, min_value, max_value } = rule.constraints;
|
|
22
|
+
// Skip if no numeric form constraints
|
|
23
|
+
if (sign === undefined && min_digits === undefined && max_digits === undefined && min_value === undefined && max_value === undefined) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const event = events.get(path);
|
|
27
|
+
if (!event)
|
|
28
|
+
continue; // Missing path handled by presence check
|
|
29
|
+
// Only apply to numeric types
|
|
30
|
+
if (event.type !== 'NumberLiteral' && event.type !== 'IntegerLiteral' && event.type !== 'FloatLiteral') {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const raw = event.raw;
|
|
34
|
+
// Sign constraint
|
|
35
|
+
if (sign !== undefined) {
|
|
36
|
+
if (sign === 'unsigned' && isNegative(raw)) {
|
|
37
|
+
emitError(ctx, createDiag(path, event.span, `Numeric form violation: expected unsigned, got negative`, ErrorCodes.NUMERIC_FORM_VIOLATION));
|
|
38
|
+
continue; // Only report first violation per path
|
|
39
|
+
}
|
|
40
|
+
// 'signed' constraint allows both positive and negative
|
|
41
|
+
}
|
|
42
|
+
// Digit count constraints
|
|
43
|
+
const digitCount = countIntegerDigits(raw);
|
|
44
|
+
if (min_digits !== undefined && digitCount < min_digits) {
|
|
45
|
+
emitError(ctx, createDiag(path, event.span, `Numeric form violation: expected min ${min_digits} digits, got ${digitCount}`, ErrorCodes.NUMERIC_FORM_VIOLATION));
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (max_digits !== undefined && digitCount > max_digits) {
|
|
49
|
+
emitError(ctx, createDiag(path, event.span, `Numeric form violation: expected max ${max_digits} digits, got ${digitCount}`, ErrorCodes.NUMERIC_FORM_VIOLATION));
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (min_value !== undefined || max_value !== undefined) {
|
|
53
|
+
const normalized = normalizeIntegerLiteral(raw);
|
|
54
|
+
if (!normalized) {
|
|
55
|
+
emitError(ctx, createDiag(path, event.span, `Numeric form violation: exact integer range constraints require integer literal form`, ErrorCodes.NUMERIC_FORM_VIOLATION));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const numeric = BigInt(normalized);
|
|
59
|
+
if (min_value !== undefined && numeric < BigInt(min_value)) {
|
|
60
|
+
emitError(ctx, createDiag(path, event.span, `Numeric form violation: expected value >= ${min_value}, got ${normalized}`, ErrorCodes.NUMERIC_FORM_VIOLATION));
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (max_value !== undefined && numeric > BigInt(max_value)) {
|
|
64
|
+
emitError(ctx, createDiag(path, event.span, `Numeric form violation: expected value <= ${max_value}, got ${normalized}`, ErrorCodes.NUMERIC_FORM_VIOLATION));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function normalizeIntegerLiteral(raw) {
|
|
70
|
+
if (!/^[+-]?\d[\d_]*$/.test(raw))
|
|
71
|
+
return null;
|
|
72
|
+
return raw.replace(/_/g, '');
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=numericForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"numericForm.js","sourceRoot":"","sources":["../../src/rules/numericForm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @altopelago/aeos-core - Rules: Presence
|
|
3
|
+
*
|
|
4
|
+
* Phase 4: Presence checks for required fields.
|
|
5
|
+
*/
|
|
6
|
+
import type { DiagContext } from '../diag/emit.js';
|
|
7
|
+
import type { RuleIndex } from './schemaIndex.js';
|
|
8
|
+
/**
|
|
9
|
+
* Check presence constraints for all rules in the schema.
|
|
10
|
+
*
|
|
11
|
+
* For each rule with `required: true`, verify the path exists in the AES.
|
|
12
|
+
* If missing, emit `missing_required_field` error with null span
|
|
13
|
+
* (since the path doesn't exist in source).
|
|
14
|
+
*
|
|
15
|
+
* @param ruleIndex - Schema rule index (path → rule)
|
|
16
|
+
* @param boundPaths - Set of paths that exist in the AES
|
|
17
|
+
* @param ctx - Diagnostic context
|
|
18
|
+
*/
|
|
19
|
+
export declare function checkPresence(ruleIndex: RuleIndex, boundPaths: ReadonlySet<string>, ctx: DiagContext): void;
|
|
20
|
+
//# sourceMappingURL=presence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/rules/presence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CACzB,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,EAC/B,GAAG,EAAE,WAAW,GACjB,IAAI,CAaN"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @altopelago/aeos-core - Rules: Presence
|
|
3
|
+
*
|
|
4
|
+
* Phase 4: Presence checks for required fields.
|
|
5
|
+
*/
|
|
6
|
+
import { createDiag, emitError } from '../diag/emit.js';
|
|
7
|
+
import { ErrorCodes } from '../diag/codes.js';
|
|
8
|
+
/**
|
|
9
|
+
* Check presence constraints for all rules in the schema.
|
|
10
|
+
*
|
|
11
|
+
* For each rule with `required: true`, verify the path exists in the AES.
|
|
12
|
+
* If missing, emit `missing_required_field` error with null span
|
|
13
|
+
* (since the path doesn't exist in source).
|
|
14
|
+
*
|
|
15
|
+
* @param ruleIndex - Schema rule index (path → rule)
|
|
16
|
+
* @param boundPaths - Set of paths that exist in the AES
|
|
17
|
+
* @param ctx - Diagnostic context
|
|
18
|
+
*/
|
|
19
|
+
export function checkPresence(ruleIndex, boundPaths, ctx) {
|
|
20
|
+
for (const [path, rule] of ruleIndex) {
|
|
21
|
+
if (rule.constraints.required === true) {
|
|
22
|
+
if (!boundPaths.has(path)) {
|
|
23
|
+
emitError(ctx, createDiag(path, null, // null span for missing paths
|
|
24
|
+
`Missing required field: ${path}`, ErrorCodes.MISSING_REQUIRED_FIELD));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.js","sourceRoot":"","sources":["../../src/rules/presence.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CACzB,SAAoB,EACpB,UAA+B,EAC/B,GAAgB;IAEhB,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,WAAW,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACrC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,SAAS,CAAC,GAAG,EAAE,UAAU,CACrB,IAAI,EACJ,IAAI,EAAE,8BAA8B;gBACpC,2BAA2B,IAAI,EAAE,EACjC,UAAU,CAAC,sBAAsB,CACpC,CAAC,CAAC;YACP,CAAC;QACL,CAAC;IACL,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type DiagContext } from '../diag/emit.js';
|
|
2
|
+
import type { RuleIndex } from './schemaIndex.js';
|
|
3
|
+
import type { SchemaV1 } from '../types/schema.js';
|
|
4
|
+
type EventInfo = {
|
|
5
|
+
type: string;
|
|
6
|
+
raw: string;
|
|
7
|
+
value: string;
|
|
8
|
+
datatype?: string;
|
|
9
|
+
span: [number, number] | null;
|
|
10
|
+
referencePath?: readonly (string | number | {
|
|
11
|
+
readonly type: 'attr';
|
|
12
|
+
readonly key: string;
|
|
13
|
+
})[];
|
|
14
|
+
};
|
|
15
|
+
export declare function checkReferenceForms(schema: SchemaV1, ruleIndex: RuleIndex, eventsByPath: ReadonlyMap<string, EventInfo>, ctx: DiagContext): void;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=referenceForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"referenceForm.d.ts","sourceRoot":"","sources":["../../src/rules/referenceForm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD,KAAK,SAAS,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;CAClG,CAAC;AA+BF,wBAAgB,mBAAmB,CAC/B,MAAM,EAAE,QAAQ,EAChB,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,EAC5C,GAAG,EAAE,WAAW,GACjB,IAAI,CAyEN"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createDiag, emitError } from '../diag/emit.js';
|
|
2
|
+
import { ErrorCodes } from '../diag/codes.js';
|
|
3
|
+
function isReferenceType(type) {
|
|
4
|
+
return type === 'CloneReference' || type === 'PointerReference';
|
|
5
|
+
}
|
|
6
|
+
function formatQuotedMemberSegment(key) {
|
|
7
|
+
return `.[${JSON.stringify(key)}]`;
|
|
8
|
+
}
|
|
9
|
+
function formatReferenceTargetPath(segments) {
|
|
10
|
+
if (segments.length === 0)
|
|
11
|
+
return '$';
|
|
12
|
+
let out = '$';
|
|
13
|
+
for (const segment of segments) {
|
|
14
|
+
if (typeof segment === 'number') {
|
|
15
|
+
out += `[${segment}]`;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (typeof segment === 'string') {
|
|
19
|
+
out += /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(segment)
|
|
20
|
+
? `.${segment}`
|
|
21
|
+
: formatQuotedMemberSegment(segment);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
out += /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(segment.key)
|
|
25
|
+
? `@${segment.key}`
|
|
26
|
+
: `@[${JSON.stringify(segment.key)}]`;
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
export function checkReferenceForms(schema, ruleIndex, eventsByPath, ctx) {
|
|
31
|
+
if ((schema.reference_policy ?? 'allow') === 'forbid') {
|
|
32
|
+
for (const [path, event] of eventsByPath.entries()) {
|
|
33
|
+
if (!isReferenceType(event.type))
|
|
34
|
+
continue;
|
|
35
|
+
emitError(ctx, createDiag(path, event.span, `References are forbidden by schema reference_policy, got ${event.type}`, ErrorCodes.REFERENCE_FORBIDDEN));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
for (const [path, rule] of ruleIndex) {
|
|
39
|
+
const reference = rule.constraints.reference;
|
|
40
|
+
const referenceKind = rule.constraints.reference_kind;
|
|
41
|
+
const event = eventsByPath.get(path);
|
|
42
|
+
if (!event)
|
|
43
|
+
continue;
|
|
44
|
+
if (reference === 'forbid') {
|
|
45
|
+
if (!isReferenceType(event.type))
|
|
46
|
+
continue;
|
|
47
|
+
emitError(ctx, createDiag(path, event.span, `Reference not allowed at ${path}, got ${event.type}`, ErrorCodes.REFERENCE_FORBIDDEN));
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (reference === 'allow' || reference === undefined) {
|
|
51
|
+
// Allow still permits target-domain constraints.
|
|
52
|
+
}
|
|
53
|
+
else if (reference === 'require') {
|
|
54
|
+
if (!isReferenceType(event.type)) {
|
|
55
|
+
emitError(ctx, createDiag(path, event.span, `Reference required at ${path}, got ${event.type}`, ErrorCodes.REFERENCE_REQUIRED));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (referenceKind !== undefined && referenceKind !== 'either') {
|
|
59
|
+
const expectedType = referenceKind === 'clone' ? 'CloneReference' : 'PointerReference';
|
|
60
|
+
if (event.type !== expectedType) {
|
|
61
|
+
emitError(ctx, createDiag(path, event.span, `Reference kind mismatch at ${path}: expected ${expectedType}, got ${event.type}`, ErrorCodes.REFERENCE_KIND_MISMATCH));
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const targetPattern = rule.constraints.reference_target_pattern;
|
|
67
|
+
if (targetPattern === undefined) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (!isReferenceType(event.type) || !event.referencePath) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!(new RegExp(targetPattern).test(formatReferenceTargetPath(event.referencePath)))) {
|
|
74
|
+
emitError(ctx, createDiag(path, event.span, `Reference target path does not satisfy reference_target_pattern at ${path}`, ErrorCodes.REFERENCE_TARGET_MISMATCH));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=referenceForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"referenceForm.js","sourceRoot":"","sources":["../../src/rules/referenceForm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @altopelago/aeos-core - Rules: Schema Index
|
|
3
|
+
*
|
|
4
|
+
* Build a fast lookup index from schema rules.
|
|
5
|
+
*/
|
|
6
|
+
import type { SchemaV1, SchemaRule } from '../types/schema.js';
|
|
7
|
+
import type { DiagContext } from '../diag/emit.js';
|
|
8
|
+
/**
|
|
9
|
+
* Rule index: Map from canonical path to rule
|
|
10
|
+
*/
|
|
11
|
+
export type RuleIndex = ReadonlyMap<string, SchemaRule>;
|
|
12
|
+
/**
|
|
13
|
+
* Build a rule index from a schema.
|
|
14
|
+
*
|
|
15
|
+
* This preprocesses schema.rules into a Map<path, Rule> for O(1) lookup.
|
|
16
|
+
* Emits errors for:
|
|
17
|
+
* - Missing path in rule
|
|
18
|
+
* - Duplicate rule paths
|
|
19
|
+
* - Unknown constraint keys
|
|
20
|
+
*
|
|
21
|
+
* @param schema - AEOS Schema v1
|
|
22
|
+
* @param ctx - Diagnostic context for errors
|
|
23
|
+
* @returns Rule index map
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildRuleIndex(schema: SchemaV1, ctx: DiagContext): RuleIndex;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a rule has numeric form constraints
|
|
28
|
+
*/
|
|
29
|
+
export declare function hasNumericFormConstraints(constraints: SchemaRule['constraints']): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a rule has string form constraints
|
|
32
|
+
*/
|
|
33
|
+
export declare function hasStringFormConstraints(constraints: SchemaRule['constraints']): boolean;
|
|
34
|
+
//# sourceMappingURL=schemaIndex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaIndex.d.ts","sourceRoot":"","sources":["../../src/rules/schemaIndex.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAsMnD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAExD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,GAAG,SAAS,CA+D5E;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,OAAO,CAMzF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,OAAO,CAMxF"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @altopelago/aeos-core - Rules: Schema Index
|
|
3
|
+
*
|
|
4
|
+
* Build a fast lookup index from schema rules.
|
|
5
|
+
*/
|
|
6
|
+
import { hasUnknownConstraintKeys } from '../types/schema.js';
|
|
7
|
+
import { createDiag, emitError } from '../diag/emit.js';
|
|
8
|
+
import { ErrorCodes } from '../diag/codes.js';
|
|
9
|
+
function isReferenceType(type) {
|
|
10
|
+
return type === 'CloneReference' || type === 'PointerReference';
|
|
11
|
+
}
|
|
12
|
+
function validateReferenceConstraints(schema, rulePath, constraints, ctx) {
|
|
13
|
+
const reference = constraints.reference;
|
|
14
|
+
const referenceKind = constraints.reference_kind;
|
|
15
|
+
const referenceTargetPattern = constraints.reference_target_pattern;
|
|
16
|
+
const resolveReferenceForm = constraints.resolve_reference_form;
|
|
17
|
+
const expectedType = typeof constraints.type === 'string' ? constraints.type : undefined;
|
|
18
|
+
const schemaReferencePolicy = schema.reference_policy;
|
|
19
|
+
if (reference !== undefined && (typeof reference !== 'string' || !['allow', 'forbid', 'require'].includes(reference))) {
|
|
20
|
+
emitError(ctx, createDiag(rulePath, null, `Invalid reference constraint for path ${rulePath}: ${String(reference)}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (referenceKind !== undefined && (typeof referenceKind !== 'string' || !['clone', 'pointer', 'either'].includes(referenceKind))) {
|
|
24
|
+
emitError(ctx, createDiag(rulePath, null, `Invalid reference_kind constraint for path ${rulePath}: ${String(referenceKind)}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (referenceKind !== undefined && reference !== 'require') {
|
|
28
|
+
emitError(ctx, createDiag(rulePath, null, `reference_kind requires reference='require' for path ${rulePath}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (referenceTargetPattern !== undefined) {
|
|
32
|
+
if (typeof referenceTargetPattern !== 'string') {
|
|
33
|
+
emitError(ctx, createDiag(rulePath, null, `Invalid reference_target_pattern constraint for path ${rulePath}: ${String(referenceTargetPattern)}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
new RegExp(referenceTargetPattern);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
emitError(ctx, createDiag(rulePath, null, `Invalid reference_target_pattern regex for path ${rulePath}: ${referenceTargetPattern}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (reference === 'forbid') {
|
|
44
|
+
emitError(ctx, createDiag(rulePath, null, `reference_target_pattern conflicts with reference='forbid' for path ${rulePath}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (resolveReferenceForm !== undefined && typeof resolveReferenceForm !== 'boolean') {
|
|
49
|
+
emitError(ctx, createDiag(rulePath, null, `resolve_reference_form must be boolean for path ${rulePath}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (reference === 'forbid' && expectedType !== undefined && isReferenceType(expectedType)) {
|
|
53
|
+
emitError(ctx, createDiag(rulePath, null, `reference='forbid' conflicts with type='${expectedType}' for path ${rulePath}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (reference === 'require' && expectedType !== undefined && !isReferenceType(expectedType)) {
|
|
57
|
+
emitError(ctx, createDiag(rulePath, null, `reference='require' conflicts with non-reference type='${expectedType}' for path ${rulePath}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (referenceKind === 'clone' && expectedType === 'PointerReference') {
|
|
61
|
+
emitError(ctx, createDiag(rulePath, null, `reference_kind='clone' conflicts with type='PointerReference' for path ${rulePath}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (referenceKind === 'pointer' && expectedType === 'CloneReference') {
|
|
65
|
+
emitError(ctx, createDiag(rulePath, null, `reference_kind='pointer' conflicts with type='CloneReference' for path ${rulePath}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (schemaReferencePolicy === 'forbid' && (reference === 'require' || (expectedType !== undefined && isReferenceType(expectedType)))) {
|
|
69
|
+
emitError(ctx, createDiag(rulePath, null, `schema reference_policy='forbid' conflicts with rule for path ${rulePath}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
function validateConstraintTree(schema, rulePath, constraints, ctx) {
|
|
75
|
+
if (hasUnknownConstraintKeys(constraints)) {
|
|
76
|
+
emitError(ctx, createDiag(rulePath, null, `Unknown constraint key in rule for path: ${rulePath}`, ErrorCodes.UNKNOWN_CONSTRAINT_KEY));
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
if (!validateReferenceConstraints(schema, rulePath, constraints, ctx)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
const nestedAttributes = constraints.attributes;
|
|
83
|
+
if (nestedAttributes === undefined) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
if (nestedAttributes === null || typeof nestedAttributes !== 'object' || Array.isArray(nestedAttributes)) {
|
|
87
|
+
emitError(ctx, createDiag(rulePath, null, `Invalid attributes constraint for path ${rulePath}`, ErrorCodes.UNKNOWN_CONSTRAINT_KEY));
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
for (const [key, childConstraints] of Object.entries(nestedAttributes)) {
|
|
91
|
+
if (childConstraints === null || typeof childConstraints !== 'object' || Array.isArray(childConstraints)) {
|
|
92
|
+
emitError(ctx, createDiag(`${rulePath}@${key}`, null, `Invalid attribute constraint for path ${rulePath}@${key}`, ErrorCodes.UNKNOWN_CONSTRAINT_KEY));
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (!validateConstraintTree(schema, `${rulePath}@${key}`, childConstraints, ctx)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Build a rule index from a schema.
|
|
103
|
+
*
|
|
104
|
+
* This preprocesses schema.rules into a Map<path, Rule> for O(1) lookup.
|
|
105
|
+
* Emits errors for:
|
|
106
|
+
* - Missing path in rule
|
|
107
|
+
* - Duplicate rule paths
|
|
108
|
+
* - Unknown constraint keys
|
|
109
|
+
*
|
|
110
|
+
* @param schema - AEOS Schema v1
|
|
111
|
+
* @param ctx - Diagnostic context for errors
|
|
112
|
+
* @returns Rule index map
|
|
113
|
+
*/
|
|
114
|
+
export function buildRuleIndex(schema, ctx) {
|
|
115
|
+
const index = new Map();
|
|
116
|
+
// Schema-level allowlist for datatype identifiers (optional)
|
|
117
|
+
const datatypeAllowlist = schema.datatype_allowlist;
|
|
118
|
+
if (schema.reference_policy !== undefined && !['allow', 'forbid'].includes(schema.reference_policy)) {
|
|
119
|
+
emitError(ctx, createDiag('$', null, `Invalid schema reference_policy: ${String(schema.reference_policy)}`, ErrorCodes.INVALID_REFERENCE_CONSTRAINT));
|
|
120
|
+
}
|
|
121
|
+
for (const rule of schema.rules) {
|
|
122
|
+
// Check for missing path
|
|
123
|
+
if (!rule.path || typeof rule.path !== 'string') {
|
|
124
|
+
emitError(ctx, createDiag('<unknown>', null, 'Rule missing required "path" field', ErrorCodes.RULE_MISSING_PATH));
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
// Check for duplicate rule paths
|
|
128
|
+
if (index.has(rule.path)) {
|
|
129
|
+
emitError(ctx, createDiag(rule.path, null, `Duplicate rule for path: ${rule.path}`, ErrorCodes.DUPLICATE_RULE_PATH));
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (!validateConstraintTree(schema, rule.path, rule.constraints, ctx)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
// Enforce datatype allow-list if provided at schema level.
|
|
136
|
+
// This is a form-only membership check: the `datatype` string
|
|
137
|
+
// must be present in the schema.datatype_allowlist array when
|
|
138
|
+
// that array is provided. Emit a value-level diagnostic code
|
|
139
|
+
// to indicate the identifier is not allowed.
|
|
140
|
+
if (datatypeAllowlist && rule.constraints && typeof rule.constraints.datatype === 'string') {
|
|
141
|
+
const dt = rule.constraints.datatype;
|
|
142
|
+
if (!datatypeAllowlist.includes(dt)) {
|
|
143
|
+
emitError(ctx, createDiag(rule.path, null, `Datatype '${dt}' not allowed by schema datatype_allowlist`, ErrorCodes.DATATYPE_ALLOWLIST_REJECT));
|
|
144
|
+
// continue; still index the rule so other checks can run
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
index.set(rule.path, rule);
|
|
148
|
+
}
|
|
149
|
+
return index;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check if a rule has numeric form constraints
|
|
153
|
+
*/
|
|
154
|
+
export function hasNumericFormConstraints(constraints) {
|
|
155
|
+
return (constraints.sign !== undefined ||
|
|
156
|
+
constraints.min_digits !== undefined ||
|
|
157
|
+
constraints.max_digits !== undefined);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Check if a rule has string form constraints
|
|
161
|
+
*/
|
|
162
|
+
export function hasStringFormConstraints(constraints) {
|
|
163
|
+
return (constraints.pattern !== undefined ||
|
|
164
|
+
constraints.min_length !== undefined ||
|
|
165
|
+
constraints.max_length !== undefined);
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=schemaIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaIndex.js","sourceRoot":"","sources":["../../src/rules/schemaIndex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @altopelago/aeos-core - Rules: String Form
|
|
3
|
+
*
|
|
4
|
+
* Phase 7: String form constraints (min_length, max_length, pattern).
|
|
5
|
+
*/
|
|
6
|
+
import type { DiagContext } from '../diag/emit.js';
|
|
7
|
+
import type { Span } from '../types/spans.js';
|
|
8
|
+
import type { RuleIndex } from './schemaIndex.js';
|
|
9
|
+
/**
|
|
10
|
+
* Event value with type, processed value string, and span
|
|
11
|
+
*/
|
|
12
|
+
interface StringValue {
|
|
13
|
+
type: string;
|
|
14
|
+
value: string;
|
|
15
|
+
span: Span;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check string form constraints for events matching schema rules.
|
|
19
|
+
*
|
|
20
|
+
* For each event with string form constraints (min_length, max_length),
|
|
21
|
+
* verify the string value's length satisfies the constraints.
|
|
22
|
+
*
|
|
23
|
+
* AEOS v1 Decision: Length is measured in UTF-16 code units (JavaScript
|
|
24
|
+
* string.length). This means surrogate pairs (emoji, etc.) count as 2.
|
|
25
|
+
* This is intentional for v1 simplicity and JavaScript compatibility.
|
|
26
|
+
*
|
|
27
|
+
* @param ruleIndex - Schema rule index (path → rule)
|
|
28
|
+
* @param events - Map of path → string value info
|
|
29
|
+
* @param ctx - Diagnostic context
|
|
30
|
+
*/
|
|
31
|
+
export declare function checkStringForm(ruleIndex: RuleIndex, events: ReadonlyMap<string, StringValue>, ctx: DiagContext): void;
|
|
32
|
+
/**
|
|
33
|
+
* Check pattern constraints for events matching schema rules.
|
|
34
|
+
*
|
|
35
|
+
* For each event with a pattern constraint, verify the string value
|
|
36
|
+
* matches the regex pattern.
|
|
37
|
+
*
|
|
38
|
+
* AEOS v1 Decision: Patterns are ECMAScript regex strings. The pattern
|
|
39
|
+
* must match the entire string (anchored with ^...$). If the pattern
|
|
40
|
+
* does not include anchors, they are added automatically.
|
|
41
|
+
*
|
|
42
|
+
* @param ruleIndex - Schema rule index (path → rule)
|
|
43
|
+
* @param events - Map of path → string value info
|
|
44
|
+
* @param ctx - Diagnostic context
|
|
45
|
+
*/
|
|
46
|
+
export declare function checkPatterns(ruleIndex: RuleIndex, events: ReadonlyMap<string, StringValue>, ctx: DiagContext): void;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=stringForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stringForm.d.ts","sourceRoot":"","sources":["../../src/rules/stringForm.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD;;GAEG;AACH,UAAU,WAAW;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;CACd;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC3B,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EACxC,GAAG,EAAE,WAAW,GACjB,IAAI,CAwCN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CACzB,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EACxC,GAAG,EAAE,WAAW,GACjB,IAAI,CA2CN"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @altopelago/aeos-core - Rules: String Form
|
|
3
|
+
*
|
|
4
|
+
* Phase 7: String form constraints (min_length, max_length, pattern).
|
|
5
|
+
*/
|
|
6
|
+
import { createDiag, emitError } from '../diag/emit.js';
|
|
7
|
+
import { ErrorCodes } from '../diag/codes.js';
|
|
8
|
+
/**
|
|
9
|
+
* Check string form constraints for events matching schema rules.
|
|
10
|
+
*
|
|
11
|
+
* For each event with string form constraints (min_length, max_length),
|
|
12
|
+
* verify the string value's length satisfies the constraints.
|
|
13
|
+
*
|
|
14
|
+
* AEOS v1 Decision: Length is measured in UTF-16 code units (JavaScript
|
|
15
|
+
* string.length). This means surrogate pairs (emoji, etc.) count as 2.
|
|
16
|
+
* This is intentional for v1 simplicity and JavaScript compatibility.
|
|
17
|
+
*
|
|
18
|
+
* @param ruleIndex - Schema rule index (path → rule)
|
|
19
|
+
* @param events - Map of path → string value info
|
|
20
|
+
* @param ctx - Diagnostic context
|
|
21
|
+
*/
|
|
22
|
+
export function checkStringForm(ruleIndex, events, ctx) {
|
|
23
|
+
for (const [path, rule] of ruleIndex) {
|
|
24
|
+
const { min_length, max_length, pattern } = rule.constraints;
|
|
25
|
+
// Skip if no string length or pattern constraints
|
|
26
|
+
if (min_length === undefined && max_length === undefined && pattern === undefined) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const event = events.get(path);
|
|
30
|
+
if (!event)
|
|
31
|
+
continue; // Missing path handled by presence check
|
|
32
|
+
// Only apply to string types
|
|
33
|
+
if (event.type !== 'StringLiteral') {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const length = event.value.length;
|
|
37
|
+
if (min_length !== undefined && length < min_length) {
|
|
38
|
+
emitError(ctx, createDiag(path, event.span, `String form violation: expected min length ${min_length}, got ${length}`, ErrorCodes.STRING_LENGTH_VIOLATION));
|
|
39
|
+
continue; // Only report first violation per path
|
|
40
|
+
}
|
|
41
|
+
if (max_length !== undefined && length > max_length) {
|
|
42
|
+
emitError(ctx, createDiag(path, event.span, `String form violation: expected max length ${max_length}, got ${length}`, ErrorCodes.STRING_LENGTH_VIOLATION));
|
|
43
|
+
}
|
|
44
|
+
// Note: pattern enforcement is handled separately in `checkPatterns()`.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check pattern constraints for events matching schema rules.
|
|
49
|
+
*
|
|
50
|
+
* For each event with a pattern constraint, verify the string value
|
|
51
|
+
* matches the regex pattern.
|
|
52
|
+
*
|
|
53
|
+
* AEOS v1 Decision: Patterns are ECMAScript regex strings. The pattern
|
|
54
|
+
* must match the entire string (anchored with ^...$). If the pattern
|
|
55
|
+
* does not include anchors, they are added automatically.
|
|
56
|
+
*
|
|
57
|
+
* @param ruleIndex - Schema rule index (path → rule)
|
|
58
|
+
* @param events - Map of path → string value info
|
|
59
|
+
* @param ctx - Diagnostic context
|
|
60
|
+
*/
|
|
61
|
+
export function checkPatterns(ruleIndex, events, ctx) {
|
|
62
|
+
for (const [path, rule] of ruleIndex) {
|
|
63
|
+
const { pattern } = rule.constraints;
|
|
64
|
+
if (pattern === undefined) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const event = events.get(path);
|
|
68
|
+
if (!event)
|
|
69
|
+
continue; // Missing path handled by presence check
|
|
70
|
+
// Only apply to string types
|
|
71
|
+
if (event.type !== 'StringLiteral') {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// Compile pattern (add anchors if not present for full-match semantics)
|
|
75
|
+
let regexPattern = pattern;
|
|
76
|
+
if (!regexPattern.startsWith('^')) {
|
|
77
|
+
regexPattern = '^' + regexPattern;
|
|
78
|
+
}
|
|
79
|
+
if (!regexPattern.endsWith('$')) {
|
|
80
|
+
regexPattern = regexPattern + '$';
|
|
81
|
+
}
|
|
82
|
+
let regex;
|
|
83
|
+
try {
|
|
84
|
+
regex = new RegExp(regexPattern);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Invalid regex is a schema error, not a data error
|
|
88
|
+
// This should have been caught during schema validation
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!regex.test(event.value)) {
|
|
92
|
+
emitError(ctx, createDiag(path, event.span, `Pattern mismatch: value does not match pattern "${pattern}"`, ErrorCodes.PATTERN_MISMATCH));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=stringForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stringForm.js","sourceRoot":"","sources":["../../src/rules/stringForm.ts"],"names":[],"mappings":"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"}
|