@metaobjectsdev/render 0.7.0 → 0.8.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/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/prompt/output-format-renderer.d.ts +8 -0
- package/dist/prompt/output-format-renderer.d.ts.map +1 -0
- package/dist/prompt/output-format-renderer.js +164 -0
- package/dist/prompt/output-format-renderer.js.map +1 -0
- package/dist/prompt/output-format-spec.d.ts +18 -0
- package/dist/prompt/output-format-spec.d.ts.map +1 -0
- package/dist/prompt/output-format-spec.js +3 -0
- package/dist/prompt/output-format-spec.js.map +1 -0
- package/dist/prompt/prompt-field.d.ts +22 -0
- package/dist/prompt/prompt-field.d.ts.map +1 -0
- package/dist/prompt/prompt-field.js +3 -0
- package/dist/prompt/prompt-field.js.map +1 -0
- package/dist/prompt/prompt-overrides.d.ts +16 -0
- package/dist/prompt/prompt-overrides.d.ts.map +1 -0
- package/dist/prompt/prompt-overrides.js +13 -0
- package/dist/prompt/prompt-overrides.js.map +1 -0
- package/dist/prompt/prompt-style.d.ts +17 -0
- package/dist/prompt/prompt-style.d.ts.map +1 -0
- package/dist/prompt/prompt-style.js +34 -0
- package/dist/prompt/prompt-style.js.map +1 -0
- package/dist/recover/coerce.d.ts +5 -0
- package/dist/recover/coerce.d.ts.map +1 -0
- package/dist/recover/coerce.js +124 -0
- package/dist/recover/coerce.js.map +1 -0
- package/dist/recover/json-forgiving-reader.d.ts +5 -0
- package/dist/recover/json-forgiving-reader.d.ts.map +1 -0
- package/dist/recover/json-forgiving-reader.js +178 -0
- package/dist/recover/json-forgiving-reader.js.map +1 -0
- package/dist/recover/locate.d.ts +5 -0
- package/dist/recover/locate.d.ts.map +1 -0
- package/dist/recover/locate.js +75 -0
- package/dist/recover/locate.js.map +1 -0
- package/dist/recover/recover-map.d.ts +7 -0
- package/dist/recover/recover-map.d.ts.map +1 -0
- package/dist/recover/recover-map.js +36 -0
- package/dist/recover/recover-map.js.map +1 -0
- package/dist/recover/recover.d.ts +4 -0
- package/dist/recover/recover.d.ts.map +1 -0
- package/dist/recover/recover.js +115 -0
- package/dist/recover/recover.js.map +1 -0
- package/dist/recover/strip.d.ts +2 -0
- package/dist/recover/strip.d.ts.map +1 -0
- package/dist/recover/strip.js +17 -0
- package/dist/recover/strip.js.map +1 -0
- package/dist/recover/types.d.ts +117 -0
- package/dist/recover/types.d.ts.map +1 -0
- package/dist/recover/types.js +124 -0
- package/dist/recover/types.js.map +1 -0
- package/dist/recover/xml-forgiving-reader.d.ts +2 -0
- package/dist/recover/xml-forgiving-reader.d.ts.map +1 -0
- package/dist/recover/xml-forgiving-reader.js +79 -0
- package/dist/recover/xml-forgiving-reader.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +42 -0
- package/src/prompt/output-format-renderer.ts +179 -0
- package/src/prompt/output-format-spec.ts +20 -0
- package/src/prompt/prompt-field.ts +24 -0
- package/src/prompt/prompt-overrides.ts +27 -0
- package/src/prompt/prompt-style.ts +36 -0
- package/src/recover/KNOWN_GAPS.md +35 -0
- package/src/recover/coerce.ts +141 -0
- package/src/recover/json-forgiving-reader.ts +167 -0
- package/src/recover/locate.ts +72 -0
- package/src/recover/recover-map.ts +39 -0
- package/src/recover/recover.ts +146 -0
- package/src/recover/strip.ts +17 -0
- package/src/recover/types.ts +217 -0
- package/src/recover/xml-forgiving-reader.ts +82 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// FR-010 recover engine — types & model (Tier-2 idiomatic TS port).
|
|
2
|
+
//
|
|
3
|
+
// Cross-port REFERENCE is the Java engine
|
|
4
|
+
// (server/java/render/.../recover/). This file ports the Java records/enums to
|
|
5
|
+
// idiomatic TS: enums become string-union `as const` objects (values match the
|
|
6
|
+
// corpus / Java enum names exactly), records become readonly interfaces +
|
|
7
|
+
// factory functions, and the mutable RecoveryReport is a class.
|
|
8
|
+
/** Output format the dirty text claims to be. Corpus schema.json uses "JSON"/"XML". */
|
|
9
|
+
export const Format = {
|
|
10
|
+
JSON: "JSON",
|
|
11
|
+
XML: "XML",
|
|
12
|
+
};
|
|
13
|
+
/** The coercion target kinds the engine understands. OBJECT = nested RecoverSchema. */
|
|
14
|
+
export const FieldKind = {
|
|
15
|
+
STRING: "STRING",
|
|
16
|
+
INT: "INT",
|
|
17
|
+
LONG: "LONG",
|
|
18
|
+
DOUBLE: "DOUBLE",
|
|
19
|
+
BOOLEAN: "BOOLEAN",
|
|
20
|
+
ENUM: "ENUM",
|
|
21
|
+
OBJECT: "OBJECT",
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* FROZEN cross-port per-field recovery classification. Do not reorder or add
|
|
25
|
+
* without an ADR. These string values are SERIALIZED in the conformance corpus.
|
|
26
|
+
*/
|
|
27
|
+
export const FieldRecovery = {
|
|
28
|
+
RECOVERED: "RECOVERED",
|
|
29
|
+
// DEFAULTED is reserved (a future @default-backed value); the engine does not emit it yet.
|
|
30
|
+
DEFAULTED: "DEFAULTED",
|
|
31
|
+
LOST_OPTIONAL: "LOST_OPTIONAL",
|
|
32
|
+
LOST_REQUIRED: "LOST_REQUIRED",
|
|
33
|
+
MALFORMED: "MALFORMED",
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* STRICT: case-sensitive, minimal repair. NORMAL: case-insensitive keys/tags
|
|
37
|
+
* (default). LOOSE: maximal repair (currently identical to NORMAL — reserved).
|
|
38
|
+
*/
|
|
39
|
+
export const Tolerance = {
|
|
40
|
+
STRICT: "STRICT",
|
|
41
|
+
NORMAL: "NORMAL",
|
|
42
|
+
LOOSE: "LOOSE",
|
|
43
|
+
};
|
|
44
|
+
export function scalar(name, kind, required) {
|
|
45
|
+
return { name, kind, required, array: false, enumValues: null, enumAlias: null, min: null, max: null, nested: null };
|
|
46
|
+
}
|
|
47
|
+
export function enumField(name, required, values, aliases) {
|
|
48
|
+
return {
|
|
49
|
+
name,
|
|
50
|
+
kind: FieldKind.ENUM,
|
|
51
|
+
required,
|
|
52
|
+
array: false,
|
|
53
|
+
enumValues: values == null ? null : [...values],
|
|
54
|
+
enumAlias: aliases == null ? {} : { ...aliases },
|
|
55
|
+
min: null,
|
|
56
|
+
max: null,
|
|
57
|
+
nested: null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function range(name, kind, required, min, max) {
|
|
61
|
+
return { name, kind, required, array: false, enumValues: null, enumAlias: null, min, max, nested: null };
|
|
62
|
+
}
|
|
63
|
+
export function object(name, required, array, nested) {
|
|
64
|
+
return { name, kind: FieldKind.OBJECT, required, array, enumValues: null, enumAlias: null, min: null, max: null, nested };
|
|
65
|
+
}
|
|
66
|
+
export function recoverSchema(format, rootName, fields) {
|
|
67
|
+
return { format, rootName, fields: fields == null ? [] : [...fields] };
|
|
68
|
+
}
|
|
69
|
+
export function defaults() {
|
|
70
|
+
return { tolerance: Tolerance.NORMAL, aliases: {}, normalizers: {}, onField: null };
|
|
71
|
+
}
|
|
72
|
+
/** Normalize a partial / undefined options bag into a complete RecoverOptions. */
|
|
73
|
+
export function normalizeOptions(opts) {
|
|
74
|
+
if (opts == null)
|
|
75
|
+
return defaults();
|
|
76
|
+
return {
|
|
77
|
+
tolerance: opts.tolerance ?? Tolerance.NORMAL,
|
|
78
|
+
aliases: opts.aliases == null ? {} : { ...opts.aliases },
|
|
79
|
+
normalizers: opts.normalizers == null ? {} : { ...opts.normalizers },
|
|
80
|
+
onField: opts.onField ?? null,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Mutable accumulator of per-field recovery classification, the degenerate-response flag, and coercion notes. */
|
|
84
|
+
export class RecoveryReport {
|
|
85
|
+
// Insertion-ordered (Map preserves insertion order, mirroring Java LinkedHashMap).
|
|
86
|
+
_states = new Map();
|
|
87
|
+
_coercions = [];
|
|
88
|
+
_empty = false;
|
|
89
|
+
set(fieldPath, state) {
|
|
90
|
+
this._states.set(fieldPath, state);
|
|
91
|
+
}
|
|
92
|
+
addCoercion(c) {
|
|
93
|
+
this._coercions.push(c);
|
|
94
|
+
}
|
|
95
|
+
markEmpty() {
|
|
96
|
+
this._empty = true;
|
|
97
|
+
}
|
|
98
|
+
isEmpty() {
|
|
99
|
+
return this._empty;
|
|
100
|
+
}
|
|
101
|
+
states() {
|
|
102
|
+
return new Map(this._states);
|
|
103
|
+
}
|
|
104
|
+
coercions() {
|
|
105
|
+
return [...this._coercions];
|
|
106
|
+
}
|
|
107
|
+
lostRequired() {
|
|
108
|
+
return this.byState(FieldRecovery.LOST_REQUIRED);
|
|
109
|
+
}
|
|
110
|
+
malformed() {
|
|
111
|
+
return this.byState(FieldRecovery.MALFORMED);
|
|
112
|
+
}
|
|
113
|
+
hasLostRequired() {
|
|
114
|
+
return this.lostRequired().length > 0;
|
|
115
|
+
}
|
|
116
|
+
byState(s) {
|
|
117
|
+
const out = [];
|
|
118
|
+
for (const [k, v] of this._states)
|
|
119
|
+
if (v === s)
|
|
120
|
+
out.push(k);
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/recover/types.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,0CAA0C;AAC1C,+EAA+E;AAC/E,+EAA+E;AAC/E,0EAA0E;AAC1E,gEAAgE;AAEhE,uFAAuF;AACvF,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;CACF,CAAC;AAGX,uFAAuF;AACvF,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM,EAAE,QAAQ;IAChB,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;CACR,CAAC;AAGX;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,SAAS,EAAE,WAAW;IACtB,2FAA2F;IAC3F,SAAS,EAAE,WAAW;IACtB,aAAa,EAAE,eAAe;IAC9B,aAAa,EAAE,eAAe;IAC9B,SAAS,EAAE,WAAW;CACd,CAAC;AAGX;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;CACN,CAAC;AA2BX,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,IAAe,EAAE,QAAiB;IACrE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACvH,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,QAAiB,EACjB,MAAgC,EAChC,OAAgD;IAEhD,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,QAAQ;QACR,KAAK,EAAE,KAAK;QACZ,UAAU,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;QAC/C,SAAS,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE;QAChD,GAAG,EAAE,IAAI;QACT,GAAG,EAAE,IAAI;QACT,MAAM,EAAE,IAAI;KACb,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,KAAK,CACnB,IAAY,EACZ,IAAe,EACf,QAAiB,EACjB,GAAkB,EAClB,GAAkB;IAElB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC3G,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,QAAiB,EAAE,KAAc,EAAE,MAA4B;IAClG,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC5H,CAAC;AASD,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,QAAgB,EAAE,MAAmC;IACjG,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC;AACzE,CAAC;AAmBD,MAAM,UAAU,QAAQ;IACtB,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACtF,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,IAAqC;IACpE,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,QAAQ,EAAE,CAAC;IACpC,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM;QAC7C,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE;QACxD,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE;QACpE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI;KAC9B,CAAC;AACJ,CAAC;AAcD,kHAAkH;AAClH,MAAM,OAAO,cAAc;IACzB,mFAAmF;IAClE,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC3C,UAAU,GAAe,EAAE,CAAC;IACrC,MAAM,GAAG,KAAK,CAAC;IAEvB,GAAG,CAAC,SAAiB,EAAE,KAAoB;QACzC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,WAAW,CAAC,CAAW;QACrB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,SAAS;QACP,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,SAAS;QACP,OAAO,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9B,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;IACnD,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAC/C,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;IACxC,CAAC;IAEO,OAAO,CAAC,CAAgB;QAC9B,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,KAAK,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5D,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xml-forgiving-reader.d.ts","sourceRoot":"","sources":["../../src/recover/xml-forgiving-reader.ts"],"names":[],"mappings":"AAgBA,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,eAAe,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS1G"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Stage-4 tolerant XML reader for the bounded corpus malformation set. Never throws.
|
|
2
|
+
// Mirrors Java XmlForgivingReader. Must not index-out-of-range on a leading close tag.
|
|
3
|
+
function quote(s) {
|
|
4
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5
|
+
}
|
|
6
|
+
/** Find first regex match at index >= `from` (emulates Java Matcher.find(int)). */
|
|
7
|
+
function matchFrom(source, flags, text, from) {
|
|
8
|
+
const g = new RegExp(source, flags.includes("g") ? flags : flags + "g");
|
|
9
|
+
g.lastIndex = from;
|
|
10
|
+
return g.exec(text);
|
|
11
|
+
}
|
|
12
|
+
const OPEN_TAG_SRC = "<([A-Za-z_][A-Za-z0-9_]*)(\\s[^>]*)?>";
|
|
13
|
+
export function readXml(span, caseInsensitive) {
|
|
14
|
+
const out = {};
|
|
15
|
+
if (span == null || span.trim().length === 0)
|
|
16
|
+
return out;
|
|
17
|
+
const gt = span.indexOf(">");
|
|
18
|
+
if (gt < 0)
|
|
19
|
+
return out;
|
|
20
|
+
const rootEnd = span.lastIndexOf("</");
|
|
21
|
+
const inner = span.substring(gt + 1, rootEnd < 0 || rootEnd <= gt ? span.length : rootEnd);
|
|
22
|
+
parseChildren(inner, caseInsensitive, out);
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
function parseChildren(inner, ci, out) {
|
|
26
|
+
const flags = ci ? "i" : "";
|
|
27
|
+
let pos = 0;
|
|
28
|
+
for (;;) {
|
|
29
|
+
const m = matchFrom(OPEN_TAG_SRC, flags, inner, pos);
|
|
30
|
+
if (m == null)
|
|
31
|
+
break;
|
|
32
|
+
const tag = m[1] ?? "";
|
|
33
|
+
const key = ci ? tag.toLowerCase() : tag;
|
|
34
|
+
const contentStart = m.index + m[0].length;
|
|
35
|
+
const closeRe = `</${quote(tag)}\\s*>`;
|
|
36
|
+
const close = matchFrom(closeRe, flags, inner, contentStart);
|
|
37
|
+
let contentEnd;
|
|
38
|
+
let next;
|
|
39
|
+
if (close != null) {
|
|
40
|
+
contentEnd = close.index;
|
|
41
|
+
next = close.index + close[0].length;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// unclosed tag: recover text up to the next sibling open tag
|
|
45
|
+
const sib = matchFrom(OPEN_TAG_SRC, flags, inner, contentStart);
|
|
46
|
+
if (sib != null) {
|
|
47
|
+
contentEnd = sib.index;
|
|
48
|
+
next = contentEnd;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
contentEnd = inner.length;
|
|
52
|
+
next = inner.length;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const content = inner.substring(contentStart, contentEnd);
|
|
56
|
+
const value = content.includes("<") ? nestedOrText(content, ci) : content.trim();
|
|
57
|
+
accumulate(out, key, value);
|
|
58
|
+
pos = next;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function nestedOrText(content, ci) {
|
|
62
|
+
const nested = {};
|
|
63
|
+
parseChildren(content, ci, nested);
|
|
64
|
+
return Object.keys(nested).length === 0 ? content.trim() : nested;
|
|
65
|
+
}
|
|
66
|
+
function accumulate(out, key, value) {
|
|
67
|
+
if (!Object.prototype.hasOwnProperty.call(out, key)) {
|
|
68
|
+
out[key] = value;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const existing = out[key];
|
|
72
|
+
if (Array.isArray(existing)) {
|
|
73
|
+
existing.push(value);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
out[key] = [existing, value];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=xml-forgiving-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xml-forgiving-reader.js","sourceRoot":"","sources":["../../src/recover/xml-forgiving-reader.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/render",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Logic-less, deterministic text render engine (Mustache) for MetaObjects templates — provider-resolved partials, format-driven escaping, zero core dependency.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/src/index.ts
CHANGED
|
@@ -11,3 +11,45 @@ export {
|
|
|
11
11
|
type VerifyError,
|
|
12
12
|
type VerifyOptions,
|
|
13
13
|
} from "./verify.js";
|
|
14
|
+
|
|
15
|
+
// FR-010 tolerant recover engine (Tier-2 forgiving parser).
|
|
16
|
+
export { recover } from "./recover/recover.js";
|
|
17
|
+
export {
|
|
18
|
+
Format,
|
|
19
|
+
FieldKind,
|
|
20
|
+
FieldRecovery,
|
|
21
|
+
Tolerance,
|
|
22
|
+
RecoveryReport,
|
|
23
|
+
scalar,
|
|
24
|
+
enumField,
|
|
25
|
+
range,
|
|
26
|
+
object,
|
|
27
|
+
recoverSchema,
|
|
28
|
+
defaults,
|
|
29
|
+
type FieldSpec,
|
|
30
|
+
type RecoverSchema,
|
|
31
|
+
type RecoverOptions,
|
|
32
|
+
type RecoverOutcome,
|
|
33
|
+
type RecoveryResult,
|
|
34
|
+
type Coercion,
|
|
35
|
+
type OnField,
|
|
36
|
+
} from "./recover/types.js";
|
|
37
|
+
export {
|
|
38
|
+
asString,
|
|
39
|
+
asInt,
|
|
40
|
+
asLong,
|
|
41
|
+
asDouble,
|
|
42
|
+
asBool,
|
|
43
|
+
asStringList,
|
|
44
|
+
} from "./recover/recover-map.js";
|
|
45
|
+
|
|
46
|
+
// FR-010 artifact 1 — output-format prompt renderer ("produce your answer like this").
|
|
47
|
+
export { renderOutputFormat } from "./prompt/output-format-renderer.js";
|
|
48
|
+
export { PromptStyle, promptStyleFrom } from "./prompt/prompt-style.js";
|
|
49
|
+
export {
|
|
50
|
+
PROMPT_OVERRIDES_NONE,
|
|
51
|
+
noOverrides,
|
|
52
|
+
type PromptOverrides,
|
|
53
|
+
} from "./prompt/prompt-overrides.js";
|
|
54
|
+
export type { OutputFormatSpec } from "./prompt/output-format-spec.js";
|
|
55
|
+
export type { PromptField } from "./prompt/prompt-field.js";
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// FR-010 artifact 1 — output-format prompt renderer ("produce your answer like this").
|
|
2
|
+
//
|
|
3
|
+
// Renders an OutputFormatSpec into a prompt fragment that teaches an LLM how to
|
|
4
|
+
// shape its answer. Three comment-free styles (guide / inline / exampleOnly) ×
|
|
5
|
+
// two formats (json / xml). Guidance is carried in prose / inline placeholders /
|
|
6
|
+
// a filled skeleton — NEVER in comments (models ignore them).
|
|
7
|
+
//
|
|
8
|
+
// Cross-port INVARIANT: the rendered text is byte-identical to the Java/C#/Kotlin
|
|
9
|
+
// reference (com.metaobjects.render.prompt.OutputFormatRenderer). Do not change
|
|
10
|
+
// the verbatim prose, skeleton shapes, or numeric-vs-quoted decision.
|
|
11
|
+
|
|
12
|
+
import { ESCAPERS } from "../escapers.js";
|
|
13
|
+
import { FieldKind, Format } from "../recover/types.js";
|
|
14
|
+
import type { OutputFormatSpec } from "./output-format-spec.js";
|
|
15
|
+
import type { PromptField } from "./prompt-field.js";
|
|
16
|
+
import type { PromptOverrides } from "./prompt-overrides.js";
|
|
17
|
+
import { PromptStyle } from "./prompt-style.js";
|
|
18
|
+
|
|
19
|
+
const NUMERIC_KINDS: ReadonlySet<FieldKind> = new Set<FieldKind>([
|
|
20
|
+
FieldKind.INT,
|
|
21
|
+
FieldKind.LONG,
|
|
22
|
+
FieldKind.DOUBLE,
|
|
23
|
+
FieldKind.BOOLEAN,
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
// The render engine OWNS format-keyed escaping; Format ("JSON"/"XML") maps to the
|
|
27
|
+
// lowercase ESCAPERS keys.
|
|
28
|
+
const escapeXml = (s: string): string => ESCAPERS.xml(s);
|
|
29
|
+
const escapeJson = (s: string): string => ESCAPERS.json(s);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Render an {@link OutputFormatSpec} into an output-format prompt fragment. The
|
|
33
|
+
* effective style is the override's style if present, otherwise the spec's.
|
|
34
|
+
*/
|
|
35
|
+
export function renderOutputFormat(spec: OutputFormatSpec, overrides: PromptOverrides): string {
|
|
36
|
+
const effectiveStyle = overrides.style ?? spec.style;
|
|
37
|
+
switch (effectiveStyle) {
|
|
38
|
+
case PromptStyle.EXAMPLE_ONLY:
|
|
39
|
+
return renderExampleOnly(spec, overrides);
|
|
40
|
+
case PromptStyle.INLINE:
|
|
41
|
+
return renderInline(spec, overrides);
|
|
42
|
+
default:
|
|
43
|
+
return renderGuide(spec, overrides);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---- INLINE ----------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
function renderInline(spec: OutputFormatSpec, overrides: PromptOverrides): string {
|
|
50
|
+
return spec.format === Format.XML
|
|
51
|
+
? renderXmlInline(spec, overrides)
|
|
52
|
+
: renderJsonInline(spec, overrides);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function renderXmlInline(spec: OutputFormatSpec, overrides: PromptOverrides): string {
|
|
56
|
+
const lines = spec.fields.map((field) => {
|
|
57
|
+
const escaped = escapeXml(inlineContent(field, overrides));
|
|
58
|
+
return ` <${field.name}>${escaped}</${field.name}>\n`;
|
|
59
|
+
});
|
|
60
|
+
return `<${spec.rootName}>\n${lines.join("")}</${spec.rootName}>`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function renderJsonInline(spec: OutputFormatSpec, overrides: PromptOverrides): string {
|
|
64
|
+
const lines = spec.fields.map(
|
|
65
|
+
(field) => ` "${field.name}": "${escapeJson(inlineContent(field, overrides))}"`,
|
|
66
|
+
);
|
|
67
|
+
// Empty object is `{\n}` (Java/C# parity), not `{\n\n}` from join("") on no lines.
|
|
68
|
+
return spec.fields.length === 0 ? "{\n}" : `{\n${lines.join(",\n")}\n}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function inlineContent(field: PromptField, overrides: PromptOverrides): string {
|
|
72
|
+
if (field.kind === FieldKind.ENUM && field.enumValues != null && field.enumValues.length > 0) {
|
|
73
|
+
return field.enumValues.join(" | ");
|
|
74
|
+
}
|
|
75
|
+
if (field.kind === FieldKind.BOOLEAN) {
|
|
76
|
+
return "true | false";
|
|
77
|
+
}
|
|
78
|
+
const instruction = resolveInstruction(field, overrides);
|
|
79
|
+
return instruction != null ? `{${instruction}}` : `{${field.name}}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Effective instruction: override first, then the field default, else null. */
|
|
83
|
+
function resolveInstruction(field: PromptField, overrides: PromptOverrides): string | null {
|
|
84
|
+
const ov = overrides.instructions?.[field.name];
|
|
85
|
+
if (ov != null) return ov;
|
|
86
|
+
return field.instruction;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ---- GUIDE -----------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
function renderGuide(spec: OutputFormatSpec, overrides: PromptOverrides): string {
|
|
92
|
+
let sb = "Fill in each field as described below:\n";
|
|
93
|
+
for (const field of spec.fields) {
|
|
94
|
+
const req = field.required ? "required" : "optional";
|
|
95
|
+
sb += `- ${field.name} (${req})`;
|
|
96
|
+
const instruction = resolveInstruction(field, overrides);
|
|
97
|
+
if (instruction != null) {
|
|
98
|
+
sb += `: ${instruction}`;
|
|
99
|
+
}
|
|
100
|
+
sb += "\n";
|
|
101
|
+
if (field.kind === FieldKind.ENUM && field.enumValues != null && field.enumValues.length > 0) {
|
|
102
|
+
sb += ` one of ${field.enumValues.join(", ")}\n`;
|
|
103
|
+
const enumDoc = field.enumDoc;
|
|
104
|
+
if (enumDoc != null) {
|
|
105
|
+
for (const val of field.enumValues) {
|
|
106
|
+
const doc = enumDoc[val];
|
|
107
|
+
if (doc != null) {
|
|
108
|
+
sb += ` ${val} = ${doc}\n`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const eg = exampleValueIfDeclared(field, overrides);
|
|
114
|
+
if (eg != null) {
|
|
115
|
+
sb += ` e.g. ${eg}\n`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
sb += "\nRespond exactly like this:\n";
|
|
119
|
+
sb += renderExampleOnly(spec, overrides);
|
|
120
|
+
return sb;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ---- EXAMPLE-ONLY (also the skeleton appended by GUIDE) ---------------------
|
|
124
|
+
|
|
125
|
+
function renderExampleOnly(spec: OutputFormatSpec, overrides: PromptOverrides): string {
|
|
126
|
+
return spec.format === Format.XML
|
|
127
|
+
? renderXmlSkeleton(spec, overrides)
|
|
128
|
+
: renderJsonSkeleton(spec, overrides);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function renderXmlSkeleton(spec: OutputFormatSpec, overrides: PromptOverrides): string {
|
|
132
|
+
const lines = spec.fields.map((field) => {
|
|
133
|
+
const escaped = escapeXml(exampleValue(field, overrides));
|
|
134
|
+
return ` <${field.name}>${escaped}</${field.name}>\n`;
|
|
135
|
+
});
|
|
136
|
+
return `<${spec.rootName}>\n${lines.join("")}</${spec.rootName}>`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function renderJsonSkeleton(spec: OutputFormatSpec, overrides: PromptOverrides): string {
|
|
140
|
+
// NOTE: FieldKind.OBJECT / nested fields are not expanded here — they render as
|
|
141
|
+
// a "{fieldName}" placeholder. Nested-object expansion is a bounded deferral
|
|
142
|
+
// (mirrors Java/C#).
|
|
143
|
+
const lines = spec.fields.map((field) => {
|
|
144
|
+
const value = exampleValue(field, overrides);
|
|
145
|
+
const rendered = isNumericOrBoolean(field.kind, value) ? value : `"${escapeJson(value)}"`;
|
|
146
|
+
return ` "${field.name}": ${rendered}`;
|
|
147
|
+
});
|
|
148
|
+
// Empty object is `{\n}` (Java/C# parity), not `{\n\n}` from join("") on no lines.
|
|
149
|
+
return spec.fields.length === 0 ? "{\n}" : `{\n${lines.join(",\n")}\n}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function exampleValueIfDeclared(field: PromptField, overrides: PromptOverrides): string | null {
|
|
153
|
+
const ov = overrides.examples?.[field.name];
|
|
154
|
+
if (ov != null) return ov;
|
|
155
|
+
if (field.example != null) return field.example;
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function exampleValue(field: PromptField, overrides: PromptOverrides): string {
|
|
160
|
+
const ov = overrides.examples?.[field.name];
|
|
161
|
+
if (ov != null) return ov;
|
|
162
|
+
if (field.example != null) return field.example;
|
|
163
|
+
if (field.kind === FieldKind.ENUM && field.enumValues != null && field.enumValues.length > 0) {
|
|
164
|
+
return field.enumValues[0]!;
|
|
165
|
+
}
|
|
166
|
+
return `{${field.name}}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function isNumericOrBoolean(kind: FieldKind, value: string): boolean {
|
|
170
|
+
if (!NUMERIC_KINDS.has(kind)) return false;
|
|
171
|
+
if (value === "true" || value === "false") return true;
|
|
172
|
+
// Finite-only: NaN/Infinity fall through to a quoted string so the emitted JSON
|
|
173
|
+
// stays valid. Number("") is 0, so guard the empty/blank case explicitly. Reject
|
|
174
|
+
// JS-only radix literals (0x../0b../0o..) that Number() accepts but Java/C# don't —
|
|
175
|
+
// same guard as the recover engine's parseFiniteNumber (keeps the JSON valid + parity).
|
|
176
|
+
const t = value.trim();
|
|
177
|
+
if (t === "" || /^[+-]?0[xXbBoO]/.test(t)) return false;
|
|
178
|
+
return Number.isFinite(Number(t));
|
|
179
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// FR-010 artifact 1 — a complete output-format descriptor.
|
|
2
|
+
|
|
3
|
+
import type { Format } from "../recover/types.js";
|
|
4
|
+
import type { PromptField } from "./prompt-field.js";
|
|
5
|
+
import type { PromptStyle } from "./prompt-style.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A complete output-format descriptor: the format, the root element/object name,
|
|
9
|
+
* the default presentation style, and the ordered fields. Drives
|
|
10
|
+
* {@link renderOutputFormat}.
|
|
11
|
+
*
|
|
12
|
+
* Precondition: `rootName` must be identifier-safe (valid XML element name / JSON
|
|
13
|
+
* key). The renderer does not escape it.
|
|
14
|
+
*/
|
|
15
|
+
export interface OutputFormatSpec {
|
|
16
|
+
readonly format: Format;
|
|
17
|
+
readonly rootName: string;
|
|
18
|
+
readonly style: PromptStyle;
|
|
19
|
+
readonly fields: readonly PromptField[];
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// FR-010 artifact 1 — one field of an output-format fragment.
|
|
2
|
+
|
|
3
|
+
import type { FieldKind } from "../recover/types.js";
|
|
4
|
+
import type { OutputFormatSpec } from "./output-format-spec.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One field of an output-format fragment. `enumValues`/`enumDoc` are non-null
|
|
8
|
+
* only for ENUM; `nested` non-null only for OBJECT; `example`/`instruction` are
|
|
9
|
+
* nullable.
|
|
10
|
+
*
|
|
11
|
+
* Precondition: `name` must be identifier-safe (valid XML element name / JSON
|
|
12
|
+
* key). The renderer does not escape field names.
|
|
13
|
+
*/
|
|
14
|
+
export interface PromptField {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly kind: FieldKind;
|
|
17
|
+
readonly required: boolean;
|
|
18
|
+
readonly array: boolean;
|
|
19
|
+
readonly enumValues: readonly string[] | null;
|
|
20
|
+
readonly enumDoc: Readonly<Record<string, string>> | null;
|
|
21
|
+
readonly example: string | null;
|
|
22
|
+
readonly instruction: string | null;
|
|
23
|
+
readonly nested: OutputFormatSpec | null;
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// FR-010 artifact 1 — render-time overrides of the metadata defaults.
|
|
2
|
+
|
|
3
|
+
import type { PromptStyle } from "./prompt-style.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Render-time overrides of the metadata defaults. `style` undefined keeps the
|
|
7
|
+
* spec's style; the maps override `PromptField.example`/`PromptField.instruction`
|
|
8
|
+
* per field name.
|
|
9
|
+
*/
|
|
10
|
+
export interface PromptOverrides {
|
|
11
|
+
readonly style?: PromptStyle;
|
|
12
|
+
readonly examples?: Readonly<Record<string, string>>;
|
|
13
|
+
readonly instructions?: Readonly<Record<string, string>>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** No overrides — keep every metadata default. Mirrors Java `PromptOverrides.none()`. */
|
|
17
|
+
export const PROMPT_OVERRIDES_NONE: PromptOverrides = Object.freeze({
|
|
18
|
+
// `style` omitted (not `undefined`) under exactOptionalPropertyTypes: an absent
|
|
19
|
+
// style keeps the spec's own style, matching Java `PromptOverrides.none()`.
|
|
20
|
+
examples: {},
|
|
21
|
+
instructions: {},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/** No overrides — keep every metadata default. */
|
|
25
|
+
export function noOverrides(): PromptOverrides {
|
|
26
|
+
return PROMPT_OVERRIDES_NONE;
|
|
27
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// FR-010 artifact 1 — output-format prompt renderer.
|
|
2
|
+
//
|
|
3
|
+
// How the output-format fragment teaches the model. Guidance is NEVER carried in
|
|
4
|
+
// comments (models ignore them) — it lives in prose / inline placeholders / a
|
|
5
|
+
// filled skeleton. Default is "guide".
|
|
6
|
+
//
|
|
7
|
+
// Tier-2 idiomatic TS: the Java SCREAMING_SNAKE enum (GUIDE/INLINE/EXAMPLE_ONLY)
|
|
8
|
+
// becomes a string-union whose members ARE the wire `@promptStyle` values
|
|
9
|
+
// ("guide" | "inline" | "exampleOnly") — no name<->wire mapping table needed.
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* - `"guide"`: prose field list ("Fill in each field…") followed by an example skeleton.
|
|
13
|
+
* - `"inline"`: a single skeleton whose field values are inline placeholders / enum choices.
|
|
14
|
+
* - `"exampleOnly"`: just a filled example skeleton, nothing else.
|
|
15
|
+
*/
|
|
16
|
+
export const PromptStyle = {
|
|
17
|
+
GUIDE: "guide",
|
|
18
|
+
INLINE: "inline",
|
|
19
|
+
EXAMPLE_ONLY: "exampleOnly",
|
|
20
|
+
} as const;
|
|
21
|
+
export type PromptStyle = (typeof PromptStyle)[keyof typeof PromptStyle];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Maps the `@promptStyle` attribute string to a {@link PromptStyle}. Null or any
|
|
25
|
+
* unrecognized value falls back to `"guide"` (matches Java `PromptStyle.from`).
|
|
26
|
+
*/
|
|
27
|
+
export function promptStyleFrom(s?: string | null): PromptStyle {
|
|
28
|
+
switch (s) {
|
|
29
|
+
case PromptStyle.INLINE:
|
|
30
|
+
return PromptStyle.INLINE;
|
|
31
|
+
case PromptStyle.EXAMPLE_ONLY:
|
|
32
|
+
return PromptStyle.EXAMPLE_ONLY;
|
|
33
|
+
default:
|
|
34
|
+
return PromptStyle.GUIDE;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# FR-010 TypeScript recover engine — known gaps & intentional cross-port divergences
|
|
2
|
+
|
|
3
|
+
Scope: the tolerant `recover` pipeline (`src/recover/`). The Java engine
|
|
4
|
+
(`server/java/render/.../recover/`) is the cross-port reference; `fixtures/recover-conformance/`
|
|
5
|
+
is the oracle. All 10 corpus cases pass.
|
|
6
|
+
|
|
7
|
+
## Additive capability (TS + C#, beyond Java/Kotlin)
|
|
8
|
+
|
|
9
|
+
- **Nested-object recover is implemented.** A `FieldSpec` with a non-null `nested` schema
|
|
10
|
+
(built via the `object(...)` factory) is descended into and its sub-fields classified. The
|
|
11
|
+
Java/Kotlin ports defer this (their codegen emits a scalar-STRING placeholder). The C# port
|
|
12
|
+
also carries the OBJECT branch, so TS and C# agree. This is **dormant** under both the
|
|
13
|
+
conformance corpus (no nested fixture; the runner's schema parser never sets `nested`) and the
|
|
14
|
+
FR-010 codegen (Phase 3 emits the scalar placeholder for cross-port parity), so it changes no
|
|
15
|
+
shared-corpus result. If a future shared fixture adds a nested case, Java/Kotlin catch up.
|
|
16
|
+
|
|
17
|
+
## Intentional, documented divergence (NOT a bug)
|
|
18
|
+
|
|
19
|
+
The cross-port contract pins *classification + canonical value* (numbers within ±1e-9), not
|
|
20
|
+
byte-identical native parsing.
|
|
21
|
+
|
|
22
|
+
- **Java-style numeric suffixes / hex-float literals.** Java's `Double.parseDouble` accepts
|
|
23
|
+
`"42d"` / `"42f"` and hex-float forms (→ RECOVERED); TS uses `Number(...)` + `Number.isFinite`,
|
|
24
|
+
which rejects them → **MALFORMED** (same accepted divergence the C# port records). The
|
|
25
|
+
load-bearing behavior — finite-only acceptance, `NaN`/`±Infinity` → MALFORMED — is identical.
|
|
26
|
+
|
|
27
|
+
- **JS-only radix-prefixed literals are GUARDED for parity.** `Number("0x10")` is `16` in JS, but
|
|
28
|
+
Java/C# reject `0x..`/`0b..`/`0o..` → MALFORMED. `parseFiniteNumber` rejects these prefixes
|
|
29
|
+
explicitly so TS matches Java/C# (→ MALFORMED) rather than over-accepting. (Not a divergence —
|
|
30
|
+
noted here because the guard exists precisely to prevent one.)
|
|
31
|
+
|
|
32
|
+
## Bounded deferral (parity with all ports)
|
|
33
|
+
|
|
34
|
+
- Array-of-enum is not specialized (a scalar array recovers via `asStringList`).
|
|
35
|
+
- `asInt`/`asLong` both return `number | null` (JS has one number type) and truncate toward zero.
|