@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,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ----------------------------------------------------------------------------
|
|
3
|
+
* Boundary Rules | Require All
|
|
4
|
+
* ----------------------------------------------------------------------------
|
|
5
|
+
* @package @clementine-solutions/jane
|
|
6
|
+
* @description Requires that all specified fields be present and accepted.
|
|
7
|
+
* Missing or rejected fields each produce a boundary error.
|
|
8
|
+
* @see https://jane-io.com
|
|
9
|
+
* ----------------------------------------------------------------------------
|
|
10
|
+
*/
|
|
11
|
+
import { rootPath } from '../field-path';
|
|
12
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
13
|
+
|* Implementation *|
|
|
14
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
15
|
+
/**
|
|
16
|
+
* Requires that all specified fields be present and accepted. Any missing or
|
|
17
|
+
* rejected field results in a boundary‑level error for that key.
|
|
18
|
+
*/
|
|
19
|
+
export function requireAll(...keys) {
|
|
20
|
+
return ({ fields }) => {
|
|
21
|
+
const events = [];
|
|
22
|
+
const issues = [];
|
|
23
|
+
for (const key of keys) {
|
|
24
|
+
const r = fields[key];
|
|
25
|
+
if (!r || r.decision?.code !== 'accept') {
|
|
26
|
+
const ev = {
|
|
27
|
+
phase: 'decide',
|
|
28
|
+
kind: 'error',
|
|
29
|
+
code: 'boundary.does.require-all',
|
|
30
|
+
message: `Field "${key}" is required`,
|
|
31
|
+
path: rootPath(key),
|
|
32
|
+
};
|
|
33
|
+
events.push(ev);
|
|
34
|
+
issues.push(ev);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { events, issues };
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ----------------------------------------------------------------------------
|
|
3
|
+
* Boundary Rules | Require One
|
|
4
|
+
* ----------------------------------------------------------------------------
|
|
5
|
+
* @package @clementine-solutions/jane
|
|
6
|
+
* @description Requires that at least one of the specified fields be
|
|
7
|
+
* accepted. If none are present, a single boundary error is
|
|
8
|
+
* emitted.
|
|
9
|
+
* @see https://jane-io.com
|
|
10
|
+
* ----------------------------------------------------------------------------
|
|
11
|
+
*/
|
|
12
|
+
import { rootPath } from '../field-path';
|
|
13
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
14
|
+
|* Implementation *|
|
|
15
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
16
|
+
/**
|
|
17
|
+
* Requires that at least one of the specified fields be present and accepted.
|
|
18
|
+
* If none are provided, a single boundary‑level error is emitted.
|
|
19
|
+
*/
|
|
20
|
+
export function requireOne(...keys) {
|
|
21
|
+
return ({ fields }) => {
|
|
22
|
+
const events = [];
|
|
23
|
+
const issues = [];
|
|
24
|
+
const ok = keys.some((key) => {
|
|
25
|
+
const r = fields[key];
|
|
26
|
+
return r && r.decision?.code === 'accept';
|
|
27
|
+
});
|
|
28
|
+
if (!ok) {
|
|
29
|
+
const ev = {
|
|
30
|
+
phase: 'decide',
|
|
31
|
+
kind: 'error',
|
|
32
|
+
code: 'boundary.does.require-one',
|
|
33
|
+
message: `At least one of [${keys.join(', ')}] is required`,
|
|
34
|
+
path: rootPath(),
|
|
35
|
+
};
|
|
36
|
+
events.push(ev);
|
|
37
|
+
issues.push(ev);
|
|
38
|
+
}
|
|
39
|
+
return { events, issues };
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ----------------------------------------------------------------------------
|
|
3
|
+
* Common | Events
|
|
4
|
+
* ----------------------------------------------------------------------------
|
|
5
|
+
* @package @clementine-solutions/jane
|
|
6
|
+
* @description Shared event types, factories, and helpers used across all
|
|
7
|
+
* pipeline stages. These modules define how Jane records and
|
|
8
|
+
* communicates observable facts during execution.
|
|
9
|
+
* @see https://jane-io.com
|
|
10
|
+
* ----------------------------------------------------------------------------
|
|
11
|
+
*/
|
|
12
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
13
|
+
|* Create Event *|
|
|
14
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
15
|
+
/**
|
|
16
|
+
* Creates a frozen JaneEvent with consistent metadata.
|
|
17
|
+
*
|
|
18
|
+
* This factory centralizes event construction so all stages emit the same
|
|
19
|
+
* shape: stable identifiers, optional context, and a metadata block that
|
|
20
|
+
* tooling can rely on. Callers provide only the facts; the factory attaches
|
|
21
|
+
* timestamps and prepares the object for safe sharing across the pipeline.
|
|
22
|
+
*
|
|
23
|
+
* The returned event is immutable to prevent accidental mutation during
|
|
24
|
+
* scan, parse, normalize, validate, boundary, or policy execution.
|
|
25
|
+
*/
|
|
26
|
+
export function createEvent(phase, kind, code, path, message, userMessage, meta) {
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
phase,
|
|
29
|
+
kind,
|
|
30
|
+
code,
|
|
31
|
+
path,
|
|
32
|
+
message,
|
|
33
|
+
userMessage,
|
|
34
|
+
metadata: {
|
|
35
|
+
...meta,
|
|
36
|
+
runId: undefined,
|
|
37
|
+
createdAt: Date.now(),
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
42
|
+
|* Is Jane Event *|
|
|
43
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
44
|
+
/**
|
|
45
|
+
* Structural type guard for JaneEvent.
|
|
46
|
+
*
|
|
47
|
+
* This check verifies that a value has the minimal shape required for an
|
|
48
|
+
* event: a valid phase, kind, code, and optional context fields. It does not
|
|
49
|
+
* validate semantics or metadata—only that the object is structurally safe to
|
|
50
|
+
* treat as a JaneEvent inside the pipeline.
|
|
51
|
+
*
|
|
52
|
+
* The guard is intentionally strict and literal. It accepts only the phases
|
|
53
|
+
* and kinds defined here, ensuring that stray or misspelled values cannot
|
|
54
|
+
* enter policy, explain, or replay logic.
|
|
55
|
+
*/
|
|
56
|
+
export function isJaneEvent(value) {
|
|
57
|
+
if (typeof value !== 'object' || value === null)
|
|
58
|
+
return false;
|
|
59
|
+
const item = value;
|
|
60
|
+
const path = item['path'];
|
|
61
|
+
const kind = item['kind'];
|
|
62
|
+
const phase = item['phase'];
|
|
63
|
+
const kindIsValid = kind === 'info' || kind === 'warn' || kind === 'error' || kind === 'fatal';
|
|
64
|
+
const phaseIsValid = phase === 'scan' ||
|
|
65
|
+
phase === 'normalize' ||
|
|
66
|
+
phase === 'validate' ||
|
|
67
|
+
phase === 'parse' ||
|
|
68
|
+
phase === 'decide';
|
|
69
|
+
const pathIsValid = path === undefined ||
|
|
70
|
+
(typeof path === 'object' &&
|
|
71
|
+
path !== null &&
|
|
72
|
+
'segments' in path &&
|
|
73
|
+
Array.isArray(path['segments']));
|
|
74
|
+
const messageIsValid = item['message'] === undefined || typeof item['message'] === 'string';
|
|
75
|
+
return (typeof phase === 'string' &&
|
|
76
|
+
phaseIsValid &&
|
|
77
|
+
typeof kind === 'string' &&
|
|
78
|
+
kindIsValid &&
|
|
79
|
+
typeof item['code'] === 'string' &&
|
|
80
|
+
messageIsValid &&
|
|
81
|
+
pathIsValid);
|
|
82
|
+
}
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ----------------------------------------------------------------------------
|
|
3
|
+
* Common | Fluent
|
|
4
|
+
* ----------------------------------------------------------------------------
|
|
5
|
+
* @package @clementine-solutions/jane
|
|
6
|
+
* @description Internal helpers that build Jane’s fluent API surface.
|
|
7
|
+
* These modules attach parser and validator methods and wrap
|
|
8
|
+
* PipelineBuilder instances into the public pipeline.
|
|
9
|
+
* @see https://jane-io.com
|
|
10
|
+
* ----------------------------------------------------------------------------
|
|
11
|
+
*/
|
|
12
|
+
import { rootPath } from '../field-path';
|
|
13
|
+
import { boundaryRunner, createPipeline } from '../pipeline';
|
|
14
|
+
import { telemetry } from '../analysis';
|
|
15
|
+
import { defaultPolicy } from '../common';
|
|
16
|
+
import { janeRegistry } from '../fluent-registry';
|
|
17
|
+
import { boundaryPolicyDefault, boundaryPolicyLax, boundaryPolicyStrict, mergeBoundaryPolicies, normalizePolicy, resolvePolicy, } from './policy';
|
|
18
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
19
|
+
|* Attach Public Fluent Validators *|
|
|
20
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
21
|
+
/**
|
|
22
|
+
* Attaches public fluent validator methods to a pipeline instance.
|
|
23
|
+
*
|
|
24
|
+
* Each validator name in the registry becomes a fluent method on the public
|
|
25
|
+
* pipeline. Calls delegate to the underlying builder, then wrap the updated
|
|
26
|
+
* builder to produce the next public pipeline in the chain.
|
|
27
|
+
*
|
|
28
|
+
* Methods are defined as non‑enumerable and immutable so the fluent surface
|
|
29
|
+
* stays stable, predictable, and free of accidental overrides.
|
|
30
|
+
*/
|
|
31
|
+
function attachPublicFluentValidators(pub, builder, wrap) {
|
|
32
|
+
for (const name of Object.keys(janeRegistry.validators)) {
|
|
33
|
+
Object.defineProperty(pub, name, {
|
|
34
|
+
enumerable: false,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: false,
|
|
37
|
+
value: (...args) => {
|
|
38
|
+
const next = builder.validate(name, ...args);
|
|
39
|
+
return wrap(next);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
45
|
+
|* Builder *|
|
|
46
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
47
|
+
/**
|
|
48
|
+
* Internal hook used to associate a PipelineBuilder with a PublicPipeline.
|
|
49
|
+
*
|
|
50
|
+
* The unique symbol prevents accidental collisions with user code and keeps
|
|
51
|
+
* the builder hidden from the public API. Only internal helpers read or
|
|
52
|
+
* write this property, and it is never surfaced to consumers.
|
|
53
|
+
*
|
|
54
|
+
* PublicPipelineInternal exists solely to give internal code a typed way to
|
|
55
|
+
* access the builder without widening the public interface.
|
|
56
|
+
*/
|
|
57
|
+
const BUILDER = Symbol('JANE_PIPELINE_BUILDER');
|
|
58
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
59
|
+
|* Attach Fluent Validators *|
|
|
60
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
61
|
+
/**
|
|
62
|
+
* Attaches fluent validator methods directly to a PipelineBuilder.
|
|
63
|
+
*
|
|
64
|
+
* Each validator in the registry becomes a fluent method on the builder.
|
|
65
|
+
* Calls record the validator request on the current builder and return the
|
|
66
|
+
* same builder instance, allowing internal code to compose validation steps
|
|
67
|
+
* without wrapping or producing a new public pipeline.
|
|
68
|
+
*
|
|
69
|
+
* Methods are non‑enumerable and immutable so the builder’s shape stays
|
|
70
|
+
* stable and free of accidental overrides.
|
|
71
|
+
*/
|
|
72
|
+
export function attachFluentValidators(builder) {
|
|
73
|
+
for (const name of Object.keys(janeRegistry.validators)) {
|
|
74
|
+
Object.defineProperty(builder, name, {
|
|
75
|
+
enumerable: false,
|
|
76
|
+
configurable: true,
|
|
77
|
+
writable: false,
|
|
78
|
+
value: (...args) => builder.validate(name, ...args),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
83
|
+
|* Attach Public Fluent Parsers *|
|
|
84
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
85
|
+
/**
|
|
86
|
+
* Attaches public fluent parser methods to a pipeline instance.
|
|
87
|
+
*
|
|
88
|
+
* Each parser name in the registry becomes a fluent method on the public
|
|
89
|
+
* pipeline. Calls delegate to the underlying builder, apply the parser, and
|
|
90
|
+
* wrap the updated builder to produce the next public pipeline in the chain.
|
|
91
|
+
*
|
|
92
|
+
* Methods are defined as non‑enumerable and immutable so the fluent surface
|
|
93
|
+
* stays stable, predictable, and free of accidental overrides.
|
|
94
|
+
*/
|
|
95
|
+
function attachPublicFluentParsers(pub, builder, wrap) {
|
|
96
|
+
for (const name of Object.keys(janeRegistry.parsers)) {
|
|
97
|
+
Object.defineProperty(pub, name, {
|
|
98
|
+
enumerable: false,
|
|
99
|
+
configurable: true,
|
|
100
|
+
writable: false,
|
|
101
|
+
value: (...args) => {
|
|
102
|
+
const next = builder.parse(name, ...args);
|
|
103
|
+
return wrap(next);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
109
|
+
|* Attach Fluent Parsers *|
|
|
110
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
111
|
+
/**
|
|
112
|
+
* Attaches fluent parser methods directly to a PipelineBuilder.
|
|
113
|
+
*
|
|
114
|
+
* Each parser in the registry becomes a fluent method on the builder. Calls
|
|
115
|
+
* record the parser request on the current builder and return the same
|
|
116
|
+
* builder instance, allowing internal code to compose parse steps without
|
|
117
|
+
* wrapping or producing a new public pipeline.
|
|
118
|
+
*
|
|
119
|
+
* Methods are non‑enumerable and immutable so the builder’s shape stays
|
|
120
|
+
* stable and free of accidental overrides.
|
|
121
|
+
*/
|
|
122
|
+
export function attachFluentParsers(builder) {
|
|
123
|
+
for (const name of Object.keys(janeRegistry.parsers)) {
|
|
124
|
+
Object.defineProperty(builder, name, {
|
|
125
|
+
enumerable: false,
|
|
126
|
+
configurable: true,
|
|
127
|
+
writable: false,
|
|
128
|
+
value: (...args) => builder.parse(name, ...args),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
133
|
+
|* Create Jane *|
|
|
134
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
135
|
+
/**
|
|
136
|
+
* Creates a policy‑aware Jane instance.
|
|
137
|
+
*
|
|
138
|
+
* Normalizes the base policy, wraps pipeline builders with the public API,
|
|
139
|
+
* and wires in analysis features (diff, explain, replay, telemetry) according
|
|
140
|
+
* to the active policy. Each fluent call returns a new wrapped builder, keeping
|
|
141
|
+
* the pipeline immutable and policy‑driven end‑to‑end.
|
|
142
|
+
*/
|
|
143
|
+
export function createJane(basePolicyInput) {
|
|
144
|
+
const basePolicy = normalizePolicy(defaultPolicy, basePolicyInput);
|
|
145
|
+
const wrap = (builder) => {
|
|
146
|
+
const core = {
|
|
147
|
+
async run() {
|
|
148
|
+
const result = await builder.run();
|
|
149
|
+
const policy = builder.policy ?? defaultPolicy;
|
|
150
|
+
if (policy.analysis?.telemetry && builder.overrides.telemetrySink) {
|
|
151
|
+
const tel = telemetry({
|
|
152
|
+
boundaryName: policy.boundaryName ?? 'unknown-boundary',
|
|
153
|
+
pipelineName: policy.pipelineName ?? 'unknown-pipeline',
|
|
154
|
+
result,
|
|
155
|
+
});
|
|
156
|
+
builder.overrides.telemetrySink(tel.records);
|
|
157
|
+
}
|
|
158
|
+
return toJaneResult(result);
|
|
159
|
+
},
|
|
160
|
+
named(name) {
|
|
161
|
+
return wrap(builder.named(name));
|
|
162
|
+
},
|
|
163
|
+
userMessage(userMessage) {
|
|
164
|
+
return wrap(builder.userMessage(userMessage));
|
|
165
|
+
},
|
|
166
|
+
parse(ruleOrName, ...args) {
|
|
167
|
+
const next = typeof ruleOrName === 'string'
|
|
168
|
+
? builder.parse(ruleOrName, ...args)
|
|
169
|
+
: builder.parse(ruleOrName);
|
|
170
|
+
return wrap(next);
|
|
171
|
+
},
|
|
172
|
+
validate(ruleOrName, ...args) {
|
|
173
|
+
const next = typeof ruleOrName === 'string'
|
|
174
|
+
? builder.validate(ruleOrName, ...args)
|
|
175
|
+
: builder.validate(ruleOrName);
|
|
176
|
+
return wrap(next);
|
|
177
|
+
},
|
|
178
|
+
scan() {
|
|
179
|
+
return wrap(builder.scan());
|
|
180
|
+
},
|
|
181
|
+
strict() {
|
|
182
|
+
return wrap(builder.strict());
|
|
183
|
+
},
|
|
184
|
+
moderate() {
|
|
185
|
+
return wrap(builder.moderate());
|
|
186
|
+
},
|
|
187
|
+
lax() {
|
|
188
|
+
return wrap(builder.lax());
|
|
189
|
+
},
|
|
190
|
+
withDiff() {
|
|
191
|
+
return wrap(builder.withDiff());
|
|
192
|
+
},
|
|
193
|
+
withExplain() {
|
|
194
|
+
return wrap(builder.withExplain());
|
|
195
|
+
},
|
|
196
|
+
withReplay() {
|
|
197
|
+
return wrap(builder.withReplay());
|
|
198
|
+
},
|
|
199
|
+
withTelemetry(sink) {
|
|
200
|
+
return wrap(builder.withTelemetry(sink));
|
|
201
|
+
},
|
|
202
|
+
withPolicy(input) {
|
|
203
|
+
const merged = normalizePolicy(basePolicy, input);
|
|
204
|
+
return createJane(merged);
|
|
205
|
+
},
|
|
206
|
+
allowBigint() {
|
|
207
|
+
return wrap(builder.allowBigint());
|
|
208
|
+
},
|
|
209
|
+
strictBoundary(shape) {
|
|
210
|
+
const merged = normalizePolicy(basePolicy, {
|
|
211
|
+
boundary: boundaryPolicyStrict,
|
|
212
|
+
});
|
|
213
|
+
return createJane(merged).boundary(shape);
|
|
214
|
+
},
|
|
215
|
+
laxBoundary(shape) {
|
|
216
|
+
const merged = normalizePolicy(basePolicy, {
|
|
217
|
+
boundary: boundaryPolicyLax,
|
|
218
|
+
});
|
|
219
|
+
return createJane(merged).boundary(shape);
|
|
220
|
+
},
|
|
221
|
+
defaultBoundary(shape) {
|
|
222
|
+
const merged = normalizePolicy(basePolicy, {
|
|
223
|
+
boundary: boundaryPolicyDefault,
|
|
224
|
+
});
|
|
225
|
+
return createJane(merged).boundary(shape);
|
|
226
|
+
},
|
|
227
|
+
severity(map) {
|
|
228
|
+
return wrap(builder.withPolicy({ severity: map }));
|
|
229
|
+
},
|
|
230
|
+
reject(...patterns) {
|
|
231
|
+
return wrap(builder.withPolicy({ reject: patterns }));
|
|
232
|
+
},
|
|
233
|
+
review(...patterns) {
|
|
234
|
+
return wrap(builder.withPolicy({ review: patterns }));
|
|
235
|
+
},
|
|
236
|
+
warn(...patterns) {
|
|
237
|
+
return wrap(builder.withPolicy({ warn: patterns }));
|
|
238
|
+
},
|
|
239
|
+
boundaryAccept(mode) {
|
|
240
|
+
return wrap(builder.withPolicy({ boundary: { accept: mode } }));
|
|
241
|
+
},
|
|
242
|
+
boundaryIncludeRejected() {
|
|
243
|
+
return wrap(builder.withPolicy({ boundary: { values: { includeRejected: true } } }));
|
|
244
|
+
},
|
|
245
|
+
boundaryIncludeUndefined() {
|
|
246
|
+
return wrap(builder.withPolicy({ boundary: { values: { includeUndefined: true } } }));
|
|
247
|
+
},
|
|
248
|
+
boundaryTransform(fn) {
|
|
249
|
+
return wrap(builder.withPolicy({ boundary: { values: { transform: fn } } }));
|
|
250
|
+
},
|
|
251
|
+
boundaryIgnore(...patterns) {
|
|
252
|
+
return wrap(builder.withPolicy({ boundary: { ignore: patterns } }));
|
|
253
|
+
},
|
|
254
|
+
boundaryHideEvents(...patterns) {
|
|
255
|
+
return wrap(builder.withPolicy({ boundary: { hideEvents: patterns } }));
|
|
256
|
+
},
|
|
257
|
+
boundaryHideIssues(...patterns) {
|
|
258
|
+
return wrap(builder.withPolicy({ mode: 'lax', boundary: { hideIssues: patterns } }));
|
|
259
|
+
},
|
|
260
|
+
boundaryReducer(fn) {
|
|
261
|
+
return wrap(builder.withPolicy({ boundary: { reducer: fn } }));
|
|
262
|
+
},
|
|
263
|
+
boundaryHideFields() {
|
|
264
|
+
return wrap(builder.withPolicy({ boundary: { metadata: { includeFields: false } } }));
|
|
265
|
+
},
|
|
266
|
+
boundaryHideTimestamps() {
|
|
267
|
+
return wrap(builder.withPolicy({ boundary: { metadata: { includeTimestamps: false } } }));
|
|
268
|
+
},
|
|
269
|
+
boundaryHideRunId() {
|
|
270
|
+
return wrap(builder.withPolicy({ boundary: { metadata: { includeRunId: false } } }));
|
|
271
|
+
},
|
|
272
|
+
boundaryRule(rule) {
|
|
273
|
+
const existing = builder.policy?.boundary?.rules ?? [];
|
|
274
|
+
return wrap(builder.withPolicy({
|
|
275
|
+
boundary: {
|
|
276
|
+
rules: [...existing, rule],
|
|
277
|
+
},
|
|
278
|
+
}));
|
|
279
|
+
},
|
|
280
|
+
boundaryRules(...rules) {
|
|
281
|
+
const existing = builder.policy?.boundary?.rules ?? [];
|
|
282
|
+
return wrap(builder.withPolicy({
|
|
283
|
+
boundary: {
|
|
284
|
+
rules: [...existing, ...rules],
|
|
285
|
+
},
|
|
286
|
+
}));
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
const pub = core;
|
|
290
|
+
const internal = pub;
|
|
291
|
+
internal[BUILDER] = builder;
|
|
292
|
+
attachPublicFluentParsers(pub, builder, wrap);
|
|
293
|
+
attachPublicFluentValidators(pub, builder, wrap);
|
|
294
|
+
return pub;
|
|
295
|
+
};
|
|
296
|
+
return {
|
|
297
|
+
value(raw, path, options) {
|
|
298
|
+
const { inputName } = options ?? {};
|
|
299
|
+
const internal = createPipeline(raw, path ?? rootPath(inputName), [], [], [], basePolicy, { inputName });
|
|
300
|
+
return wrap(internal);
|
|
301
|
+
},
|
|
302
|
+
async boundary(shape) {
|
|
303
|
+
const entries = Object.entries(shape);
|
|
304
|
+
const pipelineResults = await Promise.all(entries.map(async ([key, pub]) => {
|
|
305
|
+
const internal = pub;
|
|
306
|
+
const builder = internal[BUILDER];
|
|
307
|
+
if (!builder) {
|
|
308
|
+
throw new Error(`Internal error: missing builder for field "${key}"`);
|
|
309
|
+
}
|
|
310
|
+
const result = await builder.run();
|
|
311
|
+
return [key, result];
|
|
312
|
+
}));
|
|
313
|
+
const fields = Object.fromEntries(pipelineResults);
|
|
314
|
+
const pipelineBoundaryConfigs = pipelineResults
|
|
315
|
+
.map(([_, result]) => result.policy?.boundary)
|
|
316
|
+
.filter(Boolean);
|
|
317
|
+
const builderBoundaryConfigs = entries
|
|
318
|
+
.map(([_key, pub]) => {
|
|
319
|
+
const internal = pub;
|
|
320
|
+
const b = internal[BUILDER];
|
|
321
|
+
return b?.policy?.boundary;
|
|
322
|
+
})
|
|
323
|
+
.filter(Boolean);
|
|
324
|
+
const mergedBoundary = mergeBoundaryPolicies(basePolicy.boundary, ...builderBoundaryConfigs, ...pipelineBoundaryConfigs);
|
|
325
|
+
const effectivePolicy = resolvePolicy(defaultPolicy, {
|
|
326
|
+
...basePolicy,
|
|
327
|
+
boundary: mergedBoundary,
|
|
328
|
+
}, {});
|
|
329
|
+
const boundary = await boundaryRunner({
|
|
330
|
+
policy: effectivePolicy,
|
|
331
|
+
fields,
|
|
332
|
+
});
|
|
333
|
+
return {
|
|
334
|
+
...toJaneBoundaryResult(boundary, fields),
|
|
335
|
+
policy: effectivePolicy,
|
|
336
|
+
};
|
|
337
|
+
},
|
|
338
|
+
withPolicy(input) {
|
|
339
|
+
const merged = normalizePolicy(basePolicy, input);
|
|
340
|
+
return createJane(merged);
|
|
341
|
+
},
|
|
342
|
+
strictBoundary(shape) {
|
|
343
|
+
const merged = normalizePolicy(basePolicy, {
|
|
344
|
+
boundary: boundaryPolicyStrict,
|
|
345
|
+
});
|
|
346
|
+
return createJane(merged).boundary(shape);
|
|
347
|
+
},
|
|
348
|
+
laxBoundary(shape) {
|
|
349
|
+
const merged = normalizePolicy(basePolicy, {
|
|
350
|
+
boundary: boundaryPolicyLax,
|
|
351
|
+
});
|
|
352
|
+
return createJane(merged).boundary(shape);
|
|
353
|
+
},
|
|
354
|
+
defaultBoundary(shape) {
|
|
355
|
+
const merged = normalizePolicy(basePolicy, {
|
|
356
|
+
boundary: boundaryPolicyDefault,
|
|
357
|
+
});
|
|
358
|
+
return createJane(merged).boundary(shape);
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
363
|
+
|* To Jane Result *|
|
|
364
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
365
|
+
/**
|
|
366
|
+
* Creates a Jane instance with a normalized base policy and the full fluent API.
|
|
367
|
+
*
|
|
368
|
+
* The factory wraps a PipelineBuilder and exposes a stable, user‑facing
|
|
369
|
+
* pipeline surface. Each fluent call produces a new wrapped builder, while
|
|
370
|
+
* root‑level helpers (value, boundary, withPolicy) create new Jane instances
|
|
371
|
+
* with merged policy state.
|
|
372
|
+
*
|
|
373
|
+
* The wrapper isolates internal builder mechanics from consumers, attaches
|
|
374
|
+
* parser and validator methods from the registry, and ensures that telemetry,
|
|
375
|
+
* diff, explain, replay, and boundary behavior all flow through a consistent
|
|
376
|
+
* entry point. All policy resolution happens here so contributors can reason
|
|
377
|
+
* about effective behavior without tracing through individual pipeline steps.
|
|
378
|
+
*/
|
|
379
|
+
export function toJaneResult(r) {
|
|
380
|
+
const issues = r.events.filter((e) => e.kind === 'error' || e.kind === 'fatal');
|
|
381
|
+
const events = r.events.filter((e) => e.kind === 'info' || e.kind === 'warn');
|
|
382
|
+
return {
|
|
383
|
+
ok: issues.length === 0,
|
|
384
|
+
issues: issues.length > 0 ? issues : undefined,
|
|
385
|
+
events: events.length > 0 ? events : undefined,
|
|
386
|
+
value: r.final,
|
|
387
|
+
path: r.path,
|
|
388
|
+
diff: r.diff,
|
|
389
|
+
explain: r.explain,
|
|
390
|
+
replay: r.replay,
|
|
391
|
+
metadata: {
|
|
392
|
+
raw: r.raw,
|
|
393
|
+
rawType: r.rawType,
|
|
394
|
+
safe: r.safe,
|
|
395
|
+
safeType: r.safeType,
|
|
396
|
+
normalized: r.normalized,
|
|
397
|
+
normalizedType: r.normalizedType,
|
|
398
|
+
final: r.final,
|
|
399
|
+
finalType: r.finalType,
|
|
400
|
+
startedAt: r.metadata.startedAt,
|
|
401
|
+
finishedAt: r.metadata.finishedAt,
|
|
402
|
+
durationMs: r.metadata.durationMs,
|
|
403
|
+
inputName: r.metadata.inputName,
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
408
|
+
|* To Jane Boundary Result *|
|
|
409
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
410
|
+
/**
|
|
411
|
+
* Converts an internal BoundaryResult and its field-level PipelineResults
|
|
412
|
+
* into the public JaneBoundaryResult shape.
|
|
413
|
+
*
|
|
414
|
+
* Each pipeline result is first converted to a JaneResult, then assembled
|
|
415
|
+
* alongside the boundary’s own decision, issues, events, values, and metadata.
|
|
416
|
+
* No interpretation or rewriting occurs here—this function preserves the
|
|
417
|
+
* boundary runner’s output exactly while projecting it into the public API.
|
|
418
|
+
*/
|
|
419
|
+
export function toJaneBoundaryResult(boundary, pipelines) {
|
|
420
|
+
const janeResults = Object.fromEntries(Object.entries(pipelines).map(([k, p]) => [k, toJaneResult(p)]));
|
|
421
|
+
return {
|
|
422
|
+
ok: boundary.ok,
|
|
423
|
+
issues: boundary.issues,
|
|
424
|
+
events: boundary.events,
|
|
425
|
+
values: boundary.values,
|
|
426
|
+
fields: janeResults,
|
|
427
|
+
metadata: boundary.metadata,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ----------------------------------------------------------------------------
|
|
3
|
+
* Common | Barrel File
|
|
4
|
+
* ----------------------------------------------------------------------------
|
|
5
|
+
* @package @clementine-solutions/jane
|
|
6
|
+
* @description Re‑exports shared common modules to provide a stable,
|
|
7
|
+
* minimal entry point for internal consumers. This file
|
|
8
|
+
* exposes no logic of its own.
|
|
9
|
+
* @see https://jane-io.com
|
|
10
|
+
* ----------------------------------------------------------------------------
|
|
11
|
+
*/
|
|
12
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
13
|
+
|* Events *|
|
|
14
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
15
|
+
export { createEvent, isJaneEvent } from './events';
|
|
16
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
17
|
+
|* Fluent *|
|
|
18
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
19
|
+
export { attachFluentParsers, attachFluentValidators, createJane, toJaneBoundaryResult, toJaneResult, } from './fluent';
|
|
20
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
21
|
+
|* Policy *|
|
|
22
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
23
|
+
export { applyEscalate, applyOverride, boundaryPolicyDefault, boundaryPolicyLax, boundaryPolicyStrict, clampSeverityIndex, compileSeverityMap, decideEvent, defaultPolicy, laxPolicy, normalizePolicy, mergeBoundaryPolicies, policyDecision, resolveLevel, resolvePolicy, severityIndex, SEVERITY_ORDER, strictPolicy, } from './policy';
|
|
24
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
25
|
+
|* Utilities *|
|
|
26
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
27
|
+
export { deepEqual, generateRunId, isObject, isPlainObject, isPrimitive, safeStringify, } from './utilities';
|
|
28
|
+
/* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— *\
|
|
29
|
+
|* Wildcard *|
|
|
30
|
+
\* ——— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ————— * ——— */
|
|
31
|
+
export { compileWildcard, getCompiledWildcard, matchesWildcard } from './wildcard';
|