@flighthq/scene-document 0.4.1-next.37.ff47e13 → 0.4.1-next.515.b0834fe
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 +2 -2
- package/dist/contract.d.ts +2 -1
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +2 -1
- package/dist/contract.js.map +1 -1
- package/dist/flightDocumentText.d.ts +5 -0
- package/dist/flightDocumentText.d.ts.map +1 -0
- package/dist/flightDocumentText.js +617 -0
- package/dist/flightDocumentText.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/sceneDocumentMaterializationSelection.d.ts +13 -0
- package/dist/sceneDocumentMaterializationSelection.d.ts.map +1 -0
- package/dist/sceneDocumentMaterializationSelection.js +55 -0
- package/dist/sceneDocumentMaterializationSelection.js.map +1 -0
- package/dist/sceneDocumentRefusal.d.ts +6 -0
- package/dist/sceneDocumentRefusal.d.ts.map +1 -0
- package/dist/sceneDocumentRefusal.js +60 -0
- package/dist/sceneDocumentRefusal.js.map +1 -0
- package/dist/sceneDocumentScene2DMaterialization.d.ts +4 -5
- package/dist/sceneDocumentScene2DMaterialization.d.ts.map +1 -1
- package/dist/sceneDocumentScene2DMaterialization.js +81 -277
- package/dist/sceneDocumentScene2DMaterialization.js.map +1 -1
- package/dist/sceneDocumentScene3DMaterialization.d.ts +4 -4
- package/dist/sceneDocumentScene3DMaterialization.d.ts.map +1 -1
- package/dist/sceneDocumentScene3DMaterialization.js +145 -291
- package/dist/sceneDocumentScene3DMaterialization.js.map +1 -1
- package/dist/sceneDocumentYamlSubset.d.ts +3 -1
- package/dist/sceneDocumentYamlSubset.d.ts.map +1 -1
- package/dist/sceneDocumentYamlSubset.js.map +1 -1
- package/package.json +10 -9
- package/src/flightDocumentText.test.ts +179 -0
- package/src/sceneDocumentMaterializationSelection.test.ts +56 -0
- package/src/sceneDocumentMultiSceneMaterialization.test.ts +209 -0
- package/src/sceneDocumentRefusal.test.ts +143 -0
- package/src/sceneDocumentScene2DMaterialization.test.ts +188 -62
- package/src/sceneDocumentScene3DMaterialization.test.ts +485 -159
- package/src/sceneDocumentYamlSubset.test.ts +8 -0
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/scene-document`. The
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/scene-document@0.4.1-next.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/scene-document@0.4.1-next.515.b0834fe](https://github.com/flighthq/flight/tree/b0834fe58fcec3eae0ebe12ee598d6ffac92d553/packages/scene-document)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/b0834fe58fcec3eae0ebe12ee598d6ffac92d553/LICENSE.md)
|
package/dist/contract.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { explainFlightDocumentText, formatFlightDocumentText, parseFlightDocumentText } from './flightDocumentText';
|
|
2
|
+
export { createFlightDocumentFromScene2D, createFlightDocumentScene2DMaterialization, createFlightDocumentScene2DMaterializationFromText, explainFlightDocumentRefusal, explainFlightDocumentRefusalFromText, } from './sceneDocumentScene2DMaterialization';
|
|
2
3
|
export { createFlightDocumentFromScene3D, createFlightDocumentScene3DMaterialization, createFlightDocumentScene3DMaterializationFromText, explainFlightDocumentScene3DRefusal, explainFlightDocumentScene3DRefusalFromText, } from './sceneDocumentScene3DMaterialization';
|
|
3
4
|
//# sourceMappingURL=contract.d.ts.map
|
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,+BAA+B,EAC/B,0CAA0C,EAC1C,kDAAkD,EAClD,4BAA4B,EAC5B,oCAAoC,
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACpH,OAAO,EACL,+BAA+B,EAC/B,0CAA0C,EAC1C,kDAAkD,EAClD,4BAA4B,EAC5B,oCAAoC,GACrC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,+BAA+B,EAC/B,0CAA0C,EAC1C,kDAAkD,EAClD,mCAAmC,EACnC,2CAA2C,GAC5C,MAAM,uCAAuC,CAAC"}
|
package/dist/contract.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { explainFlightDocumentText, formatFlightDocumentText, parseFlightDocumentText } from './flightDocumentText';
|
|
2
|
+
export { createFlightDocumentFromScene2D, createFlightDocumentScene2DMaterialization, createFlightDocumentScene2DMaterializationFromText, explainFlightDocumentRefusal, explainFlightDocumentRefusalFromText, } from './sceneDocumentScene2DMaterialization';
|
|
2
3
|
export { createFlightDocumentFromScene3D, createFlightDocumentScene3DMaterialization, createFlightDocumentScene3DMaterializationFromText, explainFlightDocumentScene3DRefusal, explainFlightDocumentScene3DRefusalFromText, } from './sceneDocumentScene3DMaterialization';
|
|
3
4
|
//# sourceMappingURL=contract.js.map
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,+BAA+B,EAC/B,0CAA0C,EAC1C,kDAAkD,EAClD,4BAA4B,EAC5B,oCAAoC,
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACpH,OAAO,EACL,+BAA+B,EAC/B,0CAA0C,EAC1C,kDAAkD,EAClD,4BAA4B,EAC5B,oCAAoC,GACrC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,+BAA+B,EAC/B,0CAA0C,EAC1C,kDAAkD,EAClD,mCAAmC,EACnC,2CAA2C,GAC5C,MAAM,uCAAuC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FlightDocument, FlightDocumentRefusalExplanation, FlightDocumentText } from '@flighthq/types/contract';
|
|
2
|
+
export declare function explainFlightDocumentText(text: FlightDocumentText): FlightDocumentRefusalExplanation | null;
|
|
3
|
+
export declare function formatFlightDocumentText(document: Readonly<FlightDocument>): FlightDocumentText;
|
|
4
|
+
export declare function parseFlightDocumentText(text: FlightDocumentText): FlightDocument | null;
|
|
5
|
+
//# sourceMappingURL=flightDocumentText.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flightDocumentText.d.ts","sourceRoot":"","sources":["../src/flightDocumentText.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAGd,gCAAgC,EAKhC,kBAAkB,EAOnB,MAAM,0BAA0B,CAAC;AAMlC,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,kBAAkB,GAAG,gCAAgC,GAAG,IAAI,CAE3G;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAsB/F;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,GAAG,cAAc,GAAG,IAAI,CAEvF"}
|
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
import { createTransform3D } from '@flighthq/geometry/contract';
|
|
2
|
+
import { EntityRuntimeKey, FlightDocumentRefusalReason } from '@flighthq/types/contract';
|
|
3
|
+
import { createDocumentRefusal } from './sceneDocumentRefusal';
|
|
4
|
+
import { parseSceneDocumentYamlSubset } from './sceneDocumentYamlSubset';
|
|
5
|
+
export function explainFlightDocumentText(text) {
|
|
6
|
+
return readFlightDocumentText(text).refusal;
|
|
7
|
+
}
|
|
8
|
+
export function formatFlightDocumentText(document) {
|
|
9
|
+
if (document.version !== 1)
|
|
10
|
+
throw new RangeError('FlightDocument.version must be 1');
|
|
11
|
+
if (document.scenes.length === 0)
|
|
12
|
+
throw new RangeError('FlightDocument.scenes must not be empty');
|
|
13
|
+
if (!Number.isInteger(document.defaultScene) ||
|
|
14
|
+
document.defaultScene < 0 ||
|
|
15
|
+
document.defaultScene >= document.scenes.length) {
|
|
16
|
+
throw new RangeError('FlightDocument.defaultScene must index FlightDocument.scenes');
|
|
17
|
+
}
|
|
18
|
+
const lines = ['flight: 1', 'defaultScene: ' + String(document.defaultScene)];
|
|
19
|
+
if (document.resources.length > 0)
|
|
20
|
+
appendResources(lines, document.resources);
|
|
21
|
+
lines.push('scenes:');
|
|
22
|
+
for (const scene of document.scenes)
|
|
23
|
+
appendScene(lines, scene);
|
|
24
|
+
const text = lines.join('\n') + '\n';
|
|
25
|
+
const emitted = parseSceneDocumentYamlSubset(text);
|
|
26
|
+
if (!emitted.ok) {
|
|
27
|
+
throw new RangeError('FlightDocument cannot be represented by the YAML subset: ' + emitted.kind);
|
|
28
|
+
}
|
|
29
|
+
return text;
|
|
30
|
+
}
|
|
31
|
+
export function parseFlightDocumentText(text) {
|
|
32
|
+
return readFlightDocumentText(text).document;
|
|
33
|
+
}
|
|
34
|
+
function appendCamera(lines, camera) {
|
|
35
|
+
lines.push(' - far: ' + formatNumber(camera.far));
|
|
36
|
+
if (camera.name !== undefined)
|
|
37
|
+
lines.push(' name: ' + formatString(camera.name));
|
|
38
|
+
lines.push(' near: ' + formatNumber(camera.near));
|
|
39
|
+
if (camera.node !== undefined)
|
|
40
|
+
lines.push(' node: ' + formatNumber(camera.node));
|
|
41
|
+
lines.push(' projection:');
|
|
42
|
+
appendProjection(lines, camera.projection, 10);
|
|
43
|
+
lines.push(' transform:');
|
|
44
|
+
appendTransform3D(lines, camera.transform, 10);
|
|
45
|
+
}
|
|
46
|
+
function appendFields(lines, fields, indent, reserved) {
|
|
47
|
+
for (const key of Object.keys(fields).sort()) {
|
|
48
|
+
if (reserved.includes(key))
|
|
49
|
+
throw new TypeError('FlightDocument field collides with structural key: ' + key);
|
|
50
|
+
appendMappingEntry(lines, key, fields[key], indent);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function appendLight(lines, light) {
|
|
54
|
+
lines.push(' - descriptor:');
|
|
55
|
+
lines.push(' kind: ' + formatString(light.descriptor.kind));
|
|
56
|
+
appendObjectEntries(lines, light.descriptor, 10, ['kind']);
|
|
57
|
+
if (light.name !== undefined)
|
|
58
|
+
lines.push(' name: ' + formatString(light.name));
|
|
59
|
+
if (light.node !== undefined)
|
|
60
|
+
lines.push(' node: ' + formatNumber(light.node));
|
|
61
|
+
lines.push(' transform:');
|
|
62
|
+
appendTransform3D(lines, light.transform, 10);
|
|
63
|
+
}
|
|
64
|
+
function appendMappingEntry(lines, key, value, indent) {
|
|
65
|
+
const prefix = ' '.repeat(indent) + formatKey(key) + ':';
|
|
66
|
+
if (isFlightDocumentScalar(value)) {
|
|
67
|
+
lines.push(prefix + ' ' + formatScalar(value));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
if (value.length === 0) {
|
|
72
|
+
throw new TypeError('The FlightDocument YAML subset cannot represent an empty sequence field');
|
|
73
|
+
}
|
|
74
|
+
lines.push(prefix);
|
|
75
|
+
appendSequence(lines, value, indent + 2);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!isMapping(value))
|
|
79
|
+
throw new TypeError('FlightDocument contains a value outside its YAML subset');
|
|
80
|
+
if (Object.keys(value).length === 0) {
|
|
81
|
+
lines.push(prefix + ' {}');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
lines.push(prefix);
|
|
85
|
+
appendObjectEntries(lines, value, indent + 2, []);
|
|
86
|
+
}
|
|
87
|
+
function appendNode(lines, node, indent) {
|
|
88
|
+
const prefix = ' '.repeat(indent);
|
|
89
|
+
lines.push(prefix + 'kind: ' + formatString(node.kind));
|
|
90
|
+
appendFields(lines, node.fields, indent, NODE_RESERVED_FIELDS);
|
|
91
|
+
if (node.children.length > 0) {
|
|
92
|
+
lines.push(prefix + 'children:');
|
|
93
|
+
for (const child of node.children)
|
|
94
|
+
appendNodeSequenceItem(lines, child, indent + 2);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function appendNodeSequenceItem(lines, node, indent) {
|
|
98
|
+
const prefix = ' '.repeat(indent);
|
|
99
|
+
lines.push(prefix + '- kind: ' + formatString(node.kind));
|
|
100
|
+
appendFields(lines, node.fields, indent + 2, NODE_RESERVED_FIELDS);
|
|
101
|
+
if (node.children.length > 0) {
|
|
102
|
+
lines.push(prefix + ' children:');
|
|
103
|
+
for (const child of node.children)
|
|
104
|
+
appendNodeSequenceItem(lines, child, indent + 4);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function appendObjectEntries(lines, value, indent, reserved) {
|
|
108
|
+
for (const [key, entry] of Object.entries(value).sort(([left], [right]) => left.localeCompare(right))) {
|
|
109
|
+
if (reserved.includes(key))
|
|
110
|
+
continue;
|
|
111
|
+
if (entry !== undefined)
|
|
112
|
+
appendMappingEntry(lines, key, entry, indent);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function appendProjection(lines, projection, indent) {
|
|
116
|
+
const prefix = ' '.repeat(indent);
|
|
117
|
+
if (projection.kind === 'orthographic') {
|
|
118
|
+
lines.push(prefix + 'halfHeight: ' + formatNumber(projection.halfHeight));
|
|
119
|
+
lines.push(prefix + 'halfWidth: ' + formatNumber(projection.halfWidth));
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
lines.push(prefix + 'aspect: ' + formatNumber(projection.aspect));
|
|
123
|
+
lines.push(prefix + 'fovY: ' + formatNumber(projection.fovY));
|
|
124
|
+
}
|
|
125
|
+
lines.push(prefix + 'kind: ' + projection.kind);
|
|
126
|
+
}
|
|
127
|
+
function appendResources(lines, resources) {
|
|
128
|
+
lines.push('resources:');
|
|
129
|
+
for (const resource of resources) {
|
|
130
|
+
lines.push(' - kind: ' + formatString(resource.kind));
|
|
131
|
+
lines.push(' key: ' + formatString(resource.key));
|
|
132
|
+
appendFields(lines, resource.fields, 4, RESOURCE_RESERVED_FIELDS);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function appendScene(lines, scene) {
|
|
136
|
+
lines.push(' - kind: ' + scene.kind);
|
|
137
|
+
if (scene.kind === 'Scene2D') {
|
|
138
|
+
if (scene.backgroundColor !== null) {
|
|
139
|
+
lines.push(' backgroundColor: ' + formatNumber(scene.backgroundColor));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
if (scene.cameras.length > 0) {
|
|
144
|
+
lines.push(' cameras:');
|
|
145
|
+
for (const camera of scene.cameras)
|
|
146
|
+
appendCamera(lines, camera);
|
|
147
|
+
}
|
|
148
|
+
if (scene.lights.length > 0) {
|
|
149
|
+
lines.push(' lights:');
|
|
150
|
+
for (const light of scene.lights)
|
|
151
|
+
appendLight(lines, light);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
lines.push(' scene:');
|
|
155
|
+
appendNode(lines, scene.scene, 6);
|
|
156
|
+
}
|
|
157
|
+
function appendSequence(lines, values, indent) {
|
|
158
|
+
const prefix = ' '.repeat(indent);
|
|
159
|
+
for (const value of values) {
|
|
160
|
+
if (isFlightDocumentScalar(value)) {
|
|
161
|
+
lines.push(prefix + '- ' + formatScalar(value));
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (Array.isArray(value)) {
|
|
165
|
+
if (value.length === 0) {
|
|
166
|
+
throw new TypeError('The FlightDocument YAML subset cannot represent an empty sequence field');
|
|
167
|
+
}
|
|
168
|
+
lines.push(prefix + '-');
|
|
169
|
+
appendSequence(lines, value, indent + 2);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (!isMapping(value))
|
|
173
|
+
throw new TypeError('FlightDocument contains a value outside its YAML subset');
|
|
174
|
+
if (Object.keys(value).length === 0) {
|
|
175
|
+
lines.push(prefix + '- {}');
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
lines.push(prefix + '-');
|
|
179
|
+
appendObjectEntries(lines, value, indent + 2, []);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function appendTransform3D(lines, transform, indent) {
|
|
183
|
+
const prefix = ' '.repeat(indent);
|
|
184
|
+
lines.push(prefix + 'position:');
|
|
185
|
+
appendVector3(lines, transform.position, indent + 2);
|
|
186
|
+
lines.push(prefix + 'rotation:');
|
|
187
|
+
lines.push(' '.repeat(indent + 2) + 'w: ' + formatNumber(transform.rotation.w));
|
|
188
|
+
appendVector3(lines, transform.rotation, indent + 2);
|
|
189
|
+
lines.push(prefix + 'scale:');
|
|
190
|
+
appendVector3(lines, transform.scale, indent + 2);
|
|
191
|
+
}
|
|
192
|
+
function appendVector3(lines, value, indent) {
|
|
193
|
+
const prefix = ' '.repeat(indent);
|
|
194
|
+
lines.push(prefix + 'x: ' + formatNumber(value.x));
|
|
195
|
+
lines.push(prefix + 'y: ' + formatNumber(value.y));
|
|
196
|
+
lines.push(prefix + 'z: ' + formatNumber(value.z));
|
|
197
|
+
}
|
|
198
|
+
function copyFlightDocumentFields(mapping, reserved, path, context) {
|
|
199
|
+
const fields = {};
|
|
200
|
+
for (const key of Object.keys(mapping)) {
|
|
201
|
+
if (reserved.includes(key))
|
|
202
|
+
continue;
|
|
203
|
+
const value = copyFlightDocumentValue(mapping[key], appendPath(path, key), context);
|
|
204
|
+
if (value === INVALID_FLIGHT_DOCUMENT_VALUE)
|
|
205
|
+
return null;
|
|
206
|
+
fields[key] = value;
|
|
207
|
+
}
|
|
208
|
+
return fields;
|
|
209
|
+
}
|
|
210
|
+
function copyFlightDocumentValue(value, path, context) {
|
|
211
|
+
if (isFlightDocumentScalar(value))
|
|
212
|
+
return value;
|
|
213
|
+
if (Array.isArray(value)) {
|
|
214
|
+
const out = [];
|
|
215
|
+
for (let index = 0; index < value.length; index++) {
|
|
216
|
+
const item = copyFlightDocumentValue(value[index], `${path}[${index}]`, context);
|
|
217
|
+
if (item === INVALID_FLIGHT_DOCUMENT_VALUE)
|
|
218
|
+
return item;
|
|
219
|
+
out.push(item);
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
if (!isMapping(value))
|
|
224
|
+
return refuseValue(context, FlightDocumentRefusalReason.StructureInvalid, path);
|
|
225
|
+
const out = {};
|
|
226
|
+
for (const key of Object.keys(value)) {
|
|
227
|
+
const item = copyFlightDocumentValue(value[key], appendPath(path, key), context);
|
|
228
|
+
if (item === INVALID_FLIGHT_DOCUMENT_VALUE)
|
|
229
|
+
return item;
|
|
230
|
+
out[key] = item;
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
function formatKey(value) {
|
|
235
|
+
return SAFE_PLAIN_SCALAR_PATTERN.test(value) ? value : JSON.stringify(value);
|
|
236
|
+
}
|
|
237
|
+
function formatNumber(value) {
|
|
238
|
+
if (!Number.isFinite(value))
|
|
239
|
+
throw new RangeError('FlightDocument numbers must be finite');
|
|
240
|
+
if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
|
|
241
|
+
throw new RangeError('FlightDocument integers must be safe integers');
|
|
242
|
+
}
|
|
243
|
+
return String(value);
|
|
244
|
+
}
|
|
245
|
+
function formatScalar(value) {
|
|
246
|
+
if (value === null)
|
|
247
|
+
return 'null';
|
|
248
|
+
if (typeof value === 'boolean')
|
|
249
|
+
return value ? 'true' : 'false';
|
|
250
|
+
if (typeof value === 'number')
|
|
251
|
+
return formatNumber(value);
|
|
252
|
+
return formatString(value);
|
|
253
|
+
}
|
|
254
|
+
function formatString(value) {
|
|
255
|
+
if (SAFE_PLAIN_SCALAR_PATTERN.test(value) && value !== 'false' && value !== 'null' && value !== 'true') {
|
|
256
|
+
return value;
|
|
257
|
+
}
|
|
258
|
+
return JSON.stringify(value);
|
|
259
|
+
}
|
|
260
|
+
function hasOnlyKeys(mapping, allowed) {
|
|
261
|
+
return Object.keys(mapping).every((key) => allowed.includes(key));
|
|
262
|
+
}
|
|
263
|
+
function isFlightDocumentScalar(value) {
|
|
264
|
+
return value === null || typeof value === 'boolean' || typeof value === 'number' || typeof value === 'string';
|
|
265
|
+
}
|
|
266
|
+
function isMapping(value) {
|
|
267
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
268
|
+
}
|
|
269
|
+
function readCamera(value, path, context) {
|
|
270
|
+
if (!isMapping(value) || !hasOnlyKeys(value, CAMERA_KEYS))
|
|
271
|
+
return refuse(context, path);
|
|
272
|
+
const far = value['far'];
|
|
273
|
+
const near = value['near'];
|
|
274
|
+
if (typeof far !== 'number')
|
|
275
|
+
return refuse(context, appendPath(path, 'far'));
|
|
276
|
+
if (typeof near !== 'number')
|
|
277
|
+
return refuse(context, appendPath(path, 'near'));
|
|
278
|
+
const projection = readProjection(value['projection'], appendPath(path, 'projection'), context);
|
|
279
|
+
if (projection === null)
|
|
280
|
+
return null;
|
|
281
|
+
const transform = readTransform3D(value['transform'], appendPath(path, 'transform'), context);
|
|
282
|
+
if (transform === null)
|
|
283
|
+
return null;
|
|
284
|
+
const camera = { far, near, projection, transform };
|
|
285
|
+
const name = value['name'];
|
|
286
|
+
if (name !== undefined) {
|
|
287
|
+
if (typeof name !== 'string')
|
|
288
|
+
return refuse(context, appendPath(path, 'name'));
|
|
289
|
+
camera.name = name;
|
|
290
|
+
}
|
|
291
|
+
const node = readOptionalIndex(value['node'], appendPath(path, 'node'), context);
|
|
292
|
+
if (node === INVALID_OPTIONAL_INDEX)
|
|
293
|
+
return null;
|
|
294
|
+
if (node !== null)
|
|
295
|
+
camera.node = node;
|
|
296
|
+
return camera;
|
|
297
|
+
}
|
|
298
|
+
function readFlightDocumentText(text) {
|
|
299
|
+
const parsed = parseSceneDocumentYamlSubset(text);
|
|
300
|
+
if (!parsed.ok) {
|
|
301
|
+
return {
|
|
302
|
+
document: null,
|
|
303
|
+
refusal: {
|
|
304
|
+
actual: parsed.actual,
|
|
305
|
+
column: parsed.column,
|
|
306
|
+
kind: null,
|
|
307
|
+
limit: parsed.limit,
|
|
308
|
+
line: parsed.line,
|
|
309
|
+
offset: parsed.offset,
|
|
310
|
+
path: '',
|
|
311
|
+
reason: parsed.kind,
|
|
312
|
+
resourceKey: null,
|
|
313
|
+
version: null,
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
const context = { refusal: null };
|
|
318
|
+
if (!isMapping(parsed.value) || !hasOnlyKeys(parsed.value, DOCUMENT_KEYS)) {
|
|
319
|
+
return { document: null, refusal: createDocumentRefusal(FlightDocumentRefusalReason.StructureInvalid, '') };
|
|
320
|
+
}
|
|
321
|
+
const mapping = parsed.value;
|
|
322
|
+
const version = mapping['flight'];
|
|
323
|
+
if (typeof version !== 'number') {
|
|
324
|
+
return { document: null, refusal: createDocumentRefusal(FlightDocumentRefusalReason.StructureInvalid, 'flight') };
|
|
325
|
+
}
|
|
326
|
+
if (version !== 1) {
|
|
327
|
+
const refusal = createDocumentRefusal(FlightDocumentRefusalReason.VersionUnsupported, 'version');
|
|
328
|
+
refusal.version = version;
|
|
329
|
+
return { document: null, refusal };
|
|
330
|
+
}
|
|
331
|
+
const scenesRaw = mapping['scenes'];
|
|
332
|
+
if (scenesRaw === null) {
|
|
333
|
+
return { document: null, refusal: createDocumentRefusal(FlightDocumentRefusalReason.ScenesEmpty, 'scenes') };
|
|
334
|
+
}
|
|
335
|
+
if (!Array.isArray(scenesRaw)) {
|
|
336
|
+
return { document: null, refusal: createDocumentRefusal(FlightDocumentRefusalReason.StructureInvalid, 'scenes') };
|
|
337
|
+
}
|
|
338
|
+
if (scenesRaw.length === 0) {
|
|
339
|
+
return { document: null, refusal: createDocumentRefusal(FlightDocumentRefusalReason.ScenesEmpty, 'scenes') };
|
|
340
|
+
}
|
|
341
|
+
const defaultScene = mapping['defaultScene'];
|
|
342
|
+
if (typeof defaultScene !== 'number' || !Number.isInteger(defaultScene)) {
|
|
343
|
+
return {
|
|
344
|
+
document: null,
|
|
345
|
+
refusal: createDocumentRefusal(FlightDocumentRefusalReason.StructureInvalid, 'defaultScene'),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (defaultScene < 0 || defaultScene >= scenesRaw.length) {
|
|
349
|
+
const refusal = createDocumentRefusal(FlightDocumentRefusalReason.DefaultSceneOutOfRange, 'defaultScene');
|
|
350
|
+
refusal.actual = defaultScene;
|
|
351
|
+
refusal.limit = scenesRaw.length - 1;
|
|
352
|
+
return { document: null, refusal };
|
|
353
|
+
}
|
|
354
|
+
const resources = readResources(mapping['resources'], context);
|
|
355
|
+
if (resources === null)
|
|
356
|
+
return { document: null, refusal: context.refusal };
|
|
357
|
+
const scenes = [];
|
|
358
|
+
for (let index = 0; index < scenesRaw.length; index++) {
|
|
359
|
+
const scene = readScene(scenesRaw[index], index, context);
|
|
360
|
+
if (scene === null)
|
|
361
|
+
return { document: null, refusal: context.refusal };
|
|
362
|
+
scenes.push(scene);
|
|
363
|
+
}
|
|
364
|
+
const firstScene = scenes[0];
|
|
365
|
+
if (firstScene === undefined) {
|
|
366
|
+
return { document: null, refusal: createDocumentRefusal(FlightDocumentRefusalReason.ScenesEmpty, 'scenes') };
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
document: {
|
|
370
|
+
defaultScene,
|
|
371
|
+
resources,
|
|
372
|
+
scenes: [firstScene, ...scenes.slice(1)],
|
|
373
|
+
version: 1,
|
|
374
|
+
},
|
|
375
|
+
refusal: null,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function readLight(value, path, context) {
|
|
379
|
+
if (!isMapping(value) || !hasOnlyKeys(value, LIGHT_KEYS))
|
|
380
|
+
return refuse(context, path);
|
|
381
|
+
const descriptorRaw = value['descriptor'];
|
|
382
|
+
const descriptorPath = appendPath(path, 'descriptor');
|
|
383
|
+
if (!isMapping(descriptorRaw))
|
|
384
|
+
return refuse(context, descriptorPath);
|
|
385
|
+
const kind = descriptorRaw['kind'];
|
|
386
|
+
if (typeof kind !== 'string')
|
|
387
|
+
return refuse(context, appendPath(descriptorPath, 'kind'));
|
|
388
|
+
const descriptorFields = copyFlightDocumentFields(descriptorRaw, ['kind'], descriptorPath, context);
|
|
389
|
+
if (descriptorFields === null)
|
|
390
|
+
return null;
|
|
391
|
+
// Light is an open kind family, so the text codec cannot select a closed concrete constructor. The
|
|
392
|
+
// logical descriptor still receives the standard entity runtime slot; materialization owns any
|
|
393
|
+
// kind-specific interpretation and runtime construction.
|
|
394
|
+
const descriptor = Object.assign({ [EntityRuntimeKey]: undefined, kind }, descriptorFields);
|
|
395
|
+
const transform = readTransform3D(value['transform'], appendPath(path, 'transform'), context);
|
|
396
|
+
if (transform === null)
|
|
397
|
+
return null;
|
|
398
|
+
const light = { descriptor, transform };
|
|
399
|
+
const name = value['name'];
|
|
400
|
+
if (name !== undefined) {
|
|
401
|
+
if (typeof name !== 'string')
|
|
402
|
+
return refuse(context, appendPath(path, 'name'));
|
|
403
|
+
light.name = name;
|
|
404
|
+
}
|
|
405
|
+
const node = readOptionalIndex(value['node'], appendPath(path, 'node'), context);
|
|
406
|
+
if (node === INVALID_OPTIONAL_INDEX)
|
|
407
|
+
return null;
|
|
408
|
+
if (node !== null)
|
|
409
|
+
light.node = node;
|
|
410
|
+
return light;
|
|
411
|
+
}
|
|
412
|
+
function readNode(value, path, context) {
|
|
413
|
+
if (!isMapping(value))
|
|
414
|
+
return refuse(context, path);
|
|
415
|
+
const kind = value['kind'];
|
|
416
|
+
if (typeof kind !== 'string')
|
|
417
|
+
return refuse(context, appendPath(path, 'kind'));
|
|
418
|
+
const childrenRaw = value['children'];
|
|
419
|
+
const children = [];
|
|
420
|
+
if (childrenRaw !== undefined) {
|
|
421
|
+
if (!Array.isArray(childrenRaw))
|
|
422
|
+
return refuse(context, appendPath(path, 'children'));
|
|
423
|
+
for (let index = 0; index < childrenRaw.length; index++) {
|
|
424
|
+
const child = readNode(childrenRaw[index], `${path}.children[${index}]`, context);
|
|
425
|
+
if (child === null)
|
|
426
|
+
return null;
|
|
427
|
+
children.push(child);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const fields = copyFlightDocumentFields(value, NODE_RESERVED_FIELDS, path, context);
|
|
431
|
+
return fields === null ? null : { children, fields, kind };
|
|
432
|
+
}
|
|
433
|
+
function readOptionalIndex(value, path, context) {
|
|
434
|
+
if (value === undefined)
|
|
435
|
+
return null;
|
|
436
|
+
if (typeof value === 'number' && Number.isInteger(value) && value >= 0)
|
|
437
|
+
return value;
|
|
438
|
+
refuse(context, path);
|
|
439
|
+
return INVALID_OPTIONAL_INDEX;
|
|
440
|
+
}
|
|
441
|
+
function readProjection(value, path, context) {
|
|
442
|
+
if (!isMapping(value))
|
|
443
|
+
return refuse(context, path);
|
|
444
|
+
const kind = value['kind'];
|
|
445
|
+
if (kind === 'orthographic') {
|
|
446
|
+
if (!hasOnlyKeys(value, ORTHOGRAPHIC_PROJECTION_KEYS))
|
|
447
|
+
return refuse(context, path);
|
|
448
|
+
const halfHeight = value['halfHeight'];
|
|
449
|
+
const halfWidth = value['halfWidth'];
|
|
450
|
+
if (typeof halfHeight !== 'number')
|
|
451
|
+
return refuse(context, appendPath(path, 'halfHeight'));
|
|
452
|
+
if (typeof halfWidth !== 'number')
|
|
453
|
+
return refuse(context, appendPath(path, 'halfWidth'));
|
|
454
|
+
return { halfHeight, halfWidth, kind };
|
|
455
|
+
}
|
|
456
|
+
if (kind === 'perspective') {
|
|
457
|
+
if (!hasOnlyKeys(value, PERSPECTIVE_PROJECTION_KEYS))
|
|
458
|
+
return refuse(context, path);
|
|
459
|
+
const aspect = value['aspect'];
|
|
460
|
+
const fovY = value['fovY'];
|
|
461
|
+
if (typeof aspect !== 'number')
|
|
462
|
+
return refuse(context, appendPath(path, 'aspect'));
|
|
463
|
+
if (typeof fovY !== 'number')
|
|
464
|
+
return refuse(context, appendPath(path, 'fovY'));
|
|
465
|
+
return { aspect, fovY, kind };
|
|
466
|
+
}
|
|
467
|
+
return refuse(context, appendPath(path, 'kind'));
|
|
468
|
+
}
|
|
469
|
+
function readQuaternion(value, path, context) {
|
|
470
|
+
if (!isMapping(value) || !hasOnlyKeys(value, QUATERNION_KEYS))
|
|
471
|
+
return refuse(context, path);
|
|
472
|
+
const vector = readVector3(value, path, context, QUATERNION_KEYS);
|
|
473
|
+
if (vector === null)
|
|
474
|
+
return null;
|
|
475
|
+
const w = value['w'];
|
|
476
|
+
if (typeof w !== 'number')
|
|
477
|
+
return refuse(context, appendPath(path, 'w'));
|
|
478
|
+
return { ...vector, w };
|
|
479
|
+
}
|
|
480
|
+
function readResources(value, context) {
|
|
481
|
+
if (value === undefined)
|
|
482
|
+
return [];
|
|
483
|
+
if (!Array.isArray(value))
|
|
484
|
+
return refuse(context, 'resources');
|
|
485
|
+
const resources = [];
|
|
486
|
+
for (let index = 0; index < value.length; index++) {
|
|
487
|
+
const path = `resources[${index}]`;
|
|
488
|
+
const raw = value[index];
|
|
489
|
+
if (!isMapping(raw))
|
|
490
|
+
return refuse(context, path);
|
|
491
|
+
const kind = raw['kind'];
|
|
492
|
+
const key = raw['key'];
|
|
493
|
+
if (typeof kind !== 'string')
|
|
494
|
+
return refuse(context, appendPath(path, 'kind'));
|
|
495
|
+
if (typeof key !== 'string')
|
|
496
|
+
return refuse(context, appendPath(path, 'key'));
|
|
497
|
+
const fields = copyFlightDocumentFields(raw, RESOURCE_RESERVED_FIELDS, path, context);
|
|
498
|
+
if (fields === null)
|
|
499
|
+
return null;
|
|
500
|
+
resources.push({ fields, key, kind });
|
|
501
|
+
}
|
|
502
|
+
return resources;
|
|
503
|
+
}
|
|
504
|
+
function readScene(value, index, context) {
|
|
505
|
+
const path = `scenes[${index}]`;
|
|
506
|
+
if (!isMapping(value))
|
|
507
|
+
return refuse(context, path);
|
|
508
|
+
const kind = value['kind'];
|
|
509
|
+
if (kind === 'Scene2D')
|
|
510
|
+
return readScene2D(value, path, context);
|
|
511
|
+
if (kind === 'Scene3D')
|
|
512
|
+
return readScene3D(value, path, context);
|
|
513
|
+
return refuse(context, appendPath(path, 'kind'));
|
|
514
|
+
}
|
|
515
|
+
function readScene2D(mapping, path, context) {
|
|
516
|
+
if (!hasOnlyKeys(mapping, SCENE_2D_KEYS))
|
|
517
|
+
return refuse(context, path);
|
|
518
|
+
const backgroundColor = mapping['backgroundColor'];
|
|
519
|
+
if (backgroundColor !== undefined && backgroundColor !== null && typeof backgroundColor !== 'number') {
|
|
520
|
+
return refuse(context, appendPath(path, 'backgroundColor'));
|
|
521
|
+
}
|
|
522
|
+
const scene = readNode(mapping['scene'], appendPath(path, 'scene'), context);
|
|
523
|
+
if (scene === null)
|
|
524
|
+
return null;
|
|
525
|
+
return { backgroundColor: backgroundColor ?? null, kind: 'Scene2D', scene };
|
|
526
|
+
}
|
|
527
|
+
function readScene3D(mapping, path, context) {
|
|
528
|
+
if (!hasOnlyKeys(mapping, SCENE_3D_KEYS))
|
|
529
|
+
return refuse(context, path);
|
|
530
|
+
const camerasRaw = mapping['cameras'];
|
|
531
|
+
const cameras = [];
|
|
532
|
+
if (camerasRaw !== undefined) {
|
|
533
|
+
if (!Array.isArray(camerasRaw))
|
|
534
|
+
return refuse(context, appendPath(path, 'cameras'));
|
|
535
|
+
for (let index = 0; index < camerasRaw.length; index++) {
|
|
536
|
+
const camera = readCamera(camerasRaw[index], `${path}.cameras[${index}]`, context);
|
|
537
|
+
if (camera === null)
|
|
538
|
+
return null;
|
|
539
|
+
cameras.push(camera);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const lightsRaw = mapping['lights'];
|
|
543
|
+
const lights = [];
|
|
544
|
+
if (lightsRaw !== undefined) {
|
|
545
|
+
if (!Array.isArray(lightsRaw))
|
|
546
|
+
return refuse(context, appendPath(path, 'lights'));
|
|
547
|
+
for (let index = 0; index < lightsRaw.length; index++) {
|
|
548
|
+
const light = readLight(lightsRaw[index], `${path}.lights[${index}]`, context);
|
|
549
|
+
if (light === null)
|
|
550
|
+
return null;
|
|
551
|
+
lights.push(light);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
const scene = readNode(mapping['scene'], appendPath(path, 'scene'), context);
|
|
555
|
+
if (scene === null)
|
|
556
|
+
return null;
|
|
557
|
+
return { cameras, kind: 'Scene3D', lights, scene };
|
|
558
|
+
}
|
|
559
|
+
function readTransform3D(value, path, context) {
|
|
560
|
+
if (!isMapping(value) || !hasOnlyKeys(value, TRANSFORM_3D_KEYS))
|
|
561
|
+
return refuse(context, path);
|
|
562
|
+
const position = readVector3(value['position'], appendPath(path, 'position'), context, VECTOR_3_KEYS);
|
|
563
|
+
if (position === null)
|
|
564
|
+
return null;
|
|
565
|
+
const rotation = readQuaternion(value['rotation'], appendPath(path, 'rotation'), context);
|
|
566
|
+
if (rotation === null)
|
|
567
|
+
return null;
|
|
568
|
+
const scale = readVector3(value['scale'], appendPath(path, 'scale'), context, VECTOR_3_KEYS);
|
|
569
|
+
if (scale === null)
|
|
570
|
+
return null;
|
|
571
|
+
const transform = createTransform3D();
|
|
572
|
+
Object.assign(transform.position, position);
|
|
573
|
+
Object.assign(transform.rotation, rotation);
|
|
574
|
+
Object.assign(transform.scale, scale);
|
|
575
|
+
return transform;
|
|
576
|
+
}
|
|
577
|
+
function readVector3(value, path, context, allowed) {
|
|
578
|
+
if (!isMapping(value) || !hasOnlyKeys(value, allowed))
|
|
579
|
+
return refuse(context, path);
|
|
580
|
+
const x = value['x'];
|
|
581
|
+
const y = value['y'];
|
|
582
|
+
const z = value['z'];
|
|
583
|
+
if (typeof x !== 'number')
|
|
584
|
+
return refuse(context, appendPath(path, 'x'));
|
|
585
|
+
if (typeof y !== 'number')
|
|
586
|
+
return refuse(context, appendPath(path, 'y'));
|
|
587
|
+
if (typeof z !== 'number')
|
|
588
|
+
return refuse(context, appendPath(path, 'z'));
|
|
589
|
+
return { x, y, z };
|
|
590
|
+
}
|
|
591
|
+
function refuse(context, path) {
|
|
592
|
+
context.refusal ??= createDocumentRefusal(FlightDocumentRefusalReason.StructureInvalid, path);
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
function refuseValue(context, reason, path) {
|
|
596
|
+
context.refusal ??= createDocumentRefusal(reason, path);
|
|
597
|
+
return INVALID_FLIGHT_DOCUMENT_VALUE;
|
|
598
|
+
}
|
|
599
|
+
function appendPath(path, key) {
|
|
600
|
+
return path === '' ? key : path + '.' + key;
|
|
601
|
+
}
|
|
602
|
+
const INVALID_FLIGHT_DOCUMENT_VALUE = Symbol('invalid-flight-document-value');
|
|
603
|
+
const INVALID_OPTIONAL_INDEX = Symbol('invalid-flight-document-optional-index');
|
|
604
|
+
const CAMERA_KEYS = ['far', 'name', 'near', 'node', 'projection', 'transform'];
|
|
605
|
+
const DOCUMENT_KEYS = ['defaultScene', 'flight', 'resources', 'scenes'];
|
|
606
|
+
const LIGHT_KEYS = ['descriptor', 'name', 'node', 'transform'];
|
|
607
|
+
const NODE_RESERVED_FIELDS = ['children', 'kind'];
|
|
608
|
+
const ORTHOGRAPHIC_PROJECTION_KEYS = ['halfHeight', 'halfWidth', 'kind'];
|
|
609
|
+
const PERSPECTIVE_PROJECTION_KEYS = ['aspect', 'fovY', 'kind'];
|
|
610
|
+
const QUATERNION_KEYS = ['w', 'x', 'y', 'z'];
|
|
611
|
+
const RESOURCE_RESERVED_FIELDS = ['key', 'kind'];
|
|
612
|
+
const SAFE_PLAIN_SCALAR_PATTERN = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
|
|
613
|
+
const SCENE_2D_KEYS = ['backgroundColor', 'kind', 'scene'];
|
|
614
|
+
const SCENE_3D_KEYS = ['cameras', 'kind', 'lights', 'scene'];
|
|
615
|
+
const TRANSFORM_3D_KEYS = ['position', 'rotation', 'scale'];
|
|
616
|
+
const VECTOR_3_KEYS = ['x', 'y', 'z'];
|
|
617
|
+
//# sourceMappingURL=flightDocumentText.js.map
|