@flaks/onerecord 0.1.2 → 0.2.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/CHANGELOG.md +215 -5
- package/MIGRATING.md +77 -5
- package/README.md +62 -10
- package/dist/booking-flow/index.d.ts +1 -1
- package/dist/booking-flow/index.d.ts.map +1 -1
- package/dist/booking-flow/index.js +1 -1
- package/dist/booking-flow/index.js.map +1 -1
- package/dist/booking-flow/transitions.d.ts +21 -0
- package/dist/booking-flow/transitions.d.ts.map +1 -1
- package/dist/booking-flow/transitions.js +57 -5
- package/dist/booking-flow/transitions.js.map +1 -1
- package/dist/codes/fsu-event-codes.d.ts +6 -6
- package/dist/codes/fsu-event-codes.d.ts.map +1 -1
- package/dist/codes/fsu-event-codes.js +11 -9
- package/dist/codes/fsu-event-codes.js.map +1 -1
- package/dist/dispatch/context-order.d.ts +28 -0
- package/dist/dispatch/context-order.d.ts.map +1 -0
- package/dist/dispatch/context-order.js +21 -0
- package/dist/dispatch/context-order.js.map +1 -0
- package/dist/dispatch/domain-constraints.d.ts +44 -0
- package/dist/dispatch/domain-constraints.d.ts.map +1 -0
- package/dist/dispatch/domain-constraints.js +69 -0
- package/dist/dispatch/domain-constraints.js.map +1 -0
- package/dist/dispatch/field-types.d.ts +30 -0
- package/dist/dispatch/field-types.d.ts.map +1 -0
- package/dist/dispatch/field-types.js +88 -0
- package/dist/dispatch/field-types.js.map +1 -0
- package/dist/dispatch/graph-walk.d.ts +23 -0
- package/dist/dispatch/graph-walk.d.ts.map +1 -0
- package/dist/dispatch/graph-walk.js +206 -0
- package/dist/dispatch/graph-walk.js.map +1 -0
- package/dist/dispatch/index.d.ts +30 -0
- package/dist/dispatch/index.d.ts.map +1 -0
- package/dist/dispatch/index.js +36 -0
- package/dist/dispatch/index.js.map +1 -0
- package/dist/dispatch/iri-canonical.d.ts +28 -0
- package/dist/dispatch/iri-canonical.d.ts.map +1 -0
- package/dist/dispatch/iri-canonical.js +55 -0
- package/dist/dispatch/iri-canonical.js.map +1 -0
- package/dist/error/format.d.ts.map +1 -1
- package/dist/error/format.js +8 -0
- package/dist/error/format.js.map +1 -1
- package/dist/facade.d.ts +42 -0
- package/dist/facade.d.ts.map +1 -1
- package/dist/facade.js +8 -0
- package/dist/facade.js.map +1 -1
- package/dist/factory.d.ts +12 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +10 -1
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/iri/dereference.d.ts +42 -0
- package/dist/iri/dereference.d.ts.map +1 -0
- package/dist/iri/dereference.js +75 -0
- package/dist/iri/dereference.js.map +1 -0
- package/dist/result.d.ts +27 -1
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +9 -0
- package/dist/result.js.map +1 -1
- package/dist/safety/blank-node.d.ts +20 -0
- package/dist/safety/blank-node.d.ts.map +1 -0
- package/dist/safety/blank-node.js +59 -0
- package/dist/safety/blank-node.js.map +1 -0
- package/dist/safety/pre-validate.d.ts.map +1 -1
- package/dist/safety/pre-validate.js +12 -72
- package/dist/safety/pre-validate.js.map +1 -1
- package/dist/safety/walk-graph.d.ts +30 -0
- package/dist/safety/walk-graph.d.ts.map +1 -0
- package/dist/safety/walk-graph.js +100 -0
- package/dist/safety/walk-graph.js.map +1 -0
- package/package.json +3 -4
|
@@ -1,30 +1,32 @@
|
|
|
1
|
-
// Codes
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Codes derived from IATA-Cargo XFSU Status Codes.xlsx via the sibling
|
|
2
|
+
// onerecord-xlsx-tools convert script; pinned by SHA256 of the upstream
|
|
3
|
+
// xlsx blob in test/fixtures/iata/xfsu-status-codes.json. v0.2.0 adopted
|
|
4
|
+
// the upstream 26 codes (Phase 0.2 Outcome C, see spec §2.4); 6 codes
|
|
5
|
+
// changed in each direction vs. the v0.1.x hand-pin.
|
|
4
6
|
export const FSU_EVENT_CODES = Object.freeze({
|
|
5
7
|
ARR: 'ARR',
|
|
6
8
|
AWD: 'AWD',
|
|
7
9
|
AWR: 'AWR',
|
|
8
10
|
BKD: 'BKD',
|
|
9
11
|
CCD: 'CCD',
|
|
10
|
-
CLR: 'CLR',
|
|
11
12
|
CRC: 'CRC',
|
|
12
13
|
DDL: 'DDL',
|
|
13
14
|
DEP: 'DEP',
|
|
14
15
|
DIS: 'DIS',
|
|
15
16
|
DLV: 'DLV',
|
|
17
|
+
DOC: 'DOC',
|
|
18
|
+
DPU: 'DPU',
|
|
19
|
+
FIW: 'FIW',
|
|
16
20
|
FOH: 'FOH',
|
|
17
|
-
|
|
18
|
-
FWB: 'FWB',
|
|
21
|
+
FOW: 'FOW',
|
|
19
22
|
MAN: 'MAN',
|
|
20
23
|
NFD: 'NFD',
|
|
21
|
-
|
|
24
|
+
OCI: 'OCI',
|
|
25
|
+
OSI: 'OSI',
|
|
22
26
|
PRE: 'PRE',
|
|
23
27
|
RCF: 'RCF',
|
|
24
|
-
RCM: 'RCM',
|
|
25
28
|
RCS: 'RCS',
|
|
26
29
|
RCT: 'RCT',
|
|
27
|
-
RCV: 'RCV',
|
|
28
30
|
TFD: 'TFD',
|
|
29
31
|
TGC: 'TGC',
|
|
30
32
|
TRM: 'TRM',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fsu-event-codes.js","sourceRoot":"","sources":["../../src/codes/fsu-event-codes.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"fsu-event-codes.js","sourceRoot":"","sources":["../../src/codes/fsu-event-codes.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,wEAAwE;AACxE,yEAAyE;AACzE,sEAAsE;AACtE,qDAAqD;AACrD,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3C,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;CACF,CAAC,CAAA;AASX,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,OAAO,eAAe,CAAC,OAAO,CAAC,CAAA;AACjC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per JSON-LD 1.1 §3.7, when `@context` is an array, later items
|
|
3
|
+
* override earlier ones for term definitions. The OneRecord spec §7.1
|
|
4
|
+
* doesn't pin the array form explicitly, but consumers relying on the
|
|
5
|
+
* default `assertContextAllowed`'s set semantics would silently accept
|
|
6
|
+
* `["https://onerecord.iata.org/ns/cargo", "https://attacker.com/ns"]`
|
|
7
|
+
* — where the later URL could override cargo term definitions.
|
|
8
|
+
*
|
|
9
|
+
* Closes deviation #10 (v0.2.0 — deferral E). Opt-in via dispatch only:
|
|
10
|
+
* default per-class deserializers preserve v0.1.x set semantics for
|
|
11
|
+
* non-breaking compatibility.
|
|
12
|
+
*/
|
|
13
|
+
export interface ContextOrderResult {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
/** When ok is false, the unallowed last element. */
|
|
16
|
+
lastUnallowed?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Check that the LAST element of an array `@context` is in the
|
|
20
|
+
* allowlist (= the "effective" context for term definitions). String-form
|
|
21
|
+
* `@context` is delegated to `assertContextAllowed` (set semantics);
|
|
22
|
+
* this function only fires for array form.
|
|
23
|
+
*
|
|
24
|
+
* Returns `{ ok: true }` for non-arrays or arrays where the last
|
|
25
|
+
* element is allowed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function checkContextOrder(context: unknown, allowedContexts?: readonly string[]): ContextOrderResult;
|
|
28
|
+
//# sourceMappingURL=context-order.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-order.d.ts","sourceRoot":"","sources":["../../src/dispatch/context-order.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAA;IACX,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,eAAe,GAAE,SAAS,MAAM,EAAqB,GACpD,kBAAkB,CAMpB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ALLOWED_CONTEXTS } from '../context.js';
|
|
2
|
+
/**
|
|
3
|
+
* Check that the LAST element of an array `@context` is in the
|
|
4
|
+
* allowlist (= the "effective" context for term definitions). String-form
|
|
5
|
+
* `@context` is delegated to `assertContextAllowed` (set semantics);
|
|
6
|
+
* this function only fires for array form.
|
|
7
|
+
*
|
|
8
|
+
* Returns `{ ok: true }` for non-arrays or arrays where the last
|
|
9
|
+
* element is allowed.
|
|
10
|
+
*/
|
|
11
|
+
export function checkContextOrder(context, allowedContexts = ALLOWED_CONTEXTS) {
|
|
12
|
+
if (!Array.isArray(context) || context.length === 0)
|
|
13
|
+
return { ok: true };
|
|
14
|
+
const last = context[context.length - 1];
|
|
15
|
+
if (typeof last !== 'string')
|
|
16
|
+
return { ok: true }; // shape error — handled elsewhere
|
|
17
|
+
if (allowedContexts.includes(last))
|
|
18
|
+
return { ok: true };
|
|
19
|
+
return { ok: false, lastUnallowed: last };
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=context-order.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-order.js","sourceRoot":"","sources":["../../src/dispatch/context-order.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAqBhD;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAgB,EAChB,kBAAqC,gBAAgB;IAErD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;IACxE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACxC,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA,CAAC,kCAAkC;IACpF,IAAI,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;IACvD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,CAAA;AAC3C,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-semantic constraints that the v0.1.x Zod schemas mark as
|
|
3
|
+
* optional but spec §5.x requires. Used by `dispatchGraphWalk` to emit
|
|
4
|
+
* `domain_constraint_violation` for missing-but-required fields.
|
|
5
|
+
*
|
|
6
|
+
* Closes deviation #6 PARTIALLY (v0.2 — deferral F):
|
|
7
|
+
* - graph-cardinality (this file) — DONE
|
|
8
|
+
* - AWB number consistency between Waybill ↔ embedded Shipments — v0.3
|
|
9
|
+
* - total-pieces / total-weight sums between Shipment ↔ Pieces — v0.3
|
|
10
|
+
* - reference resolvability for cross-graph IRIs — v0.3
|
|
11
|
+
*
|
|
12
|
+
* v0.2 scope chosen for high spec-fidelity-per-LOC ratio: cardinality
|
|
13
|
+
* is a clear-cut spec rule (`MUST have`) and surfaces meaningful
|
|
14
|
+
* violations early. The other sub-items require either deeper graph
|
|
15
|
+
* traversal (sums) or external resolution (resolvability).
|
|
16
|
+
*
|
|
17
|
+
* Opt-in via the dispatch path; default per-class deserializers stay
|
|
18
|
+
* v0.1.x-permissive.
|
|
19
|
+
*/
|
|
20
|
+
export interface DomainConstraint {
|
|
21
|
+
/** Field that must be present on the root LO. */
|
|
22
|
+
field: string;
|
|
23
|
+
/** Cardinality requirement (`required` = present + non-null). */
|
|
24
|
+
cardinality: 'required';
|
|
25
|
+
/** Spec section reference for the constraint. */
|
|
26
|
+
specRef: string;
|
|
27
|
+
}
|
|
28
|
+
export declare const DOMAIN_CONSTRAINTS: Readonly<Record<string, DomainConstraint[]>>;
|
|
29
|
+
export interface DomainConstraintViolation {
|
|
30
|
+
className: string;
|
|
31
|
+
field: string;
|
|
32
|
+
expected: 'required';
|
|
33
|
+
specRef: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validate root-level cardinality constraints for the given root class.
|
|
37
|
+
* Returns the FIRST missing-required-field violation, or undefined if all
|
|
38
|
+
* required fields are present.
|
|
39
|
+
*
|
|
40
|
+
* Only checks root-level constraints — nested LO-in-LO cardinality is
|
|
41
|
+
* deferred to v0.3 (alongside the other deviation #6 sub-items).
|
|
42
|
+
*/
|
|
43
|
+
export declare function checkDomainConstraints(rootClass: string, root: Record<string, unknown>): DomainConstraintViolation | undefined;
|
|
44
|
+
//# sourceMappingURL=domain-constraints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-constraints.d.ts","sourceRoot":"","sources":["../../src/dispatch/domain-constraints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,iEAAiE;IACjE,WAAW,EAAE,UAAU,CAAA;IACvB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAe1E,CAAA;AAEF,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,UAAU,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,yBAAyB,GAAG,SAAS,CAuBvC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-semantic constraints that the v0.1.x Zod schemas mark as
|
|
3
|
+
* optional but spec §5.x requires. Used by `dispatchGraphWalk` to emit
|
|
4
|
+
* `domain_constraint_violation` for missing-but-required fields.
|
|
5
|
+
*
|
|
6
|
+
* Closes deviation #6 PARTIALLY (v0.2 — deferral F):
|
|
7
|
+
* - graph-cardinality (this file) — DONE
|
|
8
|
+
* - AWB number consistency between Waybill ↔ embedded Shipments — v0.3
|
|
9
|
+
* - total-pieces / total-weight sums between Shipment ↔ Pieces — v0.3
|
|
10
|
+
* - reference resolvability for cross-graph IRIs — v0.3
|
|
11
|
+
*
|
|
12
|
+
* v0.2 scope chosen for high spec-fidelity-per-LOC ratio: cardinality
|
|
13
|
+
* is a clear-cut spec rule (`MUST have`) and surfaces meaningful
|
|
14
|
+
* violations early. The other sub-items require either deeper graph
|
|
15
|
+
* traversal (sums) or external resolution (resolvability).
|
|
16
|
+
*
|
|
17
|
+
* Opt-in via the dispatch path; default per-class deserializers stay
|
|
18
|
+
* v0.1.x-permissive.
|
|
19
|
+
*/
|
|
20
|
+
export const DOMAIN_CONSTRAINTS = Object.freeze({
|
|
21
|
+
Waybill: [
|
|
22
|
+
{
|
|
23
|
+
field: 'shipmentInformation',
|
|
24
|
+
cardinality: 'required',
|
|
25
|
+
specRef: 'data model 3.2 §5.1',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
Shipment: [
|
|
29
|
+
{
|
|
30
|
+
field: 'containedPieces',
|
|
31
|
+
cardinality: 'required',
|
|
32
|
+
specRef: 'data model 3.2 §5.2',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Validate root-level cardinality constraints for the given root class.
|
|
38
|
+
* Returns the FIRST missing-required-field violation, or undefined if all
|
|
39
|
+
* required fields are present.
|
|
40
|
+
*
|
|
41
|
+
* Only checks root-level constraints — nested LO-in-LO cardinality is
|
|
42
|
+
* deferred to v0.3 (alongside the other deviation #6 sub-items).
|
|
43
|
+
*/
|
|
44
|
+
export function checkDomainConstraints(rootClass, root) {
|
|
45
|
+
const constraints = DOMAIN_CONSTRAINTS[rootClass];
|
|
46
|
+
if (!constraints)
|
|
47
|
+
return undefined;
|
|
48
|
+
for (const c of constraints) {
|
|
49
|
+
const v = root[c.field];
|
|
50
|
+
if (v === undefined || v === null) {
|
|
51
|
+
return {
|
|
52
|
+
className: rootClass,
|
|
53
|
+
field: c.field,
|
|
54
|
+
expected: c.cardinality,
|
|
55
|
+
specRef: c.specRef,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(v) && v.length === 0) {
|
|
59
|
+
return {
|
|
60
|
+
className: rootClass,
|
|
61
|
+
field: c.field,
|
|
62
|
+
expected: c.cardinality,
|
|
63
|
+
specRef: c.specRef,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=domain-constraints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-constraints.js","sourceRoot":"","sources":["../../src/dispatch/domain-constraints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAWH,MAAM,CAAC,MAAM,kBAAkB,GAAiD,MAAM,CAAC,MAAM,CAAC;IAC5F,OAAO,EAAE;QACP;YACE,KAAK,EAAE,qBAAqB;YAC5B,WAAW,EAAE,UAAU;YACvB,OAAO,EAAE,qBAAqB;SAC/B;KACF;IACD,QAAQ,EAAE;QACR;YACE,KAAK,EAAE,iBAAiB;YACxB,WAAW,EAAE,UAAU;YACvB,OAAO,EAAE,qBAAqB;SAC/B;KACF;CACF,CAAC,CAAA;AASF;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAAiB,EACjB,IAA6B;IAE7B,MAAM,WAAW,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;IACjD,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAA;IAClC,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;QACvB,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAClC,OAAO;gBACL,SAAS,EAAE,SAAS;gBACpB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,QAAQ,EAAE,CAAC,CAAC,WAAW;gBACvB,OAAO,EAAE,CAAC,CAAC,OAAO;aACnB,CAAA;QACH,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO;gBACL,SAAS,EAAE,SAAS;gBACpB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,QAAQ,EAAE,CAAC,CAAC,WAAW;gBACvB,OAAO,EAAE,CAAC,CAAC,OAAO;aACnB,CAAA;QACH,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static map: '<Class>.<field>' -> expected `@type` of an embedded
|
|
3
|
+
* object or IRI-referenced node. Used by `dispatchGraphWalk` to emit
|
|
4
|
+
* `wrong_type_for_endpoint` when an embedded node's `@type` does not
|
|
5
|
+
* match the field's contract.
|
|
6
|
+
*
|
|
7
|
+
* Coverage: every field in the 32-class data model that points at
|
|
8
|
+
* another logistics-object node (via `safeIri()` reference or
|
|
9
|
+
* embedded array). Scalar fields and external-endpoint URLs
|
|
10
|
+
* (`Subscription.notificationEndpoint`, `ServerInformation.serverEndpoint`)
|
|
11
|
+
* are intentionally omitted — those don't resolve to a OneRecord type.
|
|
12
|
+
*
|
|
13
|
+
* Polymorphic fields are marked with `'*'`. The graph-walker treats
|
|
14
|
+
* `'*'` as "any logistics-object type accepted".
|
|
15
|
+
*
|
|
16
|
+
* Maintenance: when adding a new class or field that holds a
|
|
17
|
+
* cross-reference, also add the expected-type mapping here. The
|
|
18
|
+
* graph-walker only checks mapped fields, so omission is silent —
|
|
19
|
+
* a missing entry means "not validated".
|
|
20
|
+
*/
|
|
21
|
+
export declare const FIELD_TYPES: Readonly<Record<string, string>>;
|
|
22
|
+
/**
|
|
23
|
+
* Look up the expected `@type` for a given (className, fieldName).
|
|
24
|
+
* Returns `undefined` for fields not in the map (= not validated by
|
|
25
|
+
* the graph-walker — either scalar or out-of-scope external URL).
|
|
26
|
+
* Returns `'*'` for polymorphic fields where any logistics-object
|
|
27
|
+
* `@type` is valid.
|
|
28
|
+
*/
|
|
29
|
+
export declare function expectedTypeFor(className: string, fieldName: string): string | undefined;
|
|
30
|
+
//# sourceMappingURL=field-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-types.d.ts","sourceRoot":"","sources":["../../src/dispatch/field-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqEvD,CAAA;AAEF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAExF"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static map: '<Class>.<field>' -> expected `@type` of an embedded
|
|
3
|
+
* object or IRI-referenced node. Used by `dispatchGraphWalk` to emit
|
|
4
|
+
* `wrong_type_for_endpoint` when an embedded node's `@type` does not
|
|
5
|
+
* match the field's contract.
|
|
6
|
+
*
|
|
7
|
+
* Coverage: every field in the 32-class data model that points at
|
|
8
|
+
* another logistics-object node (via `safeIri()` reference or
|
|
9
|
+
* embedded array). Scalar fields and external-endpoint URLs
|
|
10
|
+
* (`Subscription.notificationEndpoint`, `ServerInformation.serverEndpoint`)
|
|
11
|
+
* are intentionally omitted — those don't resolve to a OneRecord type.
|
|
12
|
+
*
|
|
13
|
+
* Polymorphic fields are marked with `'*'`. The graph-walker treats
|
|
14
|
+
* `'*'` as "any logistics-object type accepted".
|
|
15
|
+
*
|
|
16
|
+
* Maintenance: when adding a new class or field that holds a
|
|
17
|
+
* cross-reference, also add the expected-type mapping here. The
|
|
18
|
+
* graph-walker only checks mapped fields, so omission is silent —
|
|
19
|
+
* a missing entry means "not validated".
|
|
20
|
+
*/
|
|
21
|
+
export const FIELD_TYPES = Object.freeze({
|
|
22
|
+
// Access-delegation
|
|
23
|
+
'AccessDelegationRequest.accessDelegation': 'AccessDelegation',
|
|
24
|
+
'AccessDelegation.delegatedTo': 'Party',
|
|
25
|
+
'AccessDelegation.delegatedFrom': 'Party',
|
|
26
|
+
// Account number
|
|
27
|
+
'AccountNumber.issuedBy': 'Organization',
|
|
28
|
+
// Booking ecosystem
|
|
29
|
+
'Booking.forBookingRequest': 'BookingRequest',
|
|
30
|
+
'Booking.forBookingOption': 'BookingOption',
|
|
31
|
+
'BookingOption.forBookingRequest': 'BookingRequest',
|
|
32
|
+
'BookingOption.preferences': 'BookingPreferences',
|
|
33
|
+
'BookingOptionRequest.forBookingOption': 'BookingOption',
|
|
34
|
+
'BookingRequest.requestor': 'Party',
|
|
35
|
+
'BookingRequest.preferences': 'BookingPreferences',
|
|
36
|
+
'BookingSegment.transportMovement': 'TransportMovement',
|
|
37
|
+
'BookingSegment.loadingLocation': 'Location',
|
|
38
|
+
'BookingSegment.unloadingLocation': 'Location',
|
|
39
|
+
'BookingShipment.forShipment': 'Shipment',
|
|
40
|
+
// Change ecosystem
|
|
41
|
+
'Change.hasOperation': 'Operation', // embedded array, not IRI ref
|
|
42
|
+
'ChangeRequest.forLogisticsObject': '*', // polymorphic
|
|
43
|
+
'ChangeRequest.hasChange': 'Change',
|
|
44
|
+
// Handling
|
|
45
|
+
'HandlingService.provider': 'Party',
|
|
46
|
+
'HandlingService.forShipment': 'Shipment',
|
|
47
|
+
// Location ↔ Address
|
|
48
|
+
'Location.address': 'Address',
|
|
49
|
+
// Logistics event
|
|
50
|
+
'LogisticsEvent.eventLocation': 'Location',
|
|
51
|
+
'LogisticsEvent.relatedTransportMovement': 'TransportMovement',
|
|
52
|
+
// Notification
|
|
53
|
+
'Notification.relatedLogisticsObject': '*', // polymorphic
|
|
54
|
+
// Organization / Party
|
|
55
|
+
'Organization.address': 'Address',
|
|
56
|
+
'Organization.contactPersons': 'Person',
|
|
57
|
+
'Party.partyDetails': '*', // polymorphic — Person or Organization
|
|
58
|
+
'Party.accountNumbers': 'AccountNumber',
|
|
59
|
+
// Shipment / Piece
|
|
60
|
+
'Shipment.containedPieces': 'Piece',
|
|
61
|
+
'Shipment.consignee': 'Party',
|
|
62
|
+
'Shipment.shipper': 'Party',
|
|
63
|
+
// Subscription
|
|
64
|
+
'SubscriptionRequest.subscription': 'Subscription',
|
|
65
|
+
'Subscription.topic': '*', // polymorphic — any logistics object class IRI
|
|
66
|
+
'Subscription.subscriber': 'Party',
|
|
67
|
+
// Transport movement
|
|
68
|
+
'TransportMovement.departureLocation': 'Location',
|
|
69
|
+
'TransportMovement.arrivalLocation': 'Location',
|
|
70
|
+
'TransportMovement.movementTimes': 'MovementTime',
|
|
71
|
+
// Verification
|
|
72
|
+
'VerificationRequest.verification': 'Verification',
|
|
73
|
+
'Verification.verifiedObject': '*', // polymorphic
|
|
74
|
+
// Waybill
|
|
75
|
+
'Waybill.shipmentInformation': 'Shipment',
|
|
76
|
+
'Waybill.referredBookingOption': 'BookingOption',
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* Look up the expected `@type` for a given (className, fieldName).
|
|
80
|
+
* Returns `undefined` for fields not in the map (= not validated by
|
|
81
|
+
* the graph-walker — either scalar or out-of-scope external URL).
|
|
82
|
+
* Returns `'*'` for polymorphic fields where any logistics-object
|
|
83
|
+
* `@type` is valid.
|
|
84
|
+
*/
|
|
85
|
+
export function expectedTypeFor(className, fieldName) {
|
|
86
|
+
return FIELD_TYPES[`${className}.${fieldName}`];
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=field-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-types.js","sourceRoot":"","sources":["../../src/dispatch/field-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAqC,MAAM,CAAC,MAAM,CAAC;IACzE,oBAAoB;IACpB,0CAA0C,EAAE,kBAAkB;IAC9D,8BAA8B,EAAE,OAAO;IACvC,gCAAgC,EAAE,OAAO;IAEzC,iBAAiB;IACjB,wBAAwB,EAAE,cAAc;IAExC,oBAAoB;IACpB,2BAA2B,EAAE,gBAAgB;IAC7C,0BAA0B,EAAE,eAAe;IAC3C,iCAAiC,EAAE,gBAAgB;IACnD,2BAA2B,EAAE,oBAAoB;IACjD,uCAAuC,EAAE,eAAe;IACxD,0BAA0B,EAAE,OAAO;IACnC,4BAA4B,EAAE,oBAAoB;IAClD,kCAAkC,EAAE,mBAAmB;IACvD,gCAAgC,EAAE,UAAU;IAC5C,kCAAkC,EAAE,UAAU;IAC9C,6BAA6B,EAAE,UAAU;IAEzC,mBAAmB;IACnB,qBAAqB,EAAE,WAAW,EAAE,8BAA8B;IAClE,kCAAkC,EAAE,GAAG,EAAE,cAAc;IACvD,yBAAyB,EAAE,QAAQ;IAEnC,WAAW;IACX,0BAA0B,EAAE,OAAO;IACnC,6BAA6B,EAAE,UAAU;IAEzC,qBAAqB;IACrB,kBAAkB,EAAE,SAAS;IAE7B,kBAAkB;IAClB,8BAA8B,EAAE,UAAU;IAC1C,yCAAyC,EAAE,mBAAmB;IAE9D,eAAe;IACf,qCAAqC,EAAE,GAAG,EAAE,cAAc;IAE1D,uBAAuB;IACvB,sBAAsB,EAAE,SAAS;IACjC,6BAA6B,EAAE,QAAQ;IACvC,oBAAoB,EAAE,GAAG,EAAE,uCAAuC;IAClE,sBAAsB,EAAE,eAAe;IAEvC,mBAAmB;IACnB,0BAA0B,EAAE,OAAO;IACnC,oBAAoB,EAAE,OAAO;IAC7B,kBAAkB,EAAE,OAAO;IAE3B,eAAe;IACf,kCAAkC,EAAE,cAAc;IAClD,oBAAoB,EAAE,GAAG,EAAE,+CAA+C;IAC1E,yBAAyB,EAAE,OAAO;IAElC,qBAAqB;IACrB,qCAAqC,EAAE,UAAU;IACjD,mCAAmC,EAAE,UAAU;IAC/C,iCAAiC,EAAE,cAAc;IAEjD,eAAe;IACf,kCAAkC,EAAE,cAAc;IAClD,6BAA6B,EAAE,GAAG,EAAE,cAAc;IAElD,UAAU;IACV,6BAA6B,EAAE,UAAU;IACzC,+BAA+B,EAAE,eAAe;CACjD,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,SAAiB;IAClE,OAAO,WAAW,CAAC,GAAG,SAAS,IAAI,SAAS,EAAE,CAAC,CAAA;AACjD,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ParseResult } from '../result.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cross-node graph integrity analysis. Runs as a single walk via
|
|
4
|
+
* `walkGraph`'s visitor; emits at most one of:
|
|
5
|
+
* - `duplicate_id_in_graph` — two distinct nodes share an `@id`.
|
|
6
|
+
* - `missing_id` — a node embedded in a cross-reference position has no `@id`.
|
|
7
|
+
* - `wrong_type_for_endpoint` — a node's `@type` does not match the
|
|
8
|
+
* declared expected type for the field that references it.
|
|
9
|
+
* - `missing_type` — a non-leaf node lacks `@type`.
|
|
10
|
+
* - `iri_not_canonical` — an IRI string is not in canonical form
|
|
11
|
+
* (lowercase scheme/host, no default port; RFC 3987 + spec §3.2).
|
|
12
|
+
*
|
|
13
|
+
* Returns `ok: true` if the graph is structurally sound (per spec §3.2 + §7.1).
|
|
14
|
+
*
|
|
15
|
+
* Does NOT replace per-class Zod schema validation — that runs after this
|
|
16
|
+
* pass succeeds, in `dispatch.deserialize.<Class>`.
|
|
17
|
+
*
|
|
18
|
+
* v0.2: containing-class tracking via path-keyed map of object @types,
|
|
19
|
+
* so `wrong_type_for_endpoint` and `missing_id` checks fire for all
|
|
20
|
+
* embedded references, not just direct children of the root.
|
|
21
|
+
*/
|
|
22
|
+
export declare function dispatchGraphWalk(input: unknown, rootClass: string): ParseResult<true>;
|
|
23
|
+
//# sourceMappingURL=graph-walk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-walk.d.ts","sourceRoot":"","sources":["../../src/dispatch/graph-walk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,cAAc,CAAA;AAO3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAmItF"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { walkGraph } from '../safety/walk-graph.js';
|
|
2
|
+
import { checkContextOrder } from './context-order.js';
|
|
3
|
+
import { checkDomainConstraints } from './domain-constraints.js';
|
|
4
|
+
import { expectedTypeFor } from './field-types.js';
|
|
5
|
+
import { checkIriCanonical, looksLikeIri } from './iri-canonical.js';
|
|
6
|
+
/**
|
|
7
|
+
* Cross-node graph integrity analysis. Runs as a single walk via
|
|
8
|
+
* `walkGraph`'s visitor; emits at most one of:
|
|
9
|
+
* - `duplicate_id_in_graph` — two distinct nodes share an `@id`.
|
|
10
|
+
* - `missing_id` — a node embedded in a cross-reference position has no `@id`.
|
|
11
|
+
* - `wrong_type_for_endpoint` — a node's `@type` does not match the
|
|
12
|
+
* declared expected type for the field that references it.
|
|
13
|
+
* - `missing_type` — a non-leaf node lacks `@type`.
|
|
14
|
+
* - `iri_not_canonical` — an IRI string is not in canonical form
|
|
15
|
+
* (lowercase scheme/host, no default port; RFC 3987 + spec §3.2).
|
|
16
|
+
*
|
|
17
|
+
* Returns `ok: true` if the graph is structurally sound (per spec §3.2 + §7.1).
|
|
18
|
+
*
|
|
19
|
+
* Does NOT replace per-class Zod schema validation — that runs after this
|
|
20
|
+
* pass succeeds, in `dispatch.deserialize.<Class>`.
|
|
21
|
+
*
|
|
22
|
+
* v0.2: containing-class tracking via path-keyed map of object @types,
|
|
23
|
+
* so `wrong_type_for_endpoint` and `missing_id` checks fire for all
|
|
24
|
+
* embedded references, not just direct children of the root.
|
|
25
|
+
*/
|
|
26
|
+
export function dispatchGraphWalk(input, rootClass) {
|
|
27
|
+
// context_order_violation (deviation #10 closure, deferral E): if the
|
|
28
|
+
// root @context is an array, verify the LAST element is allowed.
|
|
29
|
+
// Per JSON-LD 1.1 §3.7 later items override earlier ones for term
|
|
30
|
+
// definitions; the last item is the effective cargo-term context.
|
|
31
|
+
// String-form @context is delegated to assertContextAllowed (already
|
|
32
|
+
// runs in the per-class deserializer).
|
|
33
|
+
if (typeof input === 'object' && input !== null && '@context' in input) {
|
|
34
|
+
const ctx = input['@context'];
|
|
35
|
+
const orderR = checkContextOrder(ctx);
|
|
36
|
+
if (!orderR.ok && orderR.lastUnallowed !== undefined) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
error: {
|
|
40
|
+
kind: 'context_order_violation',
|
|
41
|
+
got: ctx,
|
|
42
|
+
lastUnallowed: orderR.lastUnallowed,
|
|
43
|
+
path: '$["@context"]',
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// domain_constraint_violation (deviation #6 partial closure, deferral F):
|
|
49
|
+
// root-level cardinality constraints from spec §5.x. Today: Waybill
|
|
50
|
+
// requires shipmentInformation; Shipment requires containedPieces.
|
|
51
|
+
// v0.3 expands with AWB consistency, total-pieces/weight sums, and
|
|
52
|
+
// reference resolvability.
|
|
53
|
+
if (typeof input === 'object' && input !== null && !Array.isArray(input)) {
|
|
54
|
+
const violation = checkDomainConstraints(rootClass, input);
|
|
55
|
+
if (violation) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
error: {
|
|
59
|
+
kind: 'domain_constraint_violation',
|
|
60
|
+
className: violation.className,
|
|
61
|
+
field: violation.field,
|
|
62
|
+
expected: violation.expected,
|
|
63
|
+
specRef: violation.specRef,
|
|
64
|
+
path: `$.${violation.field}`,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const seen = new Map(); // @id -> first path seen
|
|
70
|
+
// Path-keyed @type lookup: maps each object node's path to its declared
|
|
71
|
+
// (or root-defaulted) @type. Used to resolve the containing class when
|
|
72
|
+
// checking wrong_type_for_endpoint / missing_id at any depth.
|
|
73
|
+
const pathToType = new Map();
|
|
74
|
+
pathToType.set('$', rootClass);
|
|
75
|
+
let firstError;
|
|
76
|
+
const walkR = walkGraph(input, {}, (node, path, _depth) => {
|
|
77
|
+
if (firstError)
|
|
78
|
+
return; // short-circuit on first error
|
|
79
|
+
// iri_not_canonical: check every IRI-shaped string value in the
|
|
80
|
+
// graph (deviation #9 closure, deferral D). Runs before the
|
|
81
|
+
// object-only checks below because string values are visited
|
|
82
|
+
// separately by walkGraph.
|
|
83
|
+
if (typeof node === 'string' && looksLikeIri(node)) {
|
|
84
|
+
const r = checkIriCanonical(node);
|
|
85
|
+
if (!r.canonical && r.reason) {
|
|
86
|
+
firstError = {
|
|
87
|
+
kind: 'iri_not_canonical',
|
|
88
|
+
iri: node,
|
|
89
|
+
reason: r.reason,
|
|
90
|
+
path,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (typeof node !== 'object' || node === null || Array.isArray(node))
|
|
96
|
+
return;
|
|
97
|
+
const obj = node;
|
|
98
|
+
const id = typeof obj['@id'] === 'string' ? obj['@id'] : undefined;
|
|
99
|
+
const type = typeof obj['@type'] === 'string' ? obj['@type'] : undefined;
|
|
100
|
+
// Record this object's @type by path so children can find their
|
|
101
|
+
// containing class. Falls back to rootClass for the root path.
|
|
102
|
+
if (type)
|
|
103
|
+
pathToType.set(path, type);
|
|
104
|
+
// missing_type: every non-leaf node must declare @type
|
|
105
|
+
if (!type && hasObjectChildren(obj)) {
|
|
106
|
+
firstError = { kind: 'missing_type', path };
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const parentField = pathToParentField(path);
|
|
110
|
+
const containingClass = parentField ? containingClassFor(path, pathToType) : undefined;
|
|
111
|
+
// missing_id: nodes embedded in cross-reference positions must have @id.
|
|
112
|
+
// Cross-reference positions are fields in FIELD_TYPES for the containing class.
|
|
113
|
+
if (parentField && containingClass && !id && requiresId(parentField, containingClass)) {
|
|
114
|
+
firstError = {
|
|
115
|
+
kind: 'missing_id',
|
|
116
|
+
objectType: type ?? 'unknown',
|
|
117
|
+
path,
|
|
118
|
+
};
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// duplicate_id_in_graph
|
|
122
|
+
if (id) {
|
|
123
|
+
if (seen.has(id)) {
|
|
124
|
+
firstError = { kind: 'duplicate_id_in_graph', iri: id, path };
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
seen.set(id, path);
|
|
128
|
+
}
|
|
129
|
+
// wrong_type_for_endpoint: embedded @type must match the field's
|
|
130
|
+
// expected class. Now checked at any depth — containing class
|
|
131
|
+
// resolved via the path-keyed @type map.
|
|
132
|
+
if (type && parentField && containingClass) {
|
|
133
|
+
const expected = expectedTypeFor(containingClass, parentField);
|
|
134
|
+
if (expected && expected !== '*' && expected !== type) {
|
|
135
|
+
firstError = {
|
|
136
|
+
kind: 'wrong_type_for_endpoint',
|
|
137
|
+
expected,
|
|
138
|
+
got: type,
|
|
139
|
+
path,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
if (!walkR.ok)
|
|
145
|
+
return walkR; // safety-limit error
|
|
146
|
+
if (firstError)
|
|
147
|
+
return { ok: false, error: firstError };
|
|
148
|
+
return { ok: true, value: true };
|
|
149
|
+
}
|
|
150
|
+
function hasObjectChildren(obj) {
|
|
151
|
+
for (const v of Object.values(obj)) {
|
|
152
|
+
if (typeof v === 'object' && v !== null)
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Path format: `$`, `$.<field>`, `$.<field>[<n>]`, `$.<field>.<sub>`,
|
|
159
|
+
* `$.<field>[<n>].<sub>`. Returns the immediate parent field name.
|
|
160
|
+
* Returns `undefined` for the root path `$`.
|
|
161
|
+
*/
|
|
162
|
+
function pathToParentField(path) {
|
|
163
|
+
if (path === '$')
|
|
164
|
+
return undefined;
|
|
165
|
+
const last = path.split('.').pop();
|
|
166
|
+
if (!last)
|
|
167
|
+
return undefined;
|
|
168
|
+
return last.replace(/\[\d+\]$/, ''); // strip array index suffix
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Resolve the containing class for the node at `path` by looking up the
|
|
172
|
+
* @type recorded at the parent object's path. Skips array-index segments
|
|
173
|
+
* (those don't carry their own @type).
|
|
174
|
+
*
|
|
175
|
+
* Examples (rootClass = 'Waybill'):
|
|
176
|
+
* - `$.shipmentInformation` → parent path `$` → 'Waybill'
|
|
177
|
+
* - `$.shipmentInformation.consignee` → parent path `$.shipmentInformation`
|
|
178
|
+
* → whatever @type was declared on that node (typically 'Shipment')
|
|
179
|
+
* - `$.containedPieces[0]` → strip `[0]` then `.containedPieces` → '$' → 'Waybill'
|
|
180
|
+
* (the array container itself isn't an object node so we step past it)
|
|
181
|
+
* - `$.containedPieces[0].dimensions` → parent path `$.containedPieces[0]`
|
|
182
|
+
* → @type at that path (typically 'Piece')
|
|
183
|
+
*/
|
|
184
|
+
function containingClassFor(path, pathToType) {
|
|
185
|
+
if (path === '$')
|
|
186
|
+
return undefined;
|
|
187
|
+
// Strip the trailing `.field` or `[N]` segment to get the parent path.
|
|
188
|
+
const stripped = path.replace(/\[\d+\]$/, '');
|
|
189
|
+
const lastDot = stripped.lastIndexOf('.');
|
|
190
|
+
if (lastDot < 0)
|
|
191
|
+
return undefined;
|
|
192
|
+
let parentPath = stripped.slice(0, lastDot);
|
|
193
|
+
if (parentPath === '')
|
|
194
|
+
parentPath = '$';
|
|
195
|
+
return pathToType.get(parentPath);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* A field requires `@id` on referenced nodes if it's in `FIELD_TYPES`
|
|
199
|
+
* for the given containing class (= the field points at another
|
|
200
|
+
* logistics-object node). Scalar fields and unmapped fields don't
|
|
201
|
+
* require `@id`.
|
|
202
|
+
*/
|
|
203
|
+
function requiresId(parentField, containingClass) {
|
|
204
|
+
return expectedTypeFor(containingClass, parentField) !== undefined;
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=graph-walk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-walk.js","sourceRoot":"","sources":["../../src/dispatch/graph-walk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ClassName } from '../factory-classes.js';
|
|
2
|
+
import type { ParseResult } from '../result.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build the per-class deserialize map by wrapping each module's
|
|
5
|
+
* `deserialize<ClassName>` function with the graph-walk pre-pass.
|
|
6
|
+
*
|
|
7
|
+
* Failure of the graph-walk pre-pass returns the underlying ParseError
|
|
8
|
+
* untouched; per-class Zod validation only runs if the pre-pass passes.
|
|
9
|
+
*/
|
|
10
|
+
type DispatchDeserializeMap = {
|
|
11
|
+
readonly [C in ClassName]: (raw: unknown) => ParseResult<unknown>;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Opt-in dispatch facade per spec §13. Use:
|
|
15
|
+
* import { dispatch } from '@flaks/onerecord'
|
|
16
|
+
* const r = dispatch.deserialize.Waybill(input)
|
|
17
|
+
*
|
|
18
|
+
* Each per-class deserializer runs the graph-walk pre-pass before
|
|
19
|
+
* delegating to the per-class Zod schema. The default per-class
|
|
20
|
+
* deserializers (`WaybillCodec.deserialize`, `onerecord.deserialize.Waybill`)
|
|
21
|
+
* are unchanged — graph-walk is opt-in.
|
|
22
|
+
*
|
|
23
|
+
* For state-bound graph-walk (per-call limits, IRI strategy etc.), use
|
|
24
|
+
* `createMapper({ graphWalk: true })` instead.
|
|
25
|
+
*/
|
|
26
|
+
export declare const dispatch: Readonly<{
|
|
27
|
+
deserialize: Readonly<DispatchDeserializeMap>;
|
|
28
|
+
}>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dispatch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG/C;;;;;;GAMG;AACH,KAAK,sBAAsB,GAAG;IAC5B,QAAQ,EAAE,CAAC,IAAI,SAAS,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,WAAW,CAAC,OAAO,CAAC;CAClE,CAAA;AAuBD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ;;EAEnB,CAAA"}
|