@altopelago/aeon-finalize 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -0
- package/dist/datatype.d.ts +7 -0
- package/dist/datatype.d.ts.map +1 -0
- package/dist/datatype.js +19 -0
- package/dist/datatype.js.map +1 -0
- package/dist/finalize.d.ts +3 -0
- package/dist/finalize.d.ts.map +1 -0
- package/dist/finalize.js +89 -0
- package/dist/finalize.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +5 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +811 -0
- package/dist/json.js.map +1 -0
- package/dist/node.d.ts +4 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +321 -0
- package/dist/node.js.map +1 -0
- package/dist/outputs.d.ts +15 -0
- package/dist/outputs.d.ts.map +1 -0
- package/dist/outputs.js +68 -0
- package/dist/outputs.js.map +1 -0
- package/dist/projection.d.ts +7 -0
- package/dist/projection.d.ts.map +1 -0
- package/dist/projection.js +28 -0
- package/dist/projection.js.map +1 -0
- package/dist/reference-path.d.ts +6 -0
- package/dist/reference-path.d.ts.map +1 -0
- package/dist/reference-path.js +35 -0
- package/dist/reference-path.js.map +1 -0
- package/dist/transform.d.ts +4 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/transform.js +43 -0
- package/dist/transform.js.map +1 -0
- package/dist/types.d.ts +123 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +33 -0
package/dist/json.js
ADDED
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
import { formatPath } from '@altopelago/aeon-aes';
|
|
2
|
+
import { finalizeMap } from './finalize.js';
|
|
3
|
+
import { formatReferencePath } from './reference-path.js';
|
|
4
|
+
import { createProjectionState, shouldIncludeProjectedPath } from './projection.js';
|
|
5
|
+
import { formatDatatypeAnnotation } from './datatype.js';
|
|
6
|
+
function toDiagnostic(level, message, path, span) {
|
|
7
|
+
return {
|
|
8
|
+
level,
|
|
9
|
+
message,
|
|
10
|
+
...(path !== undefined ? { path } : {}),
|
|
11
|
+
...(span !== undefined ? { span: span } : {}),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const RESERVED_OBJECT_KEYS = new Set(['@', '$', '$node', '$children', '__proto__', 'constructor']);
|
|
15
|
+
const PROTOTYPE_POLLUTING_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
16
|
+
const IDENTIFIER_PATH_SEGMENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
17
|
+
function appendMemberPath(basePath, key) {
|
|
18
|
+
return IDENTIFIER_PATH_SEGMENT.test(key)
|
|
19
|
+
? `${basePath}.${key}`
|
|
20
|
+
: `${basePath}.[${JSON.stringify(key)}]`;
|
|
21
|
+
}
|
|
22
|
+
function appendAttributePath(basePath, key) {
|
|
23
|
+
return IDENTIFIER_PATH_SEGMENT.test(key)
|
|
24
|
+
? `${basePath}@${key}`
|
|
25
|
+
: `${basePath}@[${JSON.stringify(key)}]`;
|
|
26
|
+
}
|
|
27
|
+
function hasSafeOwnProperty(container, key) {
|
|
28
|
+
return !PROTOTYPE_POLLUTING_KEYS.has(key) && Object.prototype.hasOwnProperty.call(container, key);
|
|
29
|
+
}
|
|
30
|
+
export function finalizeJson(aes, options = {}) {
|
|
31
|
+
return finalizeJsonInternal(aes, options, 'tokens');
|
|
32
|
+
}
|
|
33
|
+
export function finalizeLinkedJson(aes, options = {}) {
|
|
34
|
+
return finalizeJsonInternal(aes, options, 'link-pointers');
|
|
35
|
+
}
|
|
36
|
+
function finalizeJsonInternal(aes, options, referenceStrategy) {
|
|
37
|
+
const strict = (options.mode ?? 'strict') === 'strict';
|
|
38
|
+
const scope = options.scope ?? 'payload';
|
|
39
|
+
const errors = [];
|
|
40
|
+
const warnings = [];
|
|
41
|
+
const pathToIndex = new Map();
|
|
42
|
+
for (let i = 0; i < aes.length; i++) {
|
|
43
|
+
pathToIndex.set(formatPath(aes[i].path), i);
|
|
44
|
+
}
|
|
45
|
+
const mapResult = finalizeMap(aes, options);
|
|
46
|
+
if (mapResult.meta?.errors)
|
|
47
|
+
errors.push(...mapResult.meta.errors);
|
|
48
|
+
if (mapResult.meta?.warnings)
|
|
49
|
+
warnings.push(...mapResult.meta.warnings);
|
|
50
|
+
const ctx = {
|
|
51
|
+
strict,
|
|
52
|
+
errors,
|
|
53
|
+
warnings,
|
|
54
|
+
referenceStrategy,
|
|
55
|
+
aes,
|
|
56
|
+
pathToIndex,
|
|
57
|
+
...(options.maxMaterializedWeight !== undefined ? { maxMaterializedWeight: options.maxMaterializedWeight } : {}),
|
|
58
|
+
...(options.maxReferenceDepth !== undefined ? { maxReferenceDepth: options.maxReferenceDepth } : {}),
|
|
59
|
+
materializedWeight: 0,
|
|
60
|
+
materializedWeightCache: new Map(),
|
|
61
|
+
activeClonePaths: [],
|
|
62
|
+
};
|
|
63
|
+
const projection = createProjectionState(options.includePaths, options.materialization);
|
|
64
|
+
const payload = payloadToJson(aes, ctx, projection, scope, options.header);
|
|
65
|
+
const header = headerToJson(options.header, ctx, projection, scope);
|
|
66
|
+
const document = scopeToJsonDocument(scope, header, payload);
|
|
67
|
+
if (referenceStrategy === 'link-pointers') {
|
|
68
|
+
linkPointerReferences(document, aes, ctx, projection, scope, options.header);
|
|
69
|
+
}
|
|
70
|
+
const meta = {
|
|
71
|
+
...(errors.length > 0 ? { errors } : {}),
|
|
72
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
73
|
+
};
|
|
74
|
+
return Object.keys(meta).length > 0 ? { document, meta } : { document };
|
|
75
|
+
}
|
|
76
|
+
function payloadToJson(aes, ctx, projection, scope, header) {
|
|
77
|
+
if (scope === 'header')
|
|
78
|
+
return {};
|
|
79
|
+
const document = {};
|
|
80
|
+
const documentAttrs = {};
|
|
81
|
+
for (const event of aes) {
|
|
82
|
+
if (!isTopLevel(event.path))
|
|
83
|
+
continue;
|
|
84
|
+
const segment = event.path.segments[1];
|
|
85
|
+
if (!segment || segment.type !== 'member')
|
|
86
|
+
continue;
|
|
87
|
+
const key = segment.key;
|
|
88
|
+
if (isHeaderEventKey(key, header)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const eventPath = scopedTopLevelPath(scope, 'payload', key);
|
|
92
|
+
if (!shouldIncludeProjectedPath(eventPath, projection))
|
|
93
|
+
continue;
|
|
94
|
+
if (isReservedObjectKey(key)) {
|
|
95
|
+
ctx.errors.push(toDiagnostic('error', `Reserved key: ${key}`, eventPath, event.span));
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (Object.prototype.hasOwnProperty.call(document, key)) {
|
|
99
|
+
const diag = toDiagnostic(ctx.strict ? 'error' : 'warning', `Duplicate top-level key during JSON finalization: ${key}`, eventPath, event.span);
|
|
100
|
+
if (ctx.strict)
|
|
101
|
+
ctx.errors.push(diag);
|
|
102
|
+
else
|
|
103
|
+
ctx.warnings.push(diag);
|
|
104
|
+
if (ctx.strict)
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
document[key] = valueToJson(event.value, ctx, eventPath, projection, event.datatype);
|
|
108
|
+
const attrJson = mergeAttributeJsonObjects(annotationsToJson(event.annotations, ctx, eventPath, projection), indexedChildAnnotationsToJson(event.value, ctx, eventPath, projection));
|
|
109
|
+
if (attrJson) {
|
|
110
|
+
documentAttrs[key] = attrJson;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (Object.keys(documentAttrs).length > 0) {
|
|
114
|
+
document['@'] = documentAttrs;
|
|
115
|
+
}
|
|
116
|
+
return document;
|
|
117
|
+
}
|
|
118
|
+
function headerToJson(header, ctx, projection, scope) {
|
|
119
|
+
if (scope === 'payload' || !header)
|
|
120
|
+
return {};
|
|
121
|
+
const document = {};
|
|
122
|
+
for (const [key, value] of header.fields) {
|
|
123
|
+
const fieldPath = scopedTopLevelPath(scope, 'header', key);
|
|
124
|
+
if (!shouldIncludeProjectedPath(fieldPath, projection))
|
|
125
|
+
continue;
|
|
126
|
+
document[key] = valueToJson(value, ctx, fieldPath, projection);
|
|
127
|
+
}
|
|
128
|
+
return document;
|
|
129
|
+
}
|
|
130
|
+
function scopeToJsonDocument(scope, header, payload) {
|
|
131
|
+
if (scope === 'header')
|
|
132
|
+
return header;
|
|
133
|
+
if (scope === 'full') {
|
|
134
|
+
return {
|
|
135
|
+
header,
|
|
136
|
+
payload,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return payload;
|
|
140
|
+
}
|
|
141
|
+
function isTopLevel(path) {
|
|
142
|
+
return path.segments.length === 2 && path.segments[0]?.type === 'root';
|
|
143
|
+
}
|
|
144
|
+
function valueToJson(value, ctx, path, projection, datatype) {
|
|
145
|
+
switch (value.type) {
|
|
146
|
+
case 'TypedValue':
|
|
147
|
+
return valueToJson(value.value, ctx, path, projection, value.datatype ? formatDatatypeAnnotation(value.datatype) : datatype);
|
|
148
|
+
case 'StringLiteral':
|
|
149
|
+
return value.value;
|
|
150
|
+
case 'NumberLiteral': {
|
|
151
|
+
const normalized = value.value.replace(/_/g, '');
|
|
152
|
+
const numeric = Number(normalized);
|
|
153
|
+
if (Number.isNaN(numeric)) {
|
|
154
|
+
ctx[ctx.strict ? 'errors' : 'warnings'].push({
|
|
155
|
+
...toDiagnostic(ctx.strict ? 'error' : 'warning', `Invalid numeric literal for JSON output: ${value.value}`, path, value.span),
|
|
156
|
+
code: 'FINALIZE_UNSAFE_NUMBER',
|
|
157
|
+
});
|
|
158
|
+
return value.value;
|
|
159
|
+
}
|
|
160
|
+
if (!Number.isFinite(numeric) || Math.abs(numeric) > Number.MAX_SAFE_INTEGER) {
|
|
161
|
+
ctx[ctx.strict ? 'errors' : 'warnings'].push({
|
|
162
|
+
...toDiagnostic(ctx.strict ? 'error' : 'warning', `Numeric literal exceeds JSON safe range: ${value.value}`, path, value.span),
|
|
163
|
+
code: 'FINALIZE_UNSAFE_NUMBER',
|
|
164
|
+
});
|
|
165
|
+
return value.value;
|
|
166
|
+
}
|
|
167
|
+
return numeric;
|
|
168
|
+
}
|
|
169
|
+
case 'InfinityLiteral':
|
|
170
|
+
ctx[ctx.strict ? 'errors' : 'warnings'].push({
|
|
171
|
+
...toDiagnostic(ctx.strict ? 'error' : 'warning', `Infinity literal is not representable in the strict JSON profile: ${value.value}`, path, value.span),
|
|
172
|
+
code: 'FINALIZE_JSON_PROFILE_INFINITY',
|
|
173
|
+
});
|
|
174
|
+
return value.value;
|
|
175
|
+
case 'NaNLiteral':
|
|
176
|
+
ctx[ctx.strict ? 'errors' : 'warnings'].push({
|
|
177
|
+
...toDiagnostic(ctx.strict ? 'error' : 'warning', `NaN literal is not representable in the strict JSON profile: ${value.value}`, path, value.span),
|
|
178
|
+
code: 'FINALIZE_JSON_PROFILE_NAN',
|
|
179
|
+
});
|
|
180
|
+
return value.value;
|
|
181
|
+
case 'NullLiteral':
|
|
182
|
+
if (value.mode === 'reserved' && value.value === 'none') {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
ctx[ctx.strict ? 'errors' : 'warnings'].push({
|
|
186
|
+
...toDiagnostic(ctx.strict ? 'error' : 'warning', `Null literal is not losslessly representable in the strict JSON profile: ${value.raw}`, path, value.span),
|
|
187
|
+
code: 'FINALIZE_JSON_PROFILE_NULL',
|
|
188
|
+
});
|
|
189
|
+
return value.raw;
|
|
190
|
+
case 'BooleanLiteral':
|
|
191
|
+
return value.value;
|
|
192
|
+
case 'ToggleLiteral':
|
|
193
|
+
return value.value === 'yes' || value.value === 'on';
|
|
194
|
+
case 'HexLiteral':
|
|
195
|
+
return value.value.replace(/_/g, '');
|
|
196
|
+
case 'RadixLiteral': {
|
|
197
|
+
const normalized = value.value.replace(/_/g, '');
|
|
198
|
+
const radixBase = declaredRadixBase(datatype ?? datatypeForPath(path, ctx));
|
|
199
|
+
if (radixBase != null && exceedsDeclaredRadix(normalized, radixBase)) {
|
|
200
|
+
ctx[ctx.strict ? 'errors' : 'warnings'].push({
|
|
201
|
+
...toDiagnostic(ctx.strict ? 'error' : 'warning', `Radix literal exceeds declared radix ${radixBase}: %${value.value}`, path, value.span),
|
|
202
|
+
code: 'FINALIZE_INVALID_RADIX_BASE',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return normalized;
|
|
206
|
+
}
|
|
207
|
+
case 'EncodingLiteral':
|
|
208
|
+
case 'SeparatorLiteral':
|
|
209
|
+
case 'DateLiteral':
|
|
210
|
+
case 'DateTimeLiteral':
|
|
211
|
+
case 'TimeLiteral':
|
|
212
|
+
return value.value;
|
|
213
|
+
case 'ObjectNode':
|
|
214
|
+
return objectToJson(value.bindings, ctx, path, projection);
|
|
215
|
+
case 'ListNode':
|
|
216
|
+
return value.elements.flatMap((element, index) => {
|
|
217
|
+
const elementPath = `${path}[${index}]`;
|
|
218
|
+
return shouldIncludeProjectedPath(elementPath, projection)
|
|
219
|
+
? [valueToJson(element, ctx, elementPath, projection)]
|
|
220
|
+
: [];
|
|
221
|
+
});
|
|
222
|
+
case 'TupleLiteral':
|
|
223
|
+
return value.elements.flatMap((element, index) => {
|
|
224
|
+
const elementPath = `${path}[${index}]`;
|
|
225
|
+
return shouldIncludeProjectedPath(elementPath, projection)
|
|
226
|
+
? [valueToJson(element, ctx, elementPath, projection)]
|
|
227
|
+
: [];
|
|
228
|
+
});
|
|
229
|
+
case 'NodeLiteral': {
|
|
230
|
+
const nodeAttrs = attributesToJson(value.attributes, ctx, `${path}@`, projection);
|
|
231
|
+
return {
|
|
232
|
+
$node: value.tag,
|
|
233
|
+
...(nodeAttrs ? { '@': nodeAttrs } : {}),
|
|
234
|
+
$children: value.children.map((child, index) => valueToJson(child, ctx, `${path}[${index}]`, projection)),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
case 'CloneReference': {
|
|
238
|
+
const resolved = resolveCloneReference(value.path, ctx);
|
|
239
|
+
if (resolved) {
|
|
240
|
+
if (ctx.activeClonePaths.includes(resolved.targetPath)) {
|
|
241
|
+
pushReferenceDiagnostic(ctx, `Reference cycle detected during JSON finalization: '${resolved.targetPath}'`, 'REFERENCE_CYCLE', path, value.span);
|
|
242
|
+
return referenceToJson('~', value.path, ctx, path, value.span, false);
|
|
243
|
+
}
|
|
244
|
+
const observedDepth = ctx.activeClonePaths.length + 1;
|
|
245
|
+
if (ctx.maxReferenceDepth !== undefined && observedDepth > ctx.maxReferenceDepth) {
|
|
246
|
+
pushReferenceDiagnostic(ctx, `Reference materialization depth ${observedDepth} exceeds maxReferenceDepth ${ctx.maxReferenceDepth}`, 'FINALIZE_REFERENCE_DEPTH_EXCEEDED', path, value.span);
|
|
247
|
+
return referenceToJson('~', value.path, ctx, path, value.span, false);
|
|
248
|
+
}
|
|
249
|
+
if (!consumeCloneBudget(resolved.targetPath, resolved.value, ctx, path, value.span)) {
|
|
250
|
+
return referenceToJson('~', value.path, ctx, path, value.span, false);
|
|
251
|
+
}
|
|
252
|
+
ctx.activeClonePaths.push(resolved.targetPath);
|
|
253
|
+
try {
|
|
254
|
+
return valueToJson(resolved.value, ctx, path, projection, datatype);
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
ctx.activeClonePaths.pop();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return referenceToJson('~', value.path, ctx, path, value.span);
|
|
261
|
+
}
|
|
262
|
+
case 'PointerReference':
|
|
263
|
+
return referenceToJson('~>', value.path, ctx, path, value.span, ctx.referenceStrategy === 'tokens');
|
|
264
|
+
default:
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function objectToJson(bindings, ctx, basePath, projection) {
|
|
269
|
+
const obj = {};
|
|
270
|
+
const attrEntries = {};
|
|
271
|
+
for (const binding of bindings) {
|
|
272
|
+
const key = binding.key;
|
|
273
|
+
const entryPath = appendMemberPath(basePath, key);
|
|
274
|
+
if (!shouldIncludeProjectedPath(entryPath, projection)) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (isReservedObjectKey(key)) {
|
|
278
|
+
ctx.errors.push(toDiagnostic('error', `Reserved key: ${key}`, entryPath, binding.span));
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
282
|
+
const diag = toDiagnostic(ctx.strict ? 'error' : 'warning', `Duplicate object key during JSON finalization: ${key}`, entryPath, binding.span);
|
|
283
|
+
if (ctx.strict)
|
|
284
|
+
ctx.errors.push(diag);
|
|
285
|
+
else
|
|
286
|
+
ctx.warnings.push(diag);
|
|
287
|
+
if (ctx.strict)
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
obj[key] = valueToJson(binding.value, ctx, entryPath, projection);
|
|
291
|
+
const attrJson = mergeAttributeJsonObjects(attributesToJson(binding.attributes, ctx, entryPath, projection), indexedChildAnnotationsToJson(binding.value, ctx, entryPath, projection));
|
|
292
|
+
if (attrJson) {
|
|
293
|
+
attrEntries[key] = attrJson;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (Object.keys(attrEntries).length > 0) {
|
|
297
|
+
obj['@'] = attrEntries;
|
|
298
|
+
}
|
|
299
|
+
return obj;
|
|
300
|
+
}
|
|
301
|
+
function isReservedObjectKey(key) {
|
|
302
|
+
return RESERVED_OBJECT_KEYS.has(key);
|
|
303
|
+
}
|
|
304
|
+
function referenceToJson(prefix, pathParts, ctx, path, span, emitDiagnostic = true) {
|
|
305
|
+
const token = `${prefix}${formatReferencePath(pathParts)}`;
|
|
306
|
+
if (!emitDiagnostic) {
|
|
307
|
+
return token;
|
|
308
|
+
}
|
|
309
|
+
const diag = toDiagnostic(ctx.strict ? 'error' : 'warning', `Reference left unresolved during JSON finalization: ${token}`, path, span);
|
|
310
|
+
const coded = { ...diag, code: 'FINALIZE_UNRESOLVED_REFERENCE' };
|
|
311
|
+
if (ctx.strict)
|
|
312
|
+
ctx.errors.push(coded);
|
|
313
|
+
else
|
|
314
|
+
ctx.warnings.push(coded);
|
|
315
|
+
return token;
|
|
316
|
+
}
|
|
317
|
+
function pushReferenceDiagnostic(ctx, message, code, path, span) {
|
|
318
|
+
ctx.errors.push({
|
|
319
|
+
...toDiagnostic('error', message, path, span),
|
|
320
|
+
code,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
function resolveCloneReference(pathParts, ctx) {
|
|
324
|
+
const targetPath = formatCloneTargetPath(pathParts);
|
|
325
|
+
for (let split = pathParts.length; split >= 1; split--) {
|
|
326
|
+
const prefix = pathParts.slice(0, split);
|
|
327
|
+
if (prefix.some((part) => typeof part === 'object' && part !== null && 'type' in part && part.type === 'attr')) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const prefixPath = formatCloneTargetPath(prefix);
|
|
331
|
+
const targetIndex = ctx.pathToIndex.get(prefixPath);
|
|
332
|
+
if (targetIndex === undefined)
|
|
333
|
+
continue;
|
|
334
|
+
const target = ctx.aes[targetIndex];
|
|
335
|
+
if (!target)
|
|
336
|
+
return null;
|
|
337
|
+
const remainder = pathParts.slice(split);
|
|
338
|
+
if (remainder.length === 0) {
|
|
339
|
+
return { value: target.value, targetPath };
|
|
340
|
+
}
|
|
341
|
+
const resolved = resolveReferenceSubpath(target, remainder);
|
|
342
|
+
if (resolved) {
|
|
343
|
+
return { value: resolved, targetPath };
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
function consumeCloneBudget(targetPath, value, ctx, path, span) {
|
|
349
|
+
if (ctx.maxMaterializedWeight === undefined) {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
const weight = measureMaterializedWeight(value, ctx, targetPath, new Set());
|
|
353
|
+
const nextWeight = ctx.materializedWeight + weight;
|
|
354
|
+
if (nextWeight <= ctx.maxMaterializedWeight) {
|
|
355
|
+
ctx.materializedWeight = nextWeight;
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
pushReferenceDiagnostic(ctx, `Reference materialization budget exceeded for '${targetPath}' (budget=maxMaterializedWeight, observed=${nextWeight}, limit=${ctx.maxMaterializedWeight})`, 'FINALIZE_REFERENCE_BUDGET_EXCEEDED', path, span);
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
function measureMaterializedWeight(value, ctx, currentPath, stack) {
|
|
362
|
+
if (stack.has(currentPath)) {
|
|
363
|
+
return 1;
|
|
364
|
+
}
|
|
365
|
+
switch (value.type) {
|
|
366
|
+
case 'TypedValue':
|
|
367
|
+
return measureMaterializedWeight(value.value, ctx, currentPath, stack);
|
|
368
|
+
case 'StringLiteral':
|
|
369
|
+
case 'NumberLiteral':
|
|
370
|
+
case 'InfinityLiteral':
|
|
371
|
+
case 'NaNLiteral':
|
|
372
|
+
case 'NullLiteral':
|
|
373
|
+
case 'BooleanLiteral':
|
|
374
|
+
case 'ToggleLiteral':
|
|
375
|
+
case 'HexLiteral':
|
|
376
|
+
case 'RadixLiteral':
|
|
377
|
+
case 'EncodingLiteral':
|
|
378
|
+
case 'SeparatorLiteral':
|
|
379
|
+
case 'DateLiteral':
|
|
380
|
+
case 'DateTimeLiteral':
|
|
381
|
+
case 'TimeLiteral':
|
|
382
|
+
case 'PointerReference':
|
|
383
|
+
return 1;
|
|
384
|
+
case 'CloneReference': {
|
|
385
|
+
const resolved = resolveCloneReference(value.path, ctx);
|
|
386
|
+
if (!resolved) {
|
|
387
|
+
return 1;
|
|
388
|
+
}
|
|
389
|
+
if (ctx.materializedWeightCache.has(resolved.targetPath)) {
|
|
390
|
+
return ctx.materializedWeightCache.get(resolved.targetPath);
|
|
391
|
+
}
|
|
392
|
+
const nextStack = new Set(stack);
|
|
393
|
+
nextStack.add(currentPath);
|
|
394
|
+
const weight = measureMaterializedWeight(resolved.value, ctx, resolved.targetPath, nextStack);
|
|
395
|
+
ctx.materializedWeightCache.set(resolved.targetPath, weight);
|
|
396
|
+
return weight;
|
|
397
|
+
}
|
|
398
|
+
case 'ObjectNode':
|
|
399
|
+
return value.bindings.reduce((sum, binding) => {
|
|
400
|
+
const childPath = appendMemberPath(currentPath, binding.key);
|
|
401
|
+
return sum
|
|
402
|
+
+ measureMaterializedWeight(binding.value, ctx, childPath, stack)
|
|
403
|
+
+ measureAttributeWeight(binding.attributes, ctx, childPath, stack);
|
|
404
|
+
}, 0);
|
|
405
|
+
case 'ListNode':
|
|
406
|
+
case 'TupleLiteral':
|
|
407
|
+
return value.elements.reduce((sum, element, index) => {
|
|
408
|
+
const childPath = `${currentPath}[${index}]`;
|
|
409
|
+
return sum + measureMaterializedWeight(element, ctx, childPath, stack);
|
|
410
|
+
}, 0) + measureAttributeWeight(value.attributes, ctx, currentPath, stack);
|
|
411
|
+
case 'NodeLiteral':
|
|
412
|
+
return 1
|
|
413
|
+
+ measureAttributeWeight(value.attributes, ctx, currentPath, stack)
|
|
414
|
+
+ value.children.reduce((sum, child, index) => {
|
|
415
|
+
const childPath = `${currentPath}<${index}>`;
|
|
416
|
+
return sum + measureMaterializedWeight(child, ctx, childPath, stack);
|
|
417
|
+
}, 0);
|
|
418
|
+
default:
|
|
419
|
+
return 1;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function measureAttributeWeight(attributes, ctx, currentPath, stack) {
|
|
423
|
+
if (!attributes)
|
|
424
|
+
return 0;
|
|
425
|
+
if (Array.isArray(attributes)) {
|
|
426
|
+
let total = 0;
|
|
427
|
+
for (const attr of attributes) {
|
|
428
|
+
for (const [key, entry] of attr.entries) {
|
|
429
|
+
const entryPath = appendAttributePath(currentPath, key);
|
|
430
|
+
total += measureMaterializedWeight(entry.value, ctx, entryPath, stack);
|
|
431
|
+
total += measureAttributeWeight(entry.attributes, ctx, entryPath, stack);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return total;
|
|
435
|
+
}
|
|
436
|
+
let total = 0;
|
|
437
|
+
for (const [key, entry] of attributes.entries()) {
|
|
438
|
+
const entryPath = appendAttributePath(currentPath, key);
|
|
439
|
+
total += measureMaterializedWeight(entry.value, ctx, entryPath, stack);
|
|
440
|
+
total += measureAttributeWeight(entry.annotations, ctx, entryPath, stack);
|
|
441
|
+
}
|
|
442
|
+
return total;
|
|
443
|
+
}
|
|
444
|
+
function resolveReferenceSubpath(event, remainder) {
|
|
445
|
+
let currentValue = event.value;
|
|
446
|
+
let currentAnnotations = event.annotations;
|
|
447
|
+
for (const part of remainder) {
|
|
448
|
+
if (typeof part === 'object' && part !== null && 'type' in part && part.type === 'attr') {
|
|
449
|
+
const entry = currentAnnotations?.get(part.key);
|
|
450
|
+
if (!entry)
|
|
451
|
+
return null;
|
|
452
|
+
currentValue = entry.value;
|
|
453
|
+
currentAnnotations = entry.annotations;
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (typeof part === 'string') {
|
|
457
|
+
if (currentValue.type !== 'ObjectNode')
|
|
458
|
+
return null;
|
|
459
|
+
const binding = currentValue.bindings.find((candidate) => candidate.key === part);
|
|
460
|
+
if (!binding)
|
|
461
|
+
return null;
|
|
462
|
+
currentValue = binding.value;
|
|
463
|
+
currentAnnotations = buildAnnotationEntries(binding.attributes);
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (typeof part === 'number') {
|
|
467
|
+
if (currentValue.type !== 'ListNode' && currentValue.type !== 'TupleLiteral')
|
|
468
|
+
return null;
|
|
469
|
+
const element = currentValue.elements[part];
|
|
470
|
+
if (!element)
|
|
471
|
+
return null;
|
|
472
|
+
currentValue = element;
|
|
473
|
+
currentAnnotations = undefined;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
return currentValue;
|
|
479
|
+
}
|
|
480
|
+
function formatCloneTargetPath(pathParts) {
|
|
481
|
+
if (pathParts.length === 0)
|
|
482
|
+
return '$';
|
|
483
|
+
let result = '$';
|
|
484
|
+
for (const part of pathParts) {
|
|
485
|
+
if (typeof part === 'number') {
|
|
486
|
+
result += `[${part}]`;
|
|
487
|
+
}
|
|
488
|
+
else if (typeof part === 'string') {
|
|
489
|
+
result = appendMemberPath(result, part);
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
result = appendAttributePath(result, part.key);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return result;
|
|
496
|
+
}
|
|
497
|
+
function buildAnnotationEntries(attributes) {
|
|
498
|
+
if (!attributes || attributes.length === 0)
|
|
499
|
+
return undefined;
|
|
500
|
+
const result = new Map();
|
|
501
|
+
for (const attribute of attributes) {
|
|
502
|
+
for (const [key, entry] of attribute.entries) {
|
|
503
|
+
const mapped = { value: entry.value };
|
|
504
|
+
const nested = buildAnnotationEntries(entry.attributes);
|
|
505
|
+
if (nested) {
|
|
506
|
+
mapped.annotations = nested;
|
|
507
|
+
}
|
|
508
|
+
result.set(key, mapped);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return result;
|
|
512
|
+
}
|
|
513
|
+
function attributesToJson(attributes, ctx, path, projection) {
|
|
514
|
+
if (!attributes || attributes.length === 0)
|
|
515
|
+
return null;
|
|
516
|
+
const obj = {};
|
|
517
|
+
const nestedAttrEntries = {};
|
|
518
|
+
for (const attr of attributes) {
|
|
519
|
+
for (const [key, entry] of attr.entries) {
|
|
520
|
+
const entryPath = appendAttributePath(path, key);
|
|
521
|
+
if (!shouldIncludeProjectedPath(entryPath, projection)) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
obj[key] = valueToJson(entry.value, ctx, entryPath, projection, entry.datatype ? formatDatatypeAnnotation(entry.datatype) : undefined);
|
|
525
|
+
const nested = attributesToJson(entry.attributes, ctx, entryPath, projection);
|
|
526
|
+
if (nested) {
|
|
527
|
+
nestedAttrEntries[key] = nested;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (Object.keys(nestedAttrEntries).length > 0) {
|
|
532
|
+
obj['@'] = nestedAttrEntries;
|
|
533
|
+
}
|
|
534
|
+
return Object.keys(obj).length > 0 ? obj : null;
|
|
535
|
+
}
|
|
536
|
+
function indexedChildAnnotationsToJson(value, ctx, path, projection) {
|
|
537
|
+
const children = value.type === 'ListNode' || value.type === 'TupleLiteral'
|
|
538
|
+
? value.elements
|
|
539
|
+
: value.type === 'NodeLiteral'
|
|
540
|
+
? value.children
|
|
541
|
+
: null;
|
|
542
|
+
if (!children)
|
|
543
|
+
return null;
|
|
544
|
+
const itemAttrs = {};
|
|
545
|
+
for (const [index, child] of children.entries()) {
|
|
546
|
+
if (child.type !== 'TypedValue' || child.attributes.length === 0) {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
const childPath = `${path}[${index}]`;
|
|
550
|
+
const attrJson = attributesToJson(child.attributes, ctx, childPath, projection);
|
|
551
|
+
if (attrJson) {
|
|
552
|
+
itemAttrs[String(index)] = attrJson;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return Object.keys(itemAttrs).length > 0 ? { '@items': itemAttrs } : null;
|
|
556
|
+
}
|
|
557
|
+
function mergeAttributeJsonObjects(left, right) {
|
|
558
|
+
if (left && right) {
|
|
559
|
+
return { ...left, ...right };
|
|
560
|
+
}
|
|
561
|
+
return left ?? right;
|
|
562
|
+
}
|
|
563
|
+
function datatypeForPath(path, ctx) {
|
|
564
|
+
const canonicalPath = canonicalFinalizePath(path);
|
|
565
|
+
const index = ctx.pathToIndex.get(canonicalPath);
|
|
566
|
+
return index === undefined ? undefined : ctx.aes[index]?.datatype;
|
|
567
|
+
}
|
|
568
|
+
function canonicalFinalizePath(path) {
|
|
569
|
+
if (path === '$.payload' || path === '$.header')
|
|
570
|
+
return '$';
|
|
571
|
+
if (path.startsWith('$.payload.'))
|
|
572
|
+
return `$.${path.slice('$.payload.'.length)}`;
|
|
573
|
+
if (path.startsWith('$.payload@'))
|
|
574
|
+
return `$.${path.slice('$.payload'.length)}`;
|
|
575
|
+
if (path.startsWith('$.header.'))
|
|
576
|
+
return `$.${path.slice('$.header.'.length)}`;
|
|
577
|
+
if (path.startsWith('$.header@'))
|
|
578
|
+
return `$.${path.slice('$.header'.length)}`;
|
|
579
|
+
return path;
|
|
580
|
+
}
|
|
581
|
+
function declaredRadixBase(datatype) {
|
|
582
|
+
if (!datatype)
|
|
583
|
+
return null;
|
|
584
|
+
const trimmed = datatype.trim();
|
|
585
|
+
if (trimmed === 'radix2')
|
|
586
|
+
return 2;
|
|
587
|
+
if (trimmed === 'radix6')
|
|
588
|
+
return 6;
|
|
589
|
+
if (trimmed === 'radix8')
|
|
590
|
+
return 8;
|
|
591
|
+
if (trimmed === 'radix12')
|
|
592
|
+
return 12;
|
|
593
|
+
const match = /^radix\[(\d+)\]$/.exec(trimmed);
|
|
594
|
+
if (!match)
|
|
595
|
+
return null;
|
|
596
|
+
const base = Number(match[1]);
|
|
597
|
+
return Number.isInteger(base) && base >= 2 && base <= 64 ? base : null;
|
|
598
|
+
}
|
|
599
|
+
function exceedsDeclaredRadix(value, base) {
|
|
600
|
+
for (const ch of value) {
|
|
601
|
+
if (ch === '+' || ch === '-' || ch === '.')
|
|
602
|
+
continue;
|
|
603
|
+
const digit = radixDigitValue(ch);
|
|
604
|
+
if (digit == null || digit >= base) {
|
|
605
|
+
return true;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
function radixDigitValue(ch) {
|
|
611
|
+
if (ch >= '0' && ch <= '9')
|
|
612
|
+
return ch.charCodeAt(0) - 48;
|
|
613
|
+
if (ch >= 'A' && ch <= 'Z')
|
|
614
|
+
return ch.charCodeAt(0) - 55;
|
|
615
|
+
if (ch >= 'a' && ch <= 'z')
|
|
616
|
+
return ch.charCodeAt(0) - 61;
|
|
617
|
+
if (ch === '&')
|
|
618
|
+
return 62;
|
|
619
|
+
if (ch === '!')
|
|
620
|
+
return 63;
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
function annotationsToJson(annotations, ctx, path, projection) {
|
|
624
|
+
if (!annotations || annotations.size === 0)
|
|
625
|
+
return null;
|
|
626
|
+
const obj = {};
|
|
627
|
+
const nestedAttrEntries = {};
|
|
628
|
+
for (const [key, entry] of annotations.entries()) {
|
|
629
|
+
const entryPath = appendAttributePath(path, key);
|
|
630
|
+
if (!shouldIncludeProjectedPath(entryPath, projection)) {
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
obj[key] = valueToJson(entry.value, ctx, entryPath, projection);
|
|
634
|
+
const nested = annotationsToJson(entry.annotations, ctx, entryPath, projection);
|
|
635
|
+
if (nested) {
|
|
636
|
+
nestedAttrEntries[key] = nested;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
if (Object.keys(nestedAttrEntries).length > 0) {
|
|
640
|
+
obj['@'] = nestedAttrEntries;
|
|
641
|
+
}
|
|
642
|
+
return Object.keys(obj).length > 0 ? obj : null;
|
|
643
|
+
}
|
|
644
|
+
function scopedTopLevelPath(scope, branch, key) {
|
|
645
|
+
const basePath = scope === 'full' ? `$.${branch}` : '$';
|
|
646
|
+
return appendMemberPath(basePath, key);
|
|
647
|
+
}
|
|
648
|
+
function isHeaderEventKey(key, header) {
|
|
649
|
+
if (key === 'aeon:header')
|
|
650
|
+
return true;
|
|
651
|
+
if (!header || !key.startsWith('aeon:'))
|
|
652
|
+
return false;
|
|
653
|
+
return header.fields.has(key.slice('aeon:'.length));
|
|
654
|
+
}
|
|
655
|
+
function linkPointerReferences(document, aes, ctx, projection, scope, header) {
|
|
656
|
+
if (scope === 'header') {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
const payloadRoot = scope === 'full' ? document.payload : document;
|
|
660
|
+
if (!isContainer(payloadRoot)) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
for (const event of aes) {
|
|
664
|
+
if (event.value.type !== 'PointerReference')
|
|
665
|
+
continue;
|
|
666
|
+
const topLevelKey = topLevelPayloadMemberKey(event);
|
|
667
|
+
if (topLevelKey && isHeaderEventKey(topLevelKey, header)) {
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
const sourcePath = scope === 'full'
|
|
671
|
+
? `$.payload${formatPath(event.path).slice(1)}`
|
|
672
|
+
: formatPath(event.path);
|
|
673
|
+
if (!shouldIncludeProjectedPath(sourcePath, projection)) {
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
const sourceEndpoint = endpointFromCanonicalPath(payloadRoot, event.path.segments.slice(1));
|
|
677
|
+
if (!sourceEndpoint) {
|
|
678
|
+
pushLinkDiagnostic(ctx, `Pointer source path could not be materialized: ${sourcePath}`, 'POINTER_SOURCE_NOT_MATERIALIZED', sourcePath, event.span);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
const targetEndpoint = endpointFromReferencePath(payloadRoot, event.value.path);
|
|
682
|
+
if (!targetEndpoint) {
|
|
683
|
+
pushLinkDiagnostic(ctx, `Pointer target could not be materialized: ~>${formatReferencePath(event.value.path)}`, 'POINTER_TARGET_NOT_MATERIALIZED', sourcePath, event.span);
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
Object.defineProperty(sourceEndpoint.owner, sourceEndpoint.key, {
|
|
687
|
+
enumerable: true,
|
|
688
|
+
configurable: true,
|
|
689
|
+
get() {
|
|
690
|
+
return readContainerValue(targetEndpoint.owner, targetEndpoint.key);
|
|
691
|
+
},
|
|
692
|
+
set(next) {
|
|
693
|
+
writeContainerValue(targetEndpoint.owner, targetEndpoint.key, next);
|
|
694
|
+
},
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
function pushLinkDiagnostic(ctx, message, code, path, span) {
|
|
699
|
+
const diag = {
|
|
700
|
+
...toDiagnostic(ctx.strict ? 'error' : 'warning', message, path, span),
|
|
701
|
+
code,
|
|
702
|
+
};
|
|
703
|
+
if (ctx.strict)
|
|
704
|
+
ctx.errors.push(diag);
|
|
705
|
+
else
|
|
706
|
+
ctx.warnings.push(diag);
|
|
707
|
+
}
|
|
708
|
+
function topLevelPayloadMemberKey(event) {
|
|
709
|
+
const segment = event.path.segments[1];
|
|
710
|
+
if (!segment || segment.type !== 'member')
|
|
711
|
+
return null;
|
|
712
|
+
return segment.key;
|
|
713
|
+
}
|
|
714
|
+
function endpointFromCanonicalPath(root, segments) {
|
|
715
|
+
if (segments.length === 0)
|
|
716
|
+
return null;
|
|
717
|
+
const parentSegments = segments.slice(0, -1);
|
|
718
|
+
const last = segments[segments.length - 1];
|
|
719
|
+
if (!last || last.type === 'root')
|
|
720
|
+
return null;
|
|
721
|
+
const owner = traverseCanonicalSegments(root, parentSegments);
|
|
722
|
+
if (!owner)
|
|
723
|
+
return null;
|
|
724
|
+
return last.type === 'member'
|
|
725
|
+
? { owner, key: last.key }
|
|
726
|
+
: { owner, key: last.index };
|
|
727
|
+
}
|
|
728
|
+
function endpointFromReferencePath(root, pathParts) {
|
|
729
|
+
if (pathParts.length === 0)
|
|
730
|
+
return null;
|
|
731
|
+
const parentParts = pathParts.slice(0, -1);
|
|
732
|
+
const last = pathParts[pathParts.length - 1];
|
|
733
|
+
if (last === undefined || typeof last === 'object') {
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
if (typeof last === 'string' && PROTOTYPE_POLLUTING_KEYS.has(last)) {
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
const owner = traverseReferenceParts(root, parentParts);
|
|
740
|
+
if (!owner)
|
|
741
|
+
return null;
|
|
742
|
+
return { owner, key: last };
|
|
743
|
+
}
|
|
744
|
+
function traverseCanonicalSegments(current, segments) {
|
|
745
|
+
let node = current;
|
|
746
|
+
for (const segment of segments) {
|
|
747
|
+
if (segment.type === 'root')
|
|
748
|
+
continue;
|
|
749
|
+
if (!isContainer(node))
|
|
750
|
+
return null;
|
|
751
|
+
const next = segment.type === 'member'
|
|
752
|
+
? readContainerValue(node, segment.key)
|
|
753
|
+
: readContainerValue(node, segment.index);
|
|
754
|
+
if (next === undefined)
|
|
755
|
+
return null;
|
|
756
|
+
node = next;
|
|
757
|
+
}
|
|
758
|
+
return isContainer(node) ? node : null;
|
|
759
|
+
}
|
|
760
|
+
function traverseReferenceParts(current, parts) {
|
|
761
|
+
let node = current;
|
|
762
|
+
for (const part of parts) {
|
|
763
|
+
if (typeof part === 'object') {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
if (!isContainer(node))
|
|
767
|
+
return null;
|
|
768
|
+
const next = readContainerValue(node, part);
|
|
769
|
+
if (next === undefined)
|
|
770
|
+
return null;
|
|
771
|
+
node = next;
|
|
772
|
+
}
|
|
773
|
+
return isContainer(node) ? node : null;
|
|
774
|
+
}
|
|
775
|
+
function isContainer(value) {
|
|
776
|
+
return Array.isArray(value) || (!!value && typeof value === 'object');
|
|
777
|
+
}
|
|
778
|
+
function readContainerValue(container, key) {
|
|
779
|
+
return typeof key === 'number'
|
|
780
|
+
? container[key]
|
|
781
|
+
: hasSafeOwnProperty(container, key)
|
|
782
|
+
? container[key]
|
|
783
|
+
: undefined;
|
|
784
|
+
}
|
|
785
|
+
function writeContainerValue(container, key, value) {
|
|
786
|
+
if (typeof key === 'number') {
|
|
787
|
+
container[key] = value;
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (!hasSafeOwnProperty(container, key)) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
const descriptor = Object.getOwnPropertyDescriptor(container, key);
|
|
794
|
+
if (!descriptor) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
if (typeof descriptor.set === 'function') {
|
|
798
|
+
descriptor.set.call(container, value);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (!Object.prototype.hasOwnProperty.call(descriptor, 'value')) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
Object.defineProperty(container, key, {
|
|
805
|
+
value,
|
|
806
|
+
writable: descriptor.writable ?? true,
|
|
807
|
+
enumerable: descriptor.enumerable ?? true,
|
|
808
|
+
configurable: descriptor.configurable ?? true,
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
//# sourceMappingURL=json.js.map
|