@clementine-solutions/jane-io 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/analysis/diff.js +88 -0
- package/dist/core/analysis/explain.js +117 -0
- package/dist/core/analysis/index.js +26 -0
- package/dist/core/analysis/replay.js +68 -0
- package/dist/core/analysis/telemetry.js +123 -0
- package/dist/core/boundary-rules/at-most-one.js +38 -0
- package/dist/core/boundary-rules/conditionally-required.js +44 -0
- package/dist/core/boundary-rules/date-range.js +39 -0
- package/dist/core/boundary-rules/index.js +21 -0
- package/dist/core/boundary-rules/mutually-exclusive.js +38 -0
- package/dist/core/boundary-rules/no-unknown-fields.js +39 -0
- package/dist/core/boundary-rules/require-all.js +39 -0
- package/dist/core/boundary-rules/require-one.js +41 -0
- package/dist/core/common/events.js +82 -0
- package/dist/core/common/fluent.js +429 -0
- package/dist/core/common/index.js +31 -0
- package/dist/core/common/policy.js +550 -0
- package/dist/core/common/utilities.js +177 -0
- package/dist/core/common/wildcard.js +63 -0
- package/dist/core/field-path/construct.js +189 -0
- package/dist/core/field-path/format.js +154 -0
- package/dist/core/field-path/index.js +26 -0
- package/dist/core/field-path/utilities.js +138 -0
- package/dist/core/field-path/walk.js +80 -0
- package/dist/core/fluent-registry.js +151 -0
- package/dist/core/normalizers/array/compact-sparse-array.js +40 -0
- package/dist/core/normalizers/array/flatten-one-level.js +45 -0
- package/dist/core/normalizers/array/remove-empty-string-items.js +34 -0
- package/dist/core/normalizers/array/remove-null-items.js +34 -0
- package/dist/core/normalizers/array/remove-undefined-items.js +34 -0
- package/dist/core/normalizers/date/invalid-date-to-undefined.js +35 -0
- package/dist/core/normalizers/index.js +46 -0
- package/dist/core/normalizers/normalizer-register.js +41 -0
- package/dist/core/normalizers/number/infinity-to-undefined.js +35 -0
- package/dist/core/normalizers/number/nan-to-undefined.js +34 -0
- package/dist/core/normalizers/number/normalize-negative-zero.js +33 -0
- package/dist/core/normalizers/object/remove-empty-array-keys.js +38 -0
- package/dist/core/normalizers/object/remove-empty-object-keys.js +42 -0
- package/dist/core/normalizers/object/remove-empty-string-keys.js +37 -0
- package/dist/core/normalizers/object/remove-null-keys.js +37 -0
- package/dist/core/normalizers/object/remove-undefined-keys.js +37 -0
- package/dist/core/normalizers/string/collapse-whitespace.js +35 -0
- package/dist/core/normalizers/string/empty-to-undefined.js +33 -0
- package/dist/core/normalizers/string/trim.js +34 -0
- package/dist/core/parsers/index.js +43 -0
- package/dist/core/parsers/parse-array-string.js +36 -0
- package/dist/core/parsers/parse-bigint-string.js +33 -0
- package/dist/core/parsers/parse-binary-string.js +33 -0
- package/dist/core/parsers/parse-boolean-string.js +27 -0
- package/dist/core/parsers/parse-date-string.js +30 -0
- package/dist/core/parsers/parse-duration-string.js +37 -0
- package/dist/core/parsers/parse-hex-string.js +29 -0
- package/dist/core/parsers/parse-integer-string.js +30 -0
- package/dist/core/parsers/parse-json-string.js +35 -0
- package/dist/core/parsers/parse-numeric-string.js +29 -0
- package/dist/core/parsers/parse-object-string.js +36 -0
- package/dist/core/parsers/parse-octal-string.js +33 -0
- package/dist/core/parsers/parse-scientific-notation-string.js +30 -0
- package/dist/core/parsers/parse-url-string.js +36 -0
- package/dist/core/pipeline/boundary.js +256 -0
- package/dist/core/pipeline/contain.js +339 -0
- package/dist/core/pipeline/index.js +37 -0
- package/dist/core/pipeline/normalize.js +76 -0
- package/dist/core/pipeline/parse.js +91 -0
- package/dist/core/pipeline/pipeline.js +418 -0
- package/dist/core/pipeline/scan.js +115 -0
- package/dist/core/pipeline/validate.js +74 -0
- package/dist/core/scanners/any/scan-for-sentinels.js +35 -0
- package/dist/core/scanners/array/array-is-deep.js +38 -0
- package/dist/core/scanners/array/array-is-heterogenous.js +39 -0
- package/dist/core/scanners/array/array-is-large.js +32 -0
- package/dist/core/scanners/bigint/bigint-is-large.js +34 -0
- package/dist/core/scanners/bigint/bigint-not-safe.js +34 -0
- package/dist/core/scanners/date/date-is-before-epoch.js +32 -0
- package/dist/core/scanners/date/date-is-far-future.js +32 -0
- package/dist/core/scanners/date/date-is-invalid.js +31 -0
- package/dist/core/scanners/index.js +58 -0
- package/dist/core/scanners/number/number-is-infinite.js +31 -0
- package/dist/core/scanners/number/number-is-nan.js +31 -0
- package/dist/core/scanners/number/number-is-too-large.js +33 -0
- package/dist/core/scanners/number/number-is-unsafe-integer.js +31 -0
- package/dist/core/scanners/object/object-has-circular-references.js +43 -0
- package/dist/core/scanners/object/object-has-many-keys.js +33 -0
- package/dist/core/scanners/object/object-is-deep.js +38 -0
- package/dist/core/scanners/scanner-registry.js +36 -0
- package/dist/core/scanners/string/string-has-unsafe-unicode.js +32 -0
- package/dist/core/scanners/string/string-has-whitespace-edges.js +31 -0
- package/dist/core/scanners/string/string-is-long.js +32 -0
- package/dist/core/scanners/unknown/unknown-not-scannable.js +34 -0
- package/dist/core/shapes/analysis.js +11 -0
- package/dist/core/shapes/boundary.js +11 -0
- package/dist/core/shapes/events.js +10 -0
- package/dist/core/shapes/field-path.js +10 -0
- package/dist/core/shapes/index.js +11 -0
- package/dist/core/shapes/normalize.js +11 -0
- package/dist/core/shapes/parse.js +11 -0
- package/dist/core/shapes/pipeline.js +11 -0
- package/dist/core/shapes/policy.js +11 -0
- package/dist/core/shapes/public.js +10 -0
- package/dist/core/shapes/scan.js +11 -0
- package/dist/core/shapes/validate.js +11 -0
- package/dist/core/validators/array/array-max-items.js +42 -0
- package/dist/core/validators/array/array-min-items.js +42 -0
- package/dist/core/validators/array/array.js +34 -0
- package/dist/core/validators/array/excludes.js +47 -0
- package/dist/core/validators/array/has-unique-items.js +46 -0
- package/dist/core/validators/array/includes.js +46 -0
- package/dist/core/validators/array/items-equal.js +42 -0
- package/dist/core/validators/array/no-empty-string-items.js +46 -0
- package/dist/core/validators/array/no-null-items.js +46 -0
- package/dist/core/validators/array/no-undefined-items.js +45 -0
- package/dist/core/validators/array/non-empty-array.js +45 -0
- package/dist/core/validators/array/not-sparse.js +44 -0
- package/dist/core/validators/bigint/bigint-equals.js +57 -0
- package/dist/core/validators/bigint/bigint-max.js +63 -0
- package/dist/core/validators/bigint/bigint-min.js +87 -0
- package/dist/core/validators/bigint/bigint-negative.js +73 -0
- package/dist/core/validators/bigint/bigint-non-negative.js +72 -0
- package/dist/core/validators/bigint/bigint-non-positive.js +72 -0
- package/dist/core/validators/bigint/bigint-positive.js +72 -0
- package/dist/core/validators/bigint/bigint-safe.js +75 -0
- package/dist/core/validators/bigint/bigint.js +38 -0
- package/dist/core/validators/boolean/boolean.js +39 -0
- package/dist/core/validators/boolean/is-false.js +48 -0
- package/dist/core/validators/boolean/is-true.js +48 -0
- package/dist/core/validators/common/is-country-code.js +36 -0
- package/dist/core/validators/common/is-currency-code.js +36 -0
- package/dist/core/validators/common/is-email-strict.js +36 -0
- package/dist/core/validators/common/is-email.js +36 -0
- package/dist/core/validators/common/is-ip.js +37 -0
- package/dist/core/validators/common/is-phone-strict.js +36 -0
- package/dist/core/validators/common/is-phone.js +36 -0
- package/dist/core/validators/common/is-port.js +35 -0
- package/dist/core/validators/common/is-postal-code.js +36 -0
- package/dist/core/validators/common/is-url.js +38 -0
- package/dist/core/validators/common/is-uuid.js +36 -0
- package/dist/core/validators/date/before-epoch.js +56 -0
- package/dist/core/validators/date/date-now-required.js +48 -0
- package/dist/core/validators/date/is-date.js +47 -0
- package/dist/core/validators/date/is-far-future.js +46 -0
- package/dist/core/validators/date/is-future.js +59 -0
- package/dist/core/validators/date/is-past.js +59 -0
- package/dist/core/validators/date/not-after.js +66 -0
- package/dist/core/validators/date/not-before.js +66 -0
- package/dist/core/validators/date/same-day.js +60 -0
- package/dist/core/validators/date/same-month.js +59 -0
- package/dist/core/validators/date/same-year.js +56 -0
- package/dist/core/validators/date/too-early.js +57 -0
- package/dist/core/validators/date/too-late.js +57 -0
- package/dist/core/validators/date/weekday.js +65 -0
- package/dist/core/validators/date/weekend.js +56 -0
- package/dist/core/validators/index.js +139 -0
- package/dist/core/validators/nullish/is-null-or-undefined.js +40 -0
- package/dist/core/validators/nullish/is-null.js +39 -0
- package/dist/core/validators/nullish/is-undefined.js +39 -0
- package/dist/core/validators/number/finite.js +40 -0
- package/dist/core/validators/number/integer.js +40 -0
- package/dist/core/validators/number/less-than.js +39 -0
- package/dist/core/validators/number/max.js +39 -0
- package/dist/core/validators/number/min.js +39 -0
- package/dist/core/validators/number/more-than.js +39 -0
- package/dist/core/validators/number/negative.js +38 -0
- package/dist/core/validators/number/non-negative.js +37 -0
- package/dist/core/validators/number/non-positive.js +37 -0
- package/dist/core/validators/number/number.js +31 -0
- package/dist/core/validators/number/positive.js +38 -0
- package/dist/core/validators/number/safe-integer.js +42 -0
- package/dist/core/validators/object/deep-equals.js +47 -0
- package/dist/core/validators/object/has-key.js +42 -0
- package/dist/core/validators/object/has-value.js +59 -0
- package/dist/core/validators/object/keys-equal.js +47 -0
- package/dist/core/validators/object/max-keys.js +43 -0
- package/dist/core/validators/object/min-keys.js +43 -0
- package/dist/core/validators/object/missing-key.js +42 -0
- package/dist/core/validators/object/no-empty-array-values.js +44 -0
- package/dist/core/validators/object/no-empty-object-values.js +44 -0
- package/dist/core/validators/object/no-null-values.js +44 -0
- package/dist/core/validators/object/no-undefined-values.js +44 -0
- package/dist/core/validators/object/non-empty-object.js +40 -0
- package/dist/core/validators/object/only-keys.js +43 -0
- package/dist/core/validators/object/plain-object.js +35 -0
- package/dist/core/validators/string/alpha-num.js +50 -0
- package/dist/core/validators/string/alpha.js +51 -0
- package/dist/core/validators/string/chars-equal.js +49 -0
- package/dist/core/validators/string/ends-with.js +50 -0
- package/dist/core/validators/string/is-ascii.js +53 -0
- package/dist/core/validators/string/is-printable.js +53 -0
- package/dist/core/validators/string/matches.js +50 -0
- package/dist/core/validators/string/max-length.js +50 -0
- package/dist/core/validators/string/min-length.js +50 -0
- package/dist/core/validators/string/no-lead-space.js +50 -0
- package/dist/core/validators/string/no-repeat-space.js +52 -0
- package/dist/core/validators/string/no-space.js +51 -0
- package/dist/core/validators/string/no-trail-space.js +50 -0
- package/dist/core/validators/string/non-empty.js +48 -0
- package/dist/core/validators/string/not-one-of.js +51 -0
- package/dist/core/validators/string/num-string.js +50 -0
- package/dist/core/validators/string/one-of.js +50 -0
- package/dist/core/validators/string/starts-with.js +50 -0
- package/dist/core/validators/string/string.js +39 -0
- package/dist/core/validators/string/trimmed.js +51 -0
- package/dist/index.js +26 -0
- package/dist/test.js +12 -0
- package/package.json +1 -1
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ----------------------------------------------------------------------------
|
|
3
|
+
* Common | Policy
|
|
4
|
+
* ----------------------------------------------------------------------------
|
|
5
|
+
* @package @clementine-solutions/jane
|
|
6
|
+
* @description Core types and helpers for defining, normalizing, and
|
|
7
|
+
* resolving policies that guide how Jane interprets events
|
|
8
|
+
* across all pipeline stages.
|
|
9
|
+
* @see https://jane-io.com
|
|
10
|
+
* ----------------------------------------------------------------------------
|
|
11
|
+
*/
|
|
12
|
+
import { createEvent, getCompiledWildcard, matchesWildcard } from '.';
|
|
13
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
14
|
+
|* Apply Escalate *|
|
|
15
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
16
|
+
/**
|
|
17
|
+
* Raises an event’s severity by the given number of levels.
|
|
18
|
+
*
|
|
19
|
+
* Escalation moves the severity index upward and clamps it to the valid
|
|
20
|
+
* range. This helper is used by policy transforms to harden events without
|
|
21
|
+
* altering their underlying meaning or code.
|
|
22
|
+
*/
|
|
23
|
+
export function applyEscalate(kind, levels) {
|
|
24
|
+
const idx = severityIndex(kind);
|
|
25
|
+
const next = clampSeverityIndex(idx + levels);
|
|
26
|
+
return SEVERITY_ORDER[next];
|
|
27
|
+
}
|
|
28
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
29
|
+
|* Apply Override *|
|
|
30
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
31
|
+
/**
|
|
32
|
+
* Lowers an event’s severity by the given number of levels.
|
|
33
|
+
*
|
|
34
|
+
* Override moves the severity index downward and clamps it to the valid
|
|
35
|
+
* range. This helper is used by policy transforms to soften events without
|
|
36
|
+
* changing their underlying meaning or code.
|
|
37
|
+
*/
|
|
38
|
+
export function applyOverride(kind, levels) {
|
|
39
|
+
const idx = severityIndex(kind);
|
|
40
|
+
const next = clampSeverityIndex(idx - levels);
|
|
41
|
+
return SEVERITY_ORDER[next];
|
|
42
|
+
}
|
|
43
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
44
|
+
|* Clamp Severity Index *|
|
|
45
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
46
|
+
/**
|
|
47
|
+
* Clamps a severity index to the valid range.
|
|
48
|
+
*
|
|
49
|
+
* Severity levels are stored as an ordered list. This helper ensures that
|
|
50
|
+
* arithmetic on the index—such as escalation or override—never produces an
|
|
51
|
+
* out‑of‑bounds value. No interpretation occurs here; it simply enforces the
|
|
52
|
+
* valid numeric range.
|
|
53
|
+
*/
|
|
54
|
+
export function clampSeverityIndex(i) {
|
|
55
|
+
if (i < 0)
|
|
56
|
+
return 0;
|
|
57
|
+
if (i >= SEVERITY_ORDER.length)
|
|
58
|
+
return SEVERITY_ORDER.length - 1;
|
|
59
|
+
return i;
|
|
60
|
+
}
|
|
61
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
62
|
+
|* Compile Severity *|
|
|
63
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
64
|
+
/**
|
|
65
|
+
* Compiles a user‑supplied severity map into override/escalation deltas.
|
|
66
|
+
*
|
|
67
|
+
* Each entry expresses a desired target severity for an event code. This
|
|
68
|
+
* helper compares the target against the baseline severity (“error”) and
|
|
69
|
+
* converts the difference into numeric levels suitable for the override and
|
|
70
|
+
* escalate policy transforms.
|
|
71
|
+
*
|
|
72
|
+
* Negative deltas become override levels; positive deltas become escalation
|
|
73
|
+
* levels. No interpretation occurs here—this function only computes the
|
|
74
|
+
* distance between severities.
|
|
75
|
+
*/
|
|
76
|
+
export function compileSeverityMap(severity) {
|
|
77
|
+
const override = {};
|
|
78
|
+
const escalate = {};
|
|
79
|
+
for (const [code, targetKind] of Object.entries(severity)) {
|
|
80
|
+
const targetIndex = severityIndex(targetKind);
|
|
81
|
+
const currentIndex = severityIndex('error'); // baseline assumption
|
|
82
|
+
const delta = targetIndex - currentIndex;
|
|
83
|
+
if (delta < 0)
|
|
84
|
+
override[code] = Math.abs(delta);
|
|
85
|
+
if (delta > 0)
|
|
86
|
+
escalate[code] = delta;
|
|
87
|
+
}
|
|
88
|
+
return { override, escalate };
|
|
89
|
+
}
|
|
90
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
91
|
+
|* Decide Event *|
|
|
92
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
93
|
+
/**
|
|
94
|
+
* Creates a `decide`‑phase event.
|
|
95
|
+
*
|
|
96
|
+
* Decision events are emitted after validation when policy logic evaluates
|
|
97
|
+
* severity, overrides, escalation, and boundary rules. This helper ensures
|
|
98
|
+
* all decision events share a consistent shape and metadata.
|
|
99
|
+
*/
|
|
100
|
+
export const decideEvent = (kind, code, path, message, userMessage, meta) => createEvent('decide', kind, code, path, message, userMessage, meta);
|
|
101
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
102
|
+
|* Default Policy *|
|
|
103
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
104
|
+
/**
|
|
105
|
+
* The baseline policy used when no user configuration is provided.
|
|
106
|
+
*
|
|
107
|
+
* Moderate mode applies no automatic rejects, reviews, or severity transforms.
|
|
108
|
+
* Scan and analysis features are disabled, and no structural exceptions are
|
|
109
|
+
* granted. This preset defines Jane’s neutral, predictable behavior and serves
|
|
110
|
+
* as the foundation for all policy merging.
|
|
111
|
+
*/
|
|
112
|
+
export const defaultPolicy = {
|
|
113
|
+
mode: 'moderate',
|
|
114
|
+
decide: {
|
|
115
|
+
reject: [],
|
|
116
|
+
review: [],
|
|
117
|
+
warn: [],
|
|
118
|
+
override: {},
|
|
119
|
+
escalate: {},
|
|
120
|
+
},
|
|
121
|
+
analysis: {
|
|
122
|
+
diff: false,
|
|
123
|
+
explain: false,
|
|
124
|
+
replay: false,
|
|
125
|
+
telemetry: false,
|
|
126
|
+
},
|
|
127
|
+
scan: false,
|
|
128
|
+
exception: {
|
|
129
|
+
bigint: false,
|
|
130
|
+
map: false,
|
|
131
|
+
set: false,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
135
|
+
|* Lax Policy *|
|
|
136
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
137
|
+
/**
|
|
138
|
+
* A permissive policy preset optimized for developer‑friendly behavior.
|
|
139
|
+
*
|
|
140
|
+
* Lax mode applies no rejects or reviews, downgrades all severities by one
|
|
141
|
+
* level via a wildcard override, disables scan and analysis features, and
|
|
142
|
+
* allows structural exceptions for bigint, map, and set. This preset favors
|
|
143
|
+
* flexibility and forgiveness during development and experimentation.
|
|
144
|
+
*/
|
|
145
|
+
export const laxPolicy = {
|
|
146
|
+
mode: 'lax',
|
|
147
|
+
scan: false,
|
|
148
|
+
decide: {
|
|
149
|
+
reject: [],
|
|
150
|
+
review: [],
|
|
151
|
+
override: {
|
|
152
|
+
'*': 1,
|
|
153
|
+
},
|
|
154
|
+
escalate: {},
|
|
155
|
+
warn: [],
|
|
156
|
+
},
|
|
157
|
+
analysis: {
|
|
158
|
+
diff: false,
|
|
159
|
+
explain: false,
|
|
160
|
+
replay: false,
|
|
161
|
+
telemetry: false,
|
|
162
|
+
},
|
|
163
|
+
exception: {
|
|
164
|
+
bigint: true,
|
|
165
|
+
map: true,
|
|
166
|
+
set: true,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
170
|
+
|* Boundary Strict Policy *|
|
|
171
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
172
|
+
/**
|
|
173
|
+
* A strict boundary preset that rejects on any issue.
|
|
174
|
+
*
|
|
175
|
+
* This configuration forces boundary acceptance to “strict”, applies a
|
|
176
|
+
* wildcard reject to treat every issue as fatal, and hides nothing from the
|
|
177
|
+
* event or issue streams. It provides the most conservative, compliance‑grade
|
|
178
|
+
* boundary behavior.
|
|
179
|
+
*/
|
|
180
|
+
export const boundaryPolicyStrict = {
|
|
181
|
+
accept: 'strict',
|
|
182
|
+
reject: ['*'], // any issue is a reject
|
|
183
|
+
review: [],
|
|
184
|
+
hideEvents: [], // show everything
|
|
185
|
+
hideIssues: [],
|
|
186
|
+
};
|
|
187
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
188
|
+
|* Boundary Policy Default *|
|
|
189
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
190
|
+
/**
|
|
191
|
+
* The neutral boundary preset used when no boundary configuration is supplied.
|
|
192
|
+
*
|
|
193
|
+
* Default mode requires all fields to be accepted, applies no reject or review
|
|
194
|
+
* patterns, and hides nothing from the event or issue streams. It provides a
|
|
195
|
+
* transparent, assumption‑free baseline for boundary evaluation.
|
|
196
|
+
*/
|
|
197
|
+
export const boundaryPolicyDefault = {
|
|
198
|
+
accept: 'all',
|
|
199
|
+
ignore: [],
|
|
200
|
+
hideEvents: [],
|
|
201
|
+
hideIssues: [],
|
|
202
|
+
reject: [],
|
|
203
|
+
review: [],
|
|
204
|
+
};
|
|
205
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
206
|
+
|* Boundary Policy Lax *|
|
|
207
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
208
|
+
/**
|
|
209
|
+
* A permissive boundary preset that accepts if any field succeeds.
|
|
210
|
+
*
|
|
211
|
+
* Lax mode ignores all boundary‑level events, hides both events and issues
|
|
212
|
+
* from the result, and applies no reject or review patterns. It provides a
|
|
213
|
+
* forgiving, low‑visibility boundary configuration suited for exploratory or
|
|
214
|
+
* developer‑focused workflows.
|
|
215
|
+
*/
|
|
216
|
+
export const boundaryPolicyLax = {
|
|
217
|
+
accept: 'any',
|
|
218
|
+
ignore: ['*'], // ignore all events
|
|
219
|
+
hideEvents: ['*'], // hide everything
|
|
220
|
+
hideIssues: ['*'],
|
|
221
|
+
reject: [],
|
|
222
|
+
review: [],
|
|
223
|
+
};
|
|
224
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
225
|
+
|* Strict Policy *|
|
|
226
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
227
|
+
/**
|
|
228
|
+
* A maximum‑scrutiny policy preset for safety‑critical pipelines.
|
|
229
|
+
*
|
|
230
|
+
* Strict mode enables scan, rejects high‑risk conditions outright, escalates
|
|
231
|
+
* all severities by one level, and requires review for large or complex
|
|
232
|
+
* structures. All analysis features are enabled, and no structural exceptions
|
|
233
|
+
* are granted. This preset enforces the most conservative, defensive behavior.
|
|
234
|
+
*/
|
|
235
|
+
export const strictPolicy = {
|
|
236
|
+
mode: 'strict',
|
|
237
|
+
scan: true,
|
|
238
|
+
decide: {
|
|
239
|
+
reject: [
|
|
240
|
+
'value.was.contained',
|
|
241
|
+
'object.has.circular-references',
|
|
242
|
+
'number.not.finite',
|
|
243
|
+
'number.not.number',
|
|
244
|
+
'date.is.invalid',
|
|
245
|
+
'string.has.unsafe-unicode',
|
|
246
|
+
'array.is.deep',
|
|
247
|
+
'object.is.deep',
|
|
248
|
+
],
|
|
249
|
+
review: ['array.is.large', 'object.has.many-keys', 'string.is.long', 'bigint.is.large'],
|
|
250
|
+
escalate: {
|
|
251
|
+
'*': 1,
|
|
252
|
+
},
|
|
253
|
+
override: {},
|
|
254
|
+
warn: [],
|
|
255
|
+
},
|
|
256
|
+
analysis: {
|
|
257
|
+
diff: true,
|
|
258
|
+
explain: true,
|
|
259
|
+
replay: true,
|
|
260
|
+
telemetry: true,
|
|
261
|
+
},
|
|
262
|
+
exception: {
|
|
263
|
+
bigint: false,
|
|
264
|
+
map: false,
|
|
265
|
+
set: false,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
export function mergeBoundaryPolicies(base, ...overrides) {
|
|
269
|
+
let result = { ...(base ?? {}) };
|
|
270
|
+
for (const o of overrides) {
|
|
271
|
+
if (!o)
|
|
272
|
+
continue;
|
|
273
|
+
result = {
|
|
274
|
+
...result,
|
|
275
|
+
// primitives (last one wins)
|
|
276
|
+
...(o.accept !== undefined ? { accept: o.accept } : {}),
|
|
277
|
+
// arrays (concatenate)
|
|
278
|
+
...(o.ignore !== undefined ? { ignore: [...(result.ignore ?? []), ...o.ignore] } : {}),
|
|
279
|
+
...(o.hideEvents !== undefined
|
|
280
|
+
? { hideEvents: [...(result.hideEvents ?? []), ...o.hideEvents] }
|
|
281
|
+
: {}),
|
|
282
|
+
...(o.hideIssues !== undefined
|
|
283
|
+
? { hideIssues: [...(result.hideIssues ?? []), ...o.hideIssues] }
|
|
284
|
+
: {}),
|
|
285
|
+
// ⭐ rules (concatenate)
|
|
286
|
+
...(o.rules !== undefined ? { rules: [...(result.rules ?? []), ...o.rules] } : {}),
|
|
287
|
+
// nested objects (shallow merge)
|
|
288
|
+
...(o.values !== undefined
|
|
289
|
+
? { values: { ...(result.values ?? {}), ...o.values } }
|
|
290
|
+
: {}),
|
|
291
|
+
...(o.metadata !== undefined
|
|
292
|
+
? { metadata: { ...(result.metadata ?? {}), ...o.metadata } }
|
|
293
|
+
: {}),
|
|
294
|
+
// functions (last one wins)
|
|
295
|
+
...(o.values?.transform !== undefined ? { transform: o.values?.transform } : {}),
|
|
296
|
+
...(o.reducer !== undefined ? { reducer: o.reducer } : {}),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return result;
|
|
300
|
+
}
|
|
301
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
302
|
+
|* Normalize Policy *|
|
|
303
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
304
|
+
/**
|
|
305
|
+
* Merges a base policy with user‑supplied input into a final, normalized policy.
|
|
306
|
+
*
|
|
307
|
+
* This function applies field‑by‑field overrides, compiles public severity
|
|
308
|
+
* settings into internal override/escalation maps, and merges reject/review/warn
|
|
309
|
+
* patterns in a predictable order. It preserves all unspecified fields from the
|
|
310
|
+
* base policy and never introduces defaults beyond those already present.
|
|
311
|
+
*/
|
|
312
|
+
export function normalizePolicy(base, input = {}) {
|
|
313
|
+
const next = {
|
|
314
|
+
...base,
|
|
315
|
+
};
|
|
316
|
+
if (input.mode !== undefined) {
|
|
317
|
+
next.mode = input.mode;
|
|
318
|
+
}
|
|
319
|
+
if (input.analysis !== undefined) {
|
|
320
|
+
next.analysis = {
|
|
321
|
+
...next.analysis,
|
|
322
|
+
...input.analysis,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
if (input.scan !== undefined) {
|
|
326
|
+
next.scan = input.scan;
|
|
327
|
+
}
|
|
328
|
+
if (input.exception !== undefined) {
|
|
329
|
+
next.exception = {
|
|
330
|
+
...next.exception,
|
|
331
|
+
...input.exception,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
if (input.boundaryName !== undefined) {
|
|
335
|
+
next.boundaryName = input.boundaryName;
|
|
336
|
+
}
|
|
337
|
+
if (input.pipelineName !== undefined) {
|
|
338
|
+
next.pipelineName = input.pipelineName;
|
|
339
|
+
}
|
|
340
|
+
if (input.severity !== undefined) {
|
|
341
|
+
const compiled = compileSeverityMap(input.severity);
|
|
342
|
+
next.decide = {
|
|
343
|
+
...(next.decide ?? {}),
|
|
344
|
+
override: {
|
|
345
|
+
...(next.decide?.override ?? {}),
|
|
346
|
+
...compiled.override,
|
|
347
|
+
},
|
|
348
|
+
escalate: {
|
|
349
|
+
...(next.decide?.escalate ?? {}),
|
|
350
|
+
...compiled.escalate,
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (input.reject !== undefined || input.review !== undefined || input.warn !== undefined) {
|
|
355
|
+
next.decide = {
|
|
356
|
+
...(next.decide ?? {}),
|
|
357
|
+
reject: [...(next.decide?.reject ?? []), ...(input.reject ?? [])],
|
|
358
|
+
review: [...(next.decide?.review ?? []), ...(input.review ?? [])],
|
|
359
|
+
warn: [...(next.decide?.warn ?? []), ...(input.warn ?? [])],
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
if (input.decide !== undefined) {
|
|
363
|
+
next.decide = {
|
|
364
|
+
...(next.decide ?? {}),
|
|
365
|
+
...input.decide,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
if (input.boundary !== undefined) {
|
|
369
|
+
next.boundary = {
|
|
370
|
+
...(next.boundary ?? {}),
|
|
371
|
+
...input.boundary,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return next;
|
|
375
|
+
}
|
|
376
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
377
|
+
|* Resolve Level *|
|
|
378
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
379
|
+
/**
|
|
380
|
+
* Resolves a numeric level for an event code from a sparse lookup table.
|
|
381
|
+
*
|
|
382
|
+
* Exact matches take priority. If none exist, wildcard patterns in the table
|
|
383
|
+
* are tested in insertion order using their compiled regular expressions.
|
|
384
|
+
* Returns the matched level or `undefined` when no pattern applies.
|
|
385
|
+
*/
|
|
386
|
+
export function resolveLevel(code, table) {
|
|
387
|
+
if (code in table)
|
|
388
|
+
return table[code];
|
|
389
|
+
for (const pattern of Object.keys(table)) {
|
|
390
|
+
if (pattern.includes('*')) {
|
|
391
|
+
const re = getCompiledWildcard(pattern);
|
|
392
|
+
if (re.test(code)) {
|
|
393
|
+
return table[pattern];
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
400
|
+
|* Policy Decision *|
|
|
401
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
402
|
+
/**
|
|
403
|
+
* Applies policy transforms to events and produces the final pipeline decision.
|
|
404
|
+
*
|
|
405
|
+
* This function reshapes events using override, escalation, and warn rules,
|
|
406
|
+
* derives issues from the resulting severities, and evaluates reject/review
|
|
407
|
+
* patterns according to the active policy mode. It returns the full pipeline
|
|
408
|
+
* result with shaped events, extracted issues, and a final accept/review/reject
|
|
409
|
+
* decision. No data is modified—only event interpretation changes.
|
|
410
|
+
*/
|
|
411
|
+
export function policyDecision(ctx, metadata) {
|
|
412
|
+
const { events, policy } = ctx;
|
|
413
|
+
let shapedEvents = [...events];
|
|
414
|
+
if (policy.decide?.override) {
|
|
415
|
+
shapedEvents = shapedEvents.map((ev) => {
|
|
416
|
+
const level = resolveLevel(ev.code, policy.decide.override);
|
|
417
|
+
return level === undefined ? ev : { ...ev, kind: applyOverride(ev.kind, level) };
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
if (policy.decide?.escalate) {
|
|
421
|
+
shapedEvents = shapedEvents.map((ev) => {
|
|
422
|
+
const level = resolveLevel(ev.code, policy.decide.escalate);
|
|
423
|
+
return level === undefined ? ev : { ...ev, kind: applyEscalate(ev.kind, level) };
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
if (policy.decide?.warn) {
|
|
427
|
+
shapedEvents = shapedEvents.map((ev) => {
|
|
428
|
+
if (matchesWildcard(ev.code, policy.decide.warn)) {
|
|
429
|
+
return { ...ev, kind: 'warn' };
|
|
430
|
+
}
|
|
431
|
+
return ev;
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
const shapedIssues = shapedEvents.filter((ev) => (ev.kind === 'error' || ev.kind === 'fatal') &&
|
|
435
|
+
typeof ev.code === 'string' &&
|
|
436
|
+
ev.code.trim().length > 0);
|
|
437
|
+
const rejectPatterns = policy.decide?.reject ?? [];
|
|
438
|
+
const reviewPatterns = policy.decide?.review ?? [];
|
|
439
|
+
let hasRejectEvents = false;
|
|
440
|
+
let hasReviewEvents = false;
|
|
441
|
+
for (const ev of shapedEvents) {
|
|
442
|
+
if (ev.kind === 'fatal') {
|
|
443
|
+
hasRejectEvents = true;
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
if (matchesWildcard(ev.code, rejectPatterns)) {
|
|
447
|
+
hasRejectEvents = true;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (matchesWildcard(ev.code, reviewPatterns)) {
|
|
451
|
+
hasReviewEvents = true;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
let decision;
|
|
455
|
+
switch (policy.mode) {
|
|
456
|
+
case 'strict':
|
|
457
|
+
decision =
|
|
458
|
+
hasRejectEvents || hasReviewEvents || shapedIssues.length > 0
|
|
459
|
+
? { code: 'reject', hasRejectEvents, hasReviewEvents }
|
|
460
|
+
: { code: 'accept', hasRejectEvents, hasReviewEvents };
|
|
461
|
+
break;
|
|
462
|
+
case 'moderate':
|
|
463
|
+
decision = hasRejectEvents
|
|
464
|
+
? { code: 'reject', hasRejectEvents, hasReviewEvents }
|
|
465
|
+
: hasReviewEvents
|
|
466
|
+
? { code: 'review', hasRejectEvents, hasReviewEvents }
|
|
467
|
+
: { code: 'accept', hasRejectEvents, hasReviewEvents };
|
|
468
|
+
break;
|
|
469
|
+
case 'lax':
|
|
470
|
+
default:
|
|
471
|
+
decision = hasRejectEvents
|
|
472
|
+
? { code: 'reject', hasRejectEvents, hasReviewEvents }
|
|
473
|
+
: { code: 'accept', hasRejectEvents, hasReviewEvents };
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
...ctx,
|
|
478
|
+
events: shapedEvents,
|
|
479
|
+
issues: shapedIssues,
|
|
480
|
+
decision,
|
|
481
|
+
metadata,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
485
|
+
|* Resolve Policy *|
|
|
486
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
487
|
+
/**
|
|
488
|
+
* Resolves the effective policy by merging defaults, explicit policy, and
|
|
489
|
+
* fluent‑API overrides.
|
|
490
|
+
*
|
|
491
|
+
* The default and explicit policies are combined first, then builder overrides
|
|
492
|
+
* are applied field‑by‑field with strict precedence. Only fields exposed to the
|
|
493
|
+
* fluent API are overrideable; all other structure is taken directly from the
|
|
494
|
+
* merged base. No inference or normalization occurs here—this function performs
|
|
495
|
+
* a final, literal merge for pipeline execution.
|
|
496
|
+
*/
|
|
497
|
+
export function resolvePolicy(defaultPolicy, janePolicy, overrides) {
|
|
498
|
+
const base = {
|
|
499
|
+
...defaultPolicy,
|
|
500
|
+
...(janePolicy ?? {}),
|
|
501
|
+
};
|
|
502
|
+
const o = overrides ?? {};
|
|
503
|
+
return {
|
|
504
|
+
boundaryName: base.boundaryName,
|
|
505
|
+
pipelineName: base.pipelineName,
|
|
506
|
+
mode: o.mode ?? base.mode,
|
|
507
|
+
boundary: base.boundary,
|
|
508
|
+
decide: base.decide,
|
|
509
|
+
analysis: {
|
|
510
|
+
diff: o.analysis?.diff ?? base.analysis?.diff,
|
|
511
|
+
explain: o.analysis?.explain ?? base.analysis?.explain,
|
|
512
|
+
replay: o.analysis?.replay ?? base.analysis?.replay,
|
|
513
|
+
telemetry: o.analysis?.telemetry ?? base.analysis?.telemetry,
|
|
514
|
+
},
|
|
515
|
+
scan: o.scan ?? base.scan,
|
|
516
|
+
exception: {
|
|
517
|
+
bigint: o.exception?.bigint ?? base.exception?.bigint,
|
|
518
|
+
map: o.exception?.map ?? base.exception?.map,
|
|
519
|
+
set: o.exception?.set ?? base.exception?.set,
|
|
520
|
+
},
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
524
|
+
|* Severity Index *|
|
|
525
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
526
|
+
/**
|
|
527
|
+
* Returns the numeric index for a severity kind.
|
|
528
|
+
*
|
|
529
|
+
* Event kinds are stored in a fixed ordered list. Unknown kinds fall back to
|
|
530
|
+
* index 0 to guarantee a safe, deterministic baseline for all severity math.
|
|
531
|
+
* No interpretation occurs here—this is a simple lookup with a fallback.
|
|
532
|
+
*/
|
|
533
|
+
export function severityIndex(kind) {
|
|
534
|
+
let idx = SEVERITY_ORDER.indexOf(kind);
|
|
535
|
+
if (idx === -1) {
|
|
536
|
+
idx = 0;
|
|
537
|
+
}
|
|
538
|
+
return idx;
|
|
539
|
+
}
|
|
540
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
541
|
+
|* Severity Order *|
|
|
542
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
543
|
+
/**
|
|
544
|
+
* Fixed severity ladder from least to most severe.
|
|
545
|
+
*
|
|
546
|
+
* Policies, escalation math, and explain output rely on this exact ordering.
|
|
547
|
+
* Changing it is a breaking change because severity indices are used
|
|
548
|
+
* throughout the pipeline.
|
|
549
|
+
*/
|
|
550
|
+
export const SEVERITY_ORDER = ['info', 'warn', 'error', 'fatal'];
|