@flighthq/scene-document 0.4.1-edge.28.7f8e221
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/LICENSE.md +9 -0
- package/README.md +17 -0
- package/dist/contract.d.ts +2 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +2 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/sceneDocumentScene2DMaterialization.d.ts +8 -0
- package/dist/sceneDocumentScene2DMaterialization.d.ts.map +1 -0
- package/dist/sceneDocumentScene2DMaterialization.js +287 -0
- package/dist/sceneDocumentScene2DMaterialization.js.map +1 -0
- package/dist/sceneDocumentYamlSubset.d.ts +25 -0
- package/dist/sceneDocumentYamlSubset.d.ts.map +1 -0
- package/dist/sceneDocumentYamlSubset.js +647 -0
- package/dist/sceneDocumentYamlSubset.js.map +1 -0
- package/package.json +54 -0
- package/src/sceneDocumentScene2DMaterialization.test.ts +395 -0
- package/src/sceneDocumentYamlSubset.test.ts +229 -0
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
const PARSE_FAILURE = Symbol('scene-document-yaml-subset-parse-failure');
|
|
2
|
+
/**
|
|
3
|
+
* Parses the deliberately closed YAML subset that will underlie Flight document text. The module is
|
|
4
|
+
* private because it yields syntax values only; it does not claim or materialize a scene schema.
|
|
5
|
+
*/
|
|
6
|
+
export function parseSceneDocumentYamlSubset(source) {
|
|
7
|
+
if (source.length > MAX_DOCUMENT_CODE_UNITS) {
|
|
8
|
+
const position = getSceneDocumentYamlSubsetPosition(source, MAX_DOCUMENT_CODE_UNITS);
|
|
9
|
+
return createSceneDocumentYamlSubsetRefusal('flight-document.limit.document-code-units', position, MAX_DOCUMENT_CODE_UNITS, source.length);
|
|
10
|
+
}
|
|
11
|
+
const lexed = new SceneDocumentYamlSubsetLexer(source).lex();
|
|
12
|
+
if (!lexed.ok)
|
|
13
|
+
return lexed;
|
|
14
|
+
return new SceneDocumentYamlSubsetParser(lexed.lines).parse();
|
|
15
|
+
}
|
|
16
|
+
class SceneDocumentYamlSubsetLexer {
|
|
17
|
+
source;
|
|
18
|
+
constructor(source) {
|
|
19
|
+
this.source = source;
|
|
20
|
+
}
|
|
21
|
+
lex() {
|
|
22
|
+
const lines = [];
|
|
23
|
+
let line = 1;
|
|
24
|
+
let lineStart = 0;
|
|
25
|
+
let offset = 0;
|
|
26
|
+
while (offset < this.source.length) {
|
|
27
|
+
let indent = 0;
|
|
28
|
+
while (this.source[offset] === ' ') {
|
|
29
|
+
offset++;
|
|
30
|
+
indent++;
|
|
31
|
+
}
|
|
32
|
+
if (this.source[offset] === '\t') {
|
|
33
|
+
return this.refuse('flight-document.syntax.tab-character', offset, line, offset - lineStart + 1);
|
|
34
|
+
}
|
|
35
|
+
const tokens = [];
|
|
36
|
+
let flowDepth = 0;
|
|
37
|
+
while (offset < this.source.length && !isSceneDocumentYamlSubsetLineBreak(this.source[offset])) {
|
|
38
|
+
while (this.source[offset] === ' ')
|
|
39
|
+
offset++;
|
|
40
|
+
if (this.source[offset] === '\t') {
|
|
41
|
+
return this.refuse('flight-document.syntax.tab-character', offset, line, offset - lineStart + 1);
|
|
42
|
+
}
|
|
43
|
+
if (offset >= this.source.length || isSceneDocumentYamlSubsetLineBreak(this.source[offset]))
|
|
44
|
+
break;
|
|
45
|
+
if (this.source[offset] === '#') {
|
|
46
|
+
while (offset < this.source.length && !isSceneDocumentYamlSubsetLineBreak(this.source[offset]))
|
|
47
|
+
offset++;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
const column = offset - lineStart + 1;
|
|
51
|
+
if (tokens.length === 0 && flowDepth === 0 && isSceneDocumentYamlSubsetDocumentSeparator(this.source, offset)) {
|
|
52
|
+
return this.refuse('flight-document.unsupported.document-separator', offset, line, column);
|
|
53
|
+
}
|
|
54
|
+
const character = this.source[offset];
|
|
55
|
+
if (character === '"' || character === "'") {
|
|
56
|
+
const quoted = this.lexQuotedScalar(offset, line, lineStart);
|
|
57
|
+
if (!quoted.ok)
|
|
58
|
+
return quoted;
|
|
59
|
+
tokens.push(quoted.token);
|
|
60
|
+
offset = quoted.nextOffset;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (character === '[' || character === ']') {
|
|
64
|
+
return this.refuse('flight-document.unsupported.flow-sequence', offset, line, column);
|
|
65
|
+
}
|
|
66
|
+
if (character === '&')
|
|
67
|
+
return this.refuse('flight-document.unsupported.anchor', offset, line, column);
|
|
68
|
+
if (character === '*')
|
|
69
|
+
return this.refuse('flight-document.unsupported.alias', offset, line, column);
|
|
70
|
+
if (character === '!')
|
|
71
|
+
return this.refuse('flight-document.unsupported.tag', offset, line, column);
|
|
72
|
+
if (character === '|' || character === '>') {
|
|
73
|
+
return this.refuse('flight-document.unsupported.block-scalar', offset, line, column);
|
|
74
|
+
}
|
|
75
|
+
if (character === '{') {
|
|
76
|
+
tokens.push(createSceneDocumentYamlSubsetToken('flow-mapping-start', offset, line, column));
|
|
77
|
+
flowDepth++;
|
|
78
|
+
offset++;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (character === '}') {
|
|
82
|
+
if (flowDepth === 0)
|
|
83
|
+
return this.refuse('flight-document.syntax.unexpected-token', offset, line, column);
|
|
84
|
+
tokens.push(createSceneDocumentYamlSubsetToken('flow-mapping-end', offset, line, column));
|
|
85
|
+
flowDepth--;
|
|
86
|
+
offset++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (character === ',') {
|
|
90
|
+
if (flowDepth === 0)
|
|
91
|
+
return this.refuse('flight-document.syntax.unexpected-token', offset, line, column);
|
|
92
|
+
tokens.push(createSceneDocumentYamlSubsetToken('comma', offset, line, column));
|
|
93
|
+
offset++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (character === ':') {
|
|
97
|
+
tokens.push(createSceneDocumentYamlSubsetToken('colon', offset, line, column));
|
|
98
|
+
offset++;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (character === '-' && flowDepth === 0 && isSceneDocumentYamlSubsetSeparation(this.source[offset + 1])) {
|
|
102
|
+
tokens.push(createSceneDocumentYamlSubsetToken('sequence-item', offset, line, column));
|
|
103
|
+
offset++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const plain = this.lexPlainScalar(offset, line, lineStart, flowDepth);
|
|
107
|
+
if (!plain.ok)
|
|
108
|
+
return plain;
|
|
109
|
+
tokens.push(plain.token);
|
|
110
|
+
offset = plain.nextOffset;
|
|
111
|
+
}
|
|
112
|
+
if (flowDepth !== 0) {
|
|
113
|
+
return this.refuse('flight-document.syntax.unterminated-flow-mapping', offset, line, offset - lineStart + 1);
|
|
114
|
+
}
|
|
115
|
+
if (tokens.length > 0)
|
|
116
|
+
lines.push({ indent, tokens });
|
|
117
|
+
if (this.source[offset] === '\r')
|
|
118
|
+
offset++;
|
|
119
|
+
if (this.source[offset] === '\n')
|
|
120
|
+
offset++;
|
|
121
|
+
if (offset > lineStart) {
|
|
122
|
+
line++;
|
|
123
|
+
lineStart = offset;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { ok: true, lines };
|
|
127
|
+
}
|
|
128
|
+
lexDoubleQuotedScalar(start, line, lineStart) {
|
|
129
|
+
let offset = start + 1;
|
|
130
|
+
let value = '';
|
|
131
|
+
while (offset < this.source.length && !isSceneDocumentYamlSubsetLineBreak(this.source[offset])) {
|
|
132
|
+
const character = this.source[offset];
|
|
133
|
+
if (character === '"') {
|
|
134
|
+
return {
|
|
135
|
+
ok: true,
|
|
136
|
+
nextOffset: offset + 1,
|
|
137
|
+
token: createSceneDocumentYamlSubsetToken('scalar', start, line, start - lineStart + 1, value, true),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (character !== '\\') {
|
|
141
|
+
const refusal = this.appendDecodedScalar(value, character, offset, line, lineStart);
|
|
142
|
+
if (refusal !== null)
|
|
143
|
+
return refusal;
|
|
144
|
+
value += character;
|
|
145
|
+
offset++;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const escape = this.source[offset + 1];
|
|
149
|
+
if (escape === undefined || isSceneDocumentYamlSubsetLineBreak(escape)) {
|
|
150
|
+
return this.refuse('flight-document.syntax.invalid-escape', offset, line, offset - lineStart + 1);
|
|
151
|
+
}
|
|
152
|
+
const simpleEscape = DOUBLE_QUOTED_ESCAPES[escape];
|
|
153
|
+
if (simpleEscape !== undefined) {
|
|
154
|
+
const refusal = this.appendDecodedScalar(value, simpleEscape, offset, line, lineStart);
|
|
155
|
+
if (refusal !== null)
|
|
156
|
+
return refusal;
|
|
157
|
+
value += simpleEscape;
|
|
158
|
+
offset += 2;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (escape !== 'u') {
|
|
162
|
+
return this.refuse('flight-document.syntax.invalid-escape', offset, line, offset - lineStart + 1);
|
|
163
|
+
}
|
|
164
|
+
const code = this.source.slice(offset + 2, offset + 6);
|
|
165
|
+
if (!/^[0-9a-fA-F]{4}$/.test(code)) {
|
|
166
|
+
return this.refuse('flight-document.syntax.invalid-escape', offset, line, offset - lineStart + 1);
|
|
167
|
+
}
|
|
168
|
+
const decoded = String.fromCharCode(Number.parseInt(code, 16));
|
|
169
|
+
const refusal = this.appendDecodedScalar(value, decoded, offset, line, lineStart);
|
|
170
|
+
if (refusal !== null)
|
|
171
|
+
return refusal;
|
|
172
|
+
value += decoded;
|
|
173
|
+
offset += 6;
|
|
174
|
+
}
|
|
175
|
+
return this.refuse('flight-document.syntax.unterminated-quoted-scalar', start, line, start - lineStart + 1);
|
|
176
|
+
}
|
|
177
|
+
lexPlainScalar(start, line, lineStart, flowDepth) {
|
|
178
|
+
let end = start;
|
|
179
|
+
let offset = start;
|
|
180
|
+
while (offset < this.source.length && !isSceneDocumentYamlSubsetLineBreak(this.source[offset])) {
|
|
181
|
+
const character = this.source[offset];
|
|
182
|
+
if (character === '\t') {
|
|
183
|
+
return this.refuse('flight-document.syntax.tab-character', offset, line, offset - lineStart + 1);
|
|
184
|
+
}
|
|
185
|
+
if (character === '#' && isSceneDocumentYamlSubsetSeparation(this.source[offset - 1]))
|
|
186
|
+
break;
|
|
187
|
+
if (character === ':' && isSceneDocumentYamlSubsetMappingColon(this.source[offset + 1]))
|
|
188
|
+
break;
|
|
189
|
+
if (flowDepth > 0 && (character === ',' || character === '}'))
|
|
190
|
+
break;
|
|
191
|
+
if (character === ' ') {
|
|
192
|
+
let next = offset + 1;
|
|
193
|
+
while (this.source[next] === ' ')
|
|
194
|
+
next++;
|
|
195
|
+
if (isSceneDocumentYamlSubsetUnsupportedIndicator(this.source[next]))
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
if (character !== ' ') {
|
|
199
|
+
end = offset + 1;
|
|
200
|
+
const actual = end - start;
|
|
201
|
+
if (actual > MAX_SCALAR_CODE_UNITS) {
|
|
202
|
+
return this.refuse('flight-document.limit.scalar-code-units', offset, line, offset - lineStart + 1, MAX_SCALAR_CODE_UNITS, actual);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
offset++;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
ok: true,
|
|
209
|
+
nextOffset: offset,
|
|
210
|
+
token: createSceneDocumentYamlSubsetToken('scalar', start, line, start - lineStart + 1, this.source.slice(start, end), false),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
lexQuotedScalar(start, line, lineStart) {
|
|
214
|
+
return this.source[start] === '"'
|
|
215
|
+
? this.lexDoubleQuotedScalar(start, line, lineStart)
|
|
216
|
+
: this.lexSingleQuotedScalar(start, line, lineStart);
|
|
217
|
+
}
|
|
218
|
+
lexSingleQuotedScalar(start, line, lineStart) {
|
|
219
|
+
let offset = start + 1;
|
|
220
|
+
let value = '';
|
|
221
|
+
while (offset < this.source.length && !isSceneDocumentYamlSubsetLineBreak(this.source[offset])) {
|
|
222
|
+
const character = this.source[offset];
|
|
223
|
+
if (character !== "'") {
|
|
224
|
+
const refusal = this.appendDecodedScalar(value, character, offset, line, lineStart);
|
|
225
|
+
if (refusal !== null)
|
|
226
|
+
return refusal;
|
|
227
|
+
value += character;
|
|
228
|
+
offset++;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (this.source[offset + 1] === "'") {
|
|
232
|
+
const refusal = this.appendDecodedScalar(value, "'", offset, line, lineStart);
|
|
233
|
+
if (refusal !== null)
|
|
234
|
+
return refusal;
|
|
235
|
+
value += "'";
|
|
236
|
+
offset += 2;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
ok: true,
|
|
241
|
+
nextOffset: offset + 1,
|
|
242
|
+
token: createSceneDocumentYamlSubsetToken('scalar', start, line, start - lineStart + 1, value, true),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return this.refuse('flight-document.syntax.unterminated-quoted-scalar', start, line, start - lineStart + 1);
|
|
246
|
+
}
|
|
247
|
+
appendDecodedScalar(current, next, offset, line, lineStart) {
|
|
248
|
+
const actual = current.length + next.length;
|
|
249
|
+
return actual <= MAX_SCALAR_CODE_UNITS
|
|
250
|
+
? null
|
|
251
|
+
: this.refuse('flight-document.limit.scalar-code-units', offset, line, offset - lineStart + 1, MAX_SCALAR_CODE_UNITS, actual);
|
|
252
|
+
}
|
|
253
|
+
refuse(kind, offset, line, column, limit = null, actual = null) {
|
|
254
|
+
return createSceneDocumentYamlSubsetRefusal(kind, { offset, line, column }, limit, actual);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
class SceneDocumentYamlSubsetParser {
|
|
258
|
+
lines;
|
|
259
|
+
index = 0;
|
|
260
|
+
nodeCount = 0;
|
|
261
|
+
refusal = null;
|
|
262
|
+
constructor(lines) {
|
|
263
|
+
this.lines = lines;
|
|
264
|
+
}
|
|
265
|
+
parse() {
|
|
266
|
+
if (this.lines.length === 0)
|
|
267
|
+
return { ok: true, value: null };
|
|
268
|
+
const first = this.lines[0];
|
|
269
|
+
if (first.indent !== 0) {
|
|
270
|
+
return this.createRefusal('flight-document.syntax.root-indentation', first.tokens[0]);
|
|
271
|
+
}
|
|
272
|
+
const value = this.parseBlock(0, 1);
|
|
273
|
+
if (value === PARSE_FAILURE)
|
|
274
|
+
return this.getRefusal();
|
|
275
|
+
if (this.index !== this.lines.length) {
|
|
276
|
+
return this.createRefusal('flight-document.syntax.multiple-root-values', this.lines[this.index].tokens[0]);
|
|
277
|
+
}
|
|
278
|
+
return { ok: true, value };
|
|
279
|
+
}
|
|
280
|
+
addMappingEntry(mapping, count, tokens, colonIndex, indent, depth) {
|
|
281
|
+
const colon = tokens[colonIndex];
|
|
282
|
+
if (count >= MAX_COLLECTION_ENTRIES) {
|
|
283
|
+
return this.refuse('flight-document.limit.collection-entries', tokens[0] ?? colon, MAX_COLLECTION_ENTRIES, count + 1);
|
|
284
|
+
}
|
|
285
|
+
if (colonIndex !== 1 || tokens[0]?.kind !== 'scalar') {
|
|
286
|
+
return this.refuse('flight-document.syntax.expected-mapping-key', tokens[0] ?? colon);
|
|
287
|
+
}
|
|
288
|
+
const key = tokens[0].value;
|
|
289
|
+
if (key.length > MAX_KEY_CODE_UNITS) {
|
|
290
|
+
return this.refuse('flight-document.limit.key-code-units', tokens[0], MAX_KEY_CODE_UNITS, key.length);
|
|
291
|
+
}
|
|
292
|
+
if (Object.hasOwn(mapping, key))
|
|
293
|
+
return this.refuse('flight-document.syntax.duplicate-key', tokens[0]);
|
|
294
|
+
let value;
|
|
295
|
+
if (colonIndex + 1 === tokens.length) {
|
|
296
|
+
const next = this.lines[this.index];
|
|
297
|
+
if (next !== undefined && next.indent > indent)
|
|
298
|
+
value = this.parseBlock(next.indent, depth + 1);
|
|
299
|
+
else
|
|
300
|
+
value = this.parseNull(colon);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
value = this.parseInline(tokens, colonIndex + 1, depth + 1);
|
|
304
|
+
}
|
|
305
|
+
if (value === PARSE_FAILURE)
|
|
306
|
+
return PARSE_FAILURE;
|
|
307
|
+
mapping[key] = value;
|
|
308
|
+
return count + 1;
|
|
309
|
+
}
|
|
310
|
+
checkCollection(depth, token) {
|
|
311
|
+
if (depth > MAX_NESTING_DEPTH) {
|
|
312
|
+
this.refuse('flight-document.limit.nesting-depth', token, MAX_NESTING_DEPTH, depth);
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
return this.checkNode(token);
|
|
316
|
+
}
|
|
317
|
+
checkNode(token) {
|
|
318
|
+
if (this.nodeCount >= MAX_TOTAL_NODES) {
|
|
319
|
+
this.refuse('flight-document.limit.total-nodes', token, MAX_TOTAL_NODES, this.nodeCount + 1);
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
this.nodeCount++;
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
createRefusal(kind, token, limit = null, actual = null) {
|
|
326
|
+
return createSceneDocumentYamlSubsetRefusal(kind, token, limit, actual);
|
|
327
|
+
}
|
|
328
|
+
getRefusal() {
|
|
329
|
+
return (this.refusal ??
|
|
330
|
+
createSceneDocumentYamlSubsetRefusal('flight-document.syntax.invalid-document', { offset: 0, line: 1, column: 1 }));
|
|
331
|
+
}
|
|
332
|
+
parseBlock(indent, depth) {
|
|
333
|
+
const line = this.lines[this.index];
|
|
334
|
+
const first = line?.tokens[0];
|
|
335
|
+
if (line === undefined || first === undefined)
|
|
336
|
+
return PARSE_FAILURE;
|
|
337
|
+
if (line.indent !== indent)
|
|
338
|
+
return this.refuse('flight-document.syntax.unexpected-indentation', first);
|
|
339
|
+
if (first.kind === 'sequence-item')
|
|
340
|
+
return this.parseBlockSequence(indent, depth);
|
|
341
|
+
if (findSceneDocumentYamlSubsetTopLevelColon(line.tokens, 0) >= 0) {
|
|
342
|
+
return this.parseBlockMapping(indent, depth);
|
|
343
|
+
}
|
|
344
|
+
this.index++;
|
|
345
|
+
const value = this.parseInline(line.tokens, 0, depth);
|
|
346
|
+
if (value === PARSE_FAILURE)
|
|
347
|
+
return PARSE_FAILURE;
|
|
348
|
+
const next = this.lines[this.index];
|
|
349
|
+
if (next !== undefined && next.indent > indent) {
|
|
350
|
+
return this.refuse('flight-document.syntax.unexpected-indentation', next.tokens[0]);
|
|
351
|
+
}
|
|
352
|
+
return value;
|
|
353
|
+
}
|
|
354
|
+
parseBlockMapping(indent, depth) {
|
|
355
|
+
const first = this.lines[this.index].tokens[0];
|
|
356
|
+
if (!this.checkCollection(depth, first))
|
|
357
|
+
return PARSE_FAILURE;
|
|
358
|
+
const mapping = Object.create(null);
|
|
359
|
+
let count = 0;
|
|
360
|
+
while (this.index < this.lines.length) {
|
|
361
|
+
const line = this.lines[this.index];
|
|
362
|
+
const firstToken = line.tokens[0];
|
|
363
|
+
if (line.indent < indent)
|
|
364
|
+
break;
|
|
365
|
+
if (line.indent > indent) {
|
|
366
|
+
return this.refuse('flight-document.syntax.unexpected-indentation', firstToken);
|
|
367
|
+
}
|
|
368
|
+
if (firstToken.kind === 'sequence-item') {
|
|
369
|
+
return this.refuse('flight-document.syntax.mixed-collection', firstToken);
|
|
370
|
+
}
|
|
371
|
+
const colonIndex = findSceneDocumentYamlSubsetTopLevelColon(line.tokens, 0);
|
|
372
|
+
if (colonIndex < 0)
|
|
373
|
+
return this.refuse('flight-document.syntax.expected-mapping-entry', firstToken);
|
|
374
|
+
this.index++;
|
|
375
|
+
const nextCount = this.addMappingEntry(mapping, count, line.tokens, colonIndex, indent, depth);
|
|
376
|
+
if (nextCount === PARSE_FAILURE)
|
|
377
|
+
return PARSE_FAILURE;
|
|
378
|
+
count = nextCount;
|
|
379
|
+
}
|
|
380
|
+
return mapping;
|
|
381
|
+
}
|
|
382
|
+
parseBlockSequence(indent, depth) {
|
|
383
|
+
const first = this.lines[this.index].tokens[0];
|
|
384
|
+
if (!this.checkCollection(depth, first))
|
|
385
|
+
return PARSE_FAILURE;
|
|
386
|
+
const sequence = [];
|
|
387
|
+
while (this.index < this.lines.length) {
|
|
388
|
+
const line = this.lines[this.index];
|
|
389
|
+
const item = line.tokens[0];
|
|
390
|
+
if (line.indent < indent)
|
|
391
|
+
break;
|
|
392
|
+
if (line.indent > indent)
|
|
393
|
+
return this.refuse('flight-document.syntax.unexpected-indentation', item);
|
|
394
|
+
if (item.kind !== 'sequence-item')
|
|
395
|
+
return this.refuse('flight-document.syntax.mixed-collection', item);
|
|
396
|
+
if (sequence.length >= MAX_COLLECTION_ENTRIES) {
|
|
397
|
+
return this.refuse('flight-document.limit.collection-entries', item, MAX_COLLECTION_ENTRIES, sequence.length + 1);
|
|
398
|
+
}
|
|
399
|
+
this.index++;
|
|
400
|
+
let value;
|
|
401
|
+
if (line.tokens.length === 1) {
|
|
402
|
+
const next = this.lines[this.index];
|
|
403
|
+
value =
|
|
404
|
+
next !== undefined && next.indent > indent ? this.parseBlock(next.indent, depth + 1) : this.parseNull(item);
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
const colonIndex = findSceneDocumentYamlSubsetTopLevelColon(line.tokens, 1);
|
|
408
|
+
value =
|
|
409
|
+
colonIndex >= 0
|
|
410
|
+
? this.parseSequenceMapping(line, indent, depth + 1, colonIndex)
|
|
411
|
+
: this.parseInline(line.tokens, 1, depth + 1);
|
|
412
|
+
}
|
|
413
|
+
if (value === PARSE_FAILURE)
|
|
414
|
+
return PARSE_FAILURE;
|
|
415
|
+
sequence.push(value);
|
|
416
|
+
const next = this.lines[this.index];
|
|
417
|
+
if (next !== undefined && next.indent > indent) {
|
|
418
|
+
return this.refuse('flight-document.syntax.unexpected-indentation', next.tokens[0]);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return sequence;
|
|
422
|
+
}
|
|
423
|
+
parseFlowMapping(tokens, cursor, depth) {
|
|
424
|
+
const start = tokens[cursor.index];
|
|
425
|
+
if (!this.checkCollection(depth, start))
|
|
426
|
+
return PARSE_FAILURE;
|
|
427
|
+
const mapping = Object.create(null);
|
|
428
|
+
let count = 0;
|
|
429
|
+
cursor.index++;
|
|
430
|
+
if (tokens[cursor.index]?.kind === 'flow-mapping-end') {
|
|
431
|
+
cursor.index++;
|
|
432
|
+
return mapping;
|
|
433
|
+
}
|
|
434
|
+
while (cursor.index < tokens.length) {
|
|
435
|
+
const keyToken = tokens[cursor.index];
|
|
436
|
+
if (count >= MAX_COLLECTION_ENTRIES) {
|
|
437
|
+
return this.refuse('flight-document.limit.collection-entries', keyToken, MAX_COLLECTION_ENTRIES, count + 1);
|
|
438
|
+
}
|
|
439
|
+
if (keyToken.kind !== 'scalar')
|
|
440
|
+
return this.refuse('flight-document.syntax.expected-mapping-key', keyToken);
|
|
441
|
+
if (keyToken.value.length > MAX_KEY_CODE_UNITS) {
|
|
442
|
+
return this.refuse('flight-document.limit.key-code-units', keyToken, MAX_KEY_CODE_UNITS, keyToken.value.length);
|
|
443
|
+
}
|
|
444
|
+
if (Object.hasOwn(mapping, keyToken.value)) {
|
|
445
|
+
return this.refuse('flight-document.syntax.duplicate-key', keyToken);
|
|
446
|
+
}
|
|
447
|
+
cursor.index++;
|
|
448
|
+
const colon = tokens[cursor.index];
|
|
449
|
+
if (colon?.kind !== 'colon') {
|
|
450
|
+
return this.refuse('flight-document.syntax.expected-mapping-entry', colon ?? keyToken);
|
|
451
|
+
}
|
|
452
|
+
cursor.index++;
|
|
453
|
+
const valueToken = tokens[cursor.index];
|
|
454
|
+
if (valueToken === undefined)
|
|
455
|
+
return this.refuse('flight-document.syntax.expected-value', colon);
|
|
456
|
+
let value;
|
|
457
|
+
if (valueToken.kind === 'flow-mapping-start')
|
|
458
|
+
value = this.parseFlowMapping(tokens, cursor, depth + 1);
|
|
459
|
+
else if (valueToken.kind === 'scalar') {
|
|
460
|
+
cursor.index++;
|
|
461
|
+
value = this.parseScalar(valueToken);
|
|
462
|
+
}
|
|
463
|
+
else
|
|
464
|
+
return this.refuse('flight-document.syntax.expected-value', valueToken);
|
|
465
|
+
if (value === PARSE_FAILURE)
|
|
466
|
+
return PARSE_FAILURE;
|
|
467
|
+
mapping[keyToken.value] = value;
|
|
468
|
+
count++;
|
|
469
|
+
const delimiter = tokens[cursor.index];
|
|
470
|
+
if (delimiter?.kind === 'flow-mapping-end') {
|
|
471
|
+
cursor.index++;
|
|
472
|
+
return mapping;
|
|
473
|
+
}
|
|
474
|
+
if (delimiter?.kind !== 'comma') {
|
|
475
|
+
return this.refuse('flight-document.syntax.expected-flow-delimiter', delimiter ?? valueToken);
|
|
476
|
+
}
|
|
477
|
+
cursor.index++;
|
|
478
|
+
if (tokens[cursor.index]?.kind === 'flow-mapping-end') {
|
|
479
|
+
return this.refuse('flight-document.syntax.trailing-flow-comma', tokens[cursor.index]);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return this.refuse('flight-document.syntax.unterminated-flow-mapping', start);
|
|
483
|
+
}
|
|
484
|
+
parseInline(tokens, start, depth) {
|
|
485
|
+
const token = tokens[start];
|
|
486
|
+
if (token === undefined)
|
|
487
|
+
return PARSE_FAILURE;
|
|
488
|
+
if (token.kind === 'flow-mapping-start') {
|
|
489
|
+
const cursor = { index: start };
|
|
490
|
+
const value = this.parseFlowMapping(tokens, cursor, depth);
|
|
491
|
+
if (value === PARSE_FAILURE)
|
|
492
|
+
return PARSE_FAILURE;
|
|
493
|
+
if (cursor.index !== tokens.length) {
|
|
494
|
+
return this.refuse('flight-document.syntax.trailing-flow-content', tokens[cursor.index]);
|
|
495
|
+
}
|
|
496
|
+
return value;
|
|
497
|
+
}
|
|
498
|
+
if (token.kind !== 'scalar' || start + 1 !== tokens.length) {
|
|
499
|
+
return this.refuse('flight-document.syntax.expected-scalar', token);
|
|
500
|
+
}
|
|
501
|
+
return this.parseScalar(token);
|
|
502
|
+
}
|
|
503
|
+
parseNull(token) {
|
|
504
|
+
return this.checkNode(token) ? null : PARSE_FAILURE;
|
|
505
|
+
}
|
|
506
|
+
parseScalar(token) {
|
|
507
|
+
if (!this.checkNode(token))
|
|
508
|
+
return PARSE_FAILURE;
|
|
509
|
+
if (token.quoted)
|
|
510
|
+
return token.value;
|
|
511
|
+
if (token.value === 'null')
|
|
512
|
+
return null;
|
|
513
|
+
if (token.value === 'true')
|
|
514
|
+
return true;
|
|
515
|
+
if (token.value === 'false')
|
|
516
|
+
return false;
|
|
517
|
+
if (HEX_INTEGER_PATTERN.test(token.value)) {
|
|
518
|
+
const value = Number.parseInt(token.value.slice(2), 16);
|
|
519
|
+
return Number.isSafeInteger(value) ? value : this.refuse('flight-document.scalar.number-out-of-range', token);
|
|
520
|
+
}
|
|
521
|
+
if (DECIMAL_NUMBER_PATTERN.test(token.value)) {
|
|
522
|
+
const value = Number(token.value);
|
|
523
|
+
if (!Number.isFinite(value))
|
|
524
|
+
return this.refuse('flight-document.scalar.number-out-of-range', token);
|
|
525
|
+
if (!token.value.includes('.') && !/[eE]/.test(token.value) && !Number.isSafeInteger(value)) {
|
|
526
|
+
return this.refuse('flight-document.scalar.number-out-of-range', token);
|
|
527
|
+
}
|
|
528
|
+
return value;
|
|
529
|
+
}
|
|
530
|
+
return token.value;
|
|
531
|
+
}
|
|
532
|
+
parseSequenceMapping(line, sequenceIndent, depth, colonIndex) {
|
|
533
|
+
const first = line.tokens[1];
|
|
534
|
+
if (!this.checkCollection(depth, first))
|
|
535
|
+
return PARSE_FAILURE;
|
|
536
|
+
const mapping = Object.create(null);
|
|
537
|
+
const mappingIndent = sequenceIndent + 2;
|
|
538
|
+
let count = this.addMappingEntry(mapping, 0, line.tokens.slice(1), colonIndex - 1, mappingIndent, depth);
|
|
539
|
+
if (count === PARSE_FAILURE)
|
|
540
|
+
return PARSE_FAILURE;
|
|
541
|
+
while (this.index < this.lines.length) {
|
|
542
|
+
const nextLine = this.lines[this.index];
|
|
543
|
+
const nextFirst = nextLine.tokens[0];
|
|
544
|
+
if (nextLine.indent <= sequenceIndent)
|
|
545
|
+
break;
|
|
546
|
+
if (nextLine.indent !== mappingIndent) {
|
|
547
|
+
return this.refuse('flight-document.syntax.unexpected-indentation', nextFirst);
|
|
548
|
+
}
|
|
549
|
+
if (nextFirst.kind === 'sequence-item') {
|
|
550
|
+
return this.refuse('flight-document.syntax.mixed-collection', nextFirst);
|
|
551
|
+
}
|
|
552
|
+
const nextColon = findSceneDocumentYamlSubsetTopLevelColon(nextLine.tokens, 0);
|
|
553
|
+
if (nextColon < 0)
|
|
554
|
+
return this.refuse('flight-document.syntax.expected-mapping-entry', nextFirst);
|
|
555
|
+
this.index++;
|
|
556
|
+
count = this.addMappingEntry(mapping, count, nextLine.tokens, nextColon, mappingIndent, depth);
|
|
557
|
+
if (count === PARSE_FAILURE)
|
|
558
|
+
return PARSE_FAILURE;
|
|
559
|
+
}
|
|
560
|
+
return mapping;
|
|
561
|
+
}
|
|
562
|
+
refuse(kind, token, limit = null, actual = null) {
|
|
563
|
+
this.refusal ??= this.createRefusal(kind, token, limit, actual);
|
|
564
|
+
return PARSE_FAILURE;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function createSceneDocumentYamlSubsetRefusal(kind, position, limit = null, actual = null) {
|
|
568
|
+
return {
|
|
569
|
+
ok: false,
|
|
570
|
+
kind,
|
|
571
|
+
limit,
|
|
572
|
+
actual,
|
|
573
|
+
offset: position.offset,
|
|
574
|
+
line: position.line,
|
|
575
|
+
column: position.column,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function createSceneDocumentYamlSubsetToken(kind, offset, line, column, value = '', quoted = false) {
|
|
579
|
+
return { kind, value, quoted, offset, line, column };
|
|
580
|
+
}
|
|
581
|
+
function findSceneDocumentYamlSubsetTopLevelColon(tokens, start) {
|
|
582
|
+
let flowDepth = 0;
|
|
583
|
+
for (let index = start; index < tokens.length; index++) {
|
|
584
|
+
const kind = tokens[index].kind;
|
|
585
|
+
if (kind === 'flow-mapping-start')
|
|
586
|
+
flowDepth++;
|
|
587
|
+
else if (kind === 'flow-mapping-end')
|
|
588
|
+
flowDepth--;
|
|
589
|
+
else if (kind === 'colon' && flowDepth === 0)
|
|
590
|
+
return index;
|
|
591
|
+
}
|
|
592
|
+
return -1;
|
|
593
|
+
}
|
|
594
|
+
function getSceneDocumentYamlSubsetPosition(source, offset) {
|
|
595
|
+
let line = 1;
|
|
596
|
+
let column = 1;
|
|
597
|
+
for (let index = 0; index < offset; index++) {
|
|
598
|
+
if (source[index] === '\n') {
|
|
599
|
+
line++;
|
|
600
|
+
column = 1;
|
|
601
|
+
}
|
|
602
|
+
else
|
|
603
|
+
column++;
|
|
604
|
+
}
|
|
605
|
+
return { offset, line, column };
|
|
606
|
+
}
|
|
607
|
+
function isSceneDocumentYamlSubsetDocumentSeparator(source, offset) {
|
|
608
|
+
const candidate = source.slice(offset, offset + 3);
|
|
609
|
+
return (candidate === '---' || candidate === '...') && isSceneDocumentYamlSubsetSeparation(source[offset + 3]);
|
|
610
|
+
}
|
|
611
|
+
function isSceneDocumentYamlSubsetLineBreak(character) {
|
|
612
|
+
return character === '\n' || character === '\r';
|
|
613
|
+
}
|
|
614
|
+
function isSceneDocumentYamlSubsetMappingColon(character) {
|
|
615
|
+
return isSceneDocumentYamlSubsetSeparation(character) || character === '{' || character === '"' || character === "'";
|
|
616
|
+
}
|
|
617
|
+
function isSceneDocumentYamlSubsetSeparation(character) {
|
|
618
|
+
return (character === undefined || character === ' ' || character === '\t' || isSceneDocumentYamlSubsetLineBreak(character));
|
|
619
|
+
}
|
|
620
|
+
function isSceneDocumentYamlSubsetUnsupportedIndicator(character) {
|
|
621
|
+
return (character === '&' ||
|
|
622
|
+
character === '*' ||
|
|
623
|
+
character === '!' ||
|
|
624
|
+
character === '[' ||
|
|
625
|
+
character === ']' ||
|
|
626
|
+
character === '|' ||
|
|
627
|
+
character === '>');
|
|
628
|
+
}
|
|
629
|
+
const MAX_DOCUMENT_CODE_UNITS = 4_194_304;
|
|
630
|
+
const MAX_NESTING_DEPTH = 64;
|
|
631
|
+
const MAX_COLLECTION_ENTRIES = 65_536;
|
|
632
|
+
const MAX_SCALAR_CODE_UNITS = 65_536;
|
|
633
|
+
const MAX_KEY_CODE_UNITS = 256;
|
|
634
|
+
const MAX_TOTAL_NODES = 262_144;
|
|
635
|
+
const DECIMAL_NUMBER_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
|
|
636
|
+
const HEX_INTEGER_PATTERN = /^0x[0-9a-fA-F]+$/;
|
|
637
|
+
const DOUBLE_QUOTED_ESCAPES = {
|
|
638
|
+
'"': '"',
|
|
639
|
+
'\\': '\\',
|
|
640
|
+
'/': '/',
|
|
641
|
+
b: '\b',
|
|
642
|
+
f: '\f',
|
|
643
|
+
n: '\n',
|
|
644
|
+
r: '\r',
|
|
645
|
+
t: '\t',
|
|
646
|
+
};
|
|
647
|
+
//# sourceMappingURL=sceneDocumentYamlSubset.js.map
|