worldorbit 2.5.13 → 2.5.16

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.
Files changed (41) hide show
  1. package/README.md +37 -11
  2. package/dist/browser/core/dist/index.js +1811 -386
  3. package/dist/browser/editor/dist/index.js +10534 -0
  4. package/dist/browser/markdown/dist/index.js +1477 -221
  5. package/dist/browser/viewer/dist/index.js +1569 -230
  6. package/dist/unpkg/core/dist/index.js +1814 -389
  7. package/dist/unpkg/editor/dist/index.js +10559 -0
  8. package/dist/unpkg/markdown/dist/index.js +1480 -224
  9. package/dist/unpkg/viewer/dist/index.js +1572 -233
  10. package/dist/unpkg/worldorbit-core.min.js +12 -5
  11. package/dist/unpkg/worldorbit-editor.min.js +812 -0
  12. package/dist/unpkg/worldorbit-markdown.min.js +32 -23
  13. package/dist/unpkg/worldorbit-viewer.min.js +55 -41
  14. package/dist/unpkg/worldorbit.js +1713 -231
  15. package/dist/unpkg/worldorbit.min.js +58 -44
  16. package/package.json +3 -2
  17. package/packages/core/README.md +5 -1
  18. package/packages/core/dist/atlas-edit.d.ts +2 -2
  19. package/packages/core/dist/atlas-edit.js +70 -7
  20. package/packages/core/dist/atlas-utils.d.ts +22 -0
  21. package/packages/core/dist/atlas-utils.js +189 -0
  22. package/packages/core/dist/atlas-validate.d.ts +2 -0
  23. package/packages/core/dist/atlas-validate.js +285 -0
  24. package/packages/core/dist/draft-parse.js +786 -153
  25. package/packages/core/dist/draft.d.ts +3 -0
  26. package/packages/core/dist/draft.js +47 -3
  27. package/packages/core/dist/format.js +165 -9
  28. package/packages/core/dist/load.js +58 -13
  29. package/packages/core/dist/normalize.js +7 -0
  30. package/packages/core/dist/scene.js +66 -13
  31. package/packages/core/dist/types.d.ts +97 -3
  32. package/packages/editor/dist/editor.js +44 -0
  33. package/packages/markdown/README.md +1 -1
  34. package/packages/viewer/README.md +2 -1
  35. package/packages/viewer/dist/atlas-state.js +7 -1
  36. package/packages/viewer/dist/atlas-viewer.js +35 -1
  37. package/packages/viewer/dist/render.js +16 -7
  38. package/packages/viewer/dist/theme.js +4 -0
  39. package/packages/viewer/dist/tooltip.js +35 -0
  40. package/packages/viewer/dist/types.d.ts +7 -0
  41. package/packages/viewer/dist/viewer.js +4 -0
@@ -4,9 +4,12 @@ interface UpgradeOptions extends Pick<SceneRenderOptions, "preset" | "projection
4
4
  export declare function upgradeDocumentToV2(document: WorldOrbitDocument, options?: UpgradeOptions): WorldOrbitAtlasDocument;
5
5
  export declare function upgradeDocumentToDraftV2(document: WorldOrbitDocument, options?: UpgradeOptions): {
6
6
  version: "2.0-draft";
7
+ schemaVersion: "2.0-draft";
7
8
  format: "worldorbit";
8
9
  sourceVersion: import("./types.js").WorldOrbitDocumentVersion;
9
10
  system: WorldOrbitAtlasSystem | null;
11
+ groups: import("./types.js").WorldOrbitGroup[];
12
+ relations: import("./types.js").WorldOrbitRelation[];
10
13
  objects: WorldOrbitObject[];
11
14
  diagnostics: WorldOrbitDiagnostic[];
12
15
  };
@@ -19,8 +19,11 @@ export function upgradeDocumentToV2(document, options = {}) {
19
19
  return {
20
20
  format: "worldorbit",
21
21
  version: "2.0",
22
+ schemaVersion: "2.0",
22
23
  sourceVersion: document.version,
23
24
  system,
25
+ groups: structuredClone(document.groups ?? []),
26
+ relations: structuredClone(document.relations ?? []),
24
27
  objects: document.objects.map(cloneWorldOrbitObject),
25
28
  diagnostics,
26
29
  };
@@ -33,6 +36,10 @@ export function materializeAtlasDocument(document) {
33
36
  ? {
34
37
  type: "system",
35
38
  id: document.system.id,
39
+ title: document.system.title,
40
+ description: document.system.description,
41
+ epoch: document.system.epoch,
42
+ referencePlane: document.system.referencePlane,
36
43
  properties: materializeDraftSystemProperties(document.system),
37
44
  info: materializeDraftSystemInfo(document.system),
38
45
  }
@@ -40,7 +47,10 @@ export function materializeAtlasDocument(document) {
40
47
  return {
41
48
  format: "worldorbit",
42
49
  version: "1.0",
50
+ schemaVersion: document.version,
43
51
  system,
52
+ groups: structuredClone(document.groups ?? []),
53
+ relations: structuredClone(document.relations ?? []),
44
54
  objects: document.objects.map(cloneWorldOrbitObject),
45
55
  };
46
56
  }
@@ -55,9 +65,12 @@ function createDraftSystem(document, defaults, atlasMetadata, annotations, diagn
55
65
  return {
56
66
  type: "system",
57
67
  id: document.system?.id ?? "WorldOrbit",
58
- title: typeof document.system?.properties.title === "string"
68
+ title: document.system?.title ?? (typeof document.system?.properties.title === "string"
59
69
  ? document.system.properties.title
60
- : null,
70
+ : null),
71
+ description: document.system?.description ?? null,
72
+ epoch: document.system?.epoch ?? null,
73
+ referencePlane: document.system?.referencePlane ?? null,
61
74
  defaults,
62
75
  atlasMetadata,
63
76
  viewpoints: scene.viewpoints.map(mapSceneViewpointToDraftViewpoint),
@@ -200,6 +213,27 @@ function mapSceneViewpointToDraftViewpoint(viewpoint) {
200
213
  function cloneWorldOrbitObject(object) {
201
214
  return {
202
215
  ...object,
216
+ groups: object.groups ? [...object.groups] : undefined,
217
+ resonance: object.resonance ? { ...object.resonance } : object.resonance,
218
+ renderHints: object.renderHints ? { ...object.renderHints } : object.renderHints,
219
+ deriveRules: object.deriveRules ? object.deriveRules.map((rule) => ({ ...rule })) : undefined,
220
+ validationRules: object.validationRules
221
+ ? object.validationRules.map((rule) => ({ ...rule }))
222
+ : undefined,
223
+ lockedFields: object.lockedFields ? [...object.lockedFields] : undefined,
224
+ tolerances: object.tolerances
225
+ ? object.tolerances.map((entry) => ({
226
+ field: entry.field,
227
+ value: entry.value && typeof entry.value === "object" && "value" in entry.value
228
+ ? { value: entry.value.value, unit: entry.value.unit }
229
+ : Array.isArray(entry.value)
230
+ ? [...entry.value]
231
+ : entry.value,
232
+ }))
233
+ : undefined,
234
+ typedBlocks: object.typedBlocks
235
+ ? Object.fromEntries(Object.entries(object.typedBlocks).map(([key, block]) => [key, { ...(block ?? {}) }]))
236
+ : undefined,
203
237
  properties: cloneProperties(object.properties),
204
238
  placement: object.placement ? structuredClone(object.placement) : null,
205
239
  info: { ...object.info },
@@ -255,6 +289,15 @@ function materializeDraftSystemProperties(system) {
255
289
  if (system.defaults.units) {
256
290
  properties.units = system.defaults.units;
257
291
  }
292
+ if (system.description) {
293
+ properties.description = system.description;
294
+ }
295
+ if (system.epoch) {
296
+ properties.epoch = system.epoch;
297
+ }
298
+ if (system.referencePlane) {
299
+ properties.referencePlane = system.referencePlane;
300
+ }
258
301
  return properties;
259
302
  }
260
303
  function materializeDraftSystemInfo(system) {
@@ -328,7 +371,7 @@ function serializeViewpointLayers(layers) {
328
371
  if (orbitFront !== undefined || orbitBack !== undefined) {
329
372
  tokens.push(orbitFront !== false || orbitBack !== false ? "orbits" : "-orbits");
330
373
  }
331
- for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
374
+ for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
332
375
  if (layers[key] !== undefined) {
333
376
  tokens.push(layers[key] ? key : `-${key}`);
334
377
  }
@@ -339,5 +382,6 @@ function convertAtlasDocumentToLegacyDraft(document) {
339
382
  return {
340
383
  ...document,
341
384
  version: "2.0-draft",
385
+ schemaVersion: "2.0-draft",
342
386
  };
343
387
  }
@@ -37,29 +37,40 @@ const CANONICAL_FIELD_ORDER = [
37
37
  export function formatDocument(document, options = {}) {
38
38
  const schema = options.schema ?? "auto";
39
39
  const useDraft = schema === "2.0" ||
40
+ schema === "2.1" ||
40
41
  schema === "2.0-draft" ||
41
42
  document.version === "2.0" ||
43
+ document.version === "2.1" ||
42
44
  document.version === "2.0-draft";
43
45
  if (useDraft) {
44
46
  if (schema === "2.0-draft") {
45
47
  const legacyDraftDocument = document.version === "2.0-draft"
46
48
  ? document
47
- : document.version === "2.0"
49
+ : document.version === "2.0" || document.version === "2.1"
48
50
  ? {
49
51
  ...document,
50
52
  version: "2.0-draft",
53
+ schemaVersion: "2.0-draft",
51
54
  }
52
55
  : upgradeDocumentToDraftV2(document);
53
56
  return formatDraftDocument(legacyDraftDocument);
54
57
  }
55
- const atlasDocument = document.version === "2.0"
58
+ const atlasDocument = document.version === "2.0" || document.version === "2.1"
56
59
  ? document
57
60
  : document.version === "2.0-draft"
58
61
  ? {
59
62
  ...document,
60
63
  version: "2.0",
64
+ schemaVersion: "2.0",
61
65
  }
62
66
  : upgradeDocumentToV2(document);
67
+ if (schema === "2.1" && atlasDocument.version !== "2.1") {
68
+ return formatAtlasDocument({
69
+ ...atlasDocument,
70
+ version: "2.1",
71
+ schemaVersion: "2.1",
72
+ });
73
+ }
63
74
  return formatAtlasDocument(atlasDocument);
64
75
  }
65
76
  const lines = [];
@@ -77,10 +88,18 @@ export function formatDocument(document, options = {}) {
77
88
  return lines.join("\n");
78
89
  }
79
90
  export function formatAtlasDocument(document) {
80
- const lines = ["schema 2.0", ""];
91
+ const lines = [`schema ${document.version}`, ""];
81
92
  if (document.system) {
82
93
  lines.push(...formatAtlasSystem(document.system));
83
94
  }
95
+ for (const group of [...document.groups].sort(compareIdLike)) {
96
+ lines.push("");
97
+ lines.push(...formatAtlasGroup(group));
98
+ }
99
+ for (const relation of [...document.relations].sort(compareIdLike)) {
100
+ lines.push("");
101
+ lines.push(...formatAtlasRelation(relation));
102
+ }
84
103
  const sortedObjects = [...document.objects].sort(compareObjects);
85
104
  if (sortedObjects.length > 0 && lines.at(-1) !== "") {
86
105
  lines.push("");
@@ -99,11 +118,20 @@ export function formatDraftDocument(document) {
99
118
  : {
100
119
  ...document,
101
120
  version: "2.0-draft",
121
+ schemaVersion: "2.0-draft",
102
122
  };
103
123
  const lines = ["schema 2.0-draft", ""];
104
124
  if (legacy.system) {
105
125
  lines.push(...formatAtlasSystem(legacy.system));
106
126
  }
127
+ for (const group of [...legacy.groups].sort(compareIdLike)) {
128
+ lines.push("");
129
+ lines.push(...formatAtlasGroup(group));
130
+ }
131
+ for (const relation of [...legacy.relations].sort(compareIdLike)) {
132
+ lines.push("");
133
+ lines.push(...formatAtlasRelation(relation));
134
+ }
107
135
  const sortedObjects = [...legacy.objects].sort(compareObjects);
108
136
  if (sortedObjects.length > 0 && lines.at(-1) !== "") {
109
137
  lines.push("");
@@ -119,11 +147,38 @@ export function formatDraftDocument(document) {
119
147
  function formatSystem(system) {
120
148
  return formatLines("system", system.id, system.properties, null, system.info);
121
149
  }
150
+ function formatLines(objectType, id, properties, placement, info) {
151
+ const lines = [`${objectType} ${id}`];
152
+ const fieldLines = [...formatPlacement(placement), ...formatProperties(properties)];
153
+ for (const fieldLine of fieldLines) {
154
+ lines.push(` ${fieldLine}`);
155
+ }
156
+ const infoEntries = Object.entries(info).sort(([left], [right]) => left.localeCompare(right));
157
+ if (infoEntries.length > 0) {
158
+ if (fieldLines.length > 0) {
159
+ lines.push("");
160
+ }
161
+ lines.push(" info");
162
+ for (const [key, value] of infoEntries) {
163
+ lines.push(` ${key} ${quoteIfNeeded(value)}`);
164
+ }
165
+ }
166
+ return lines;
167
+ }
122
168
  function formatAtlasSystem(system) {
123
169
  const lines = [`system ${system.id}`];
124
170
  if (system.title) {
125
171
  lines.push(` title ${quoteIfNeeded(system.title)}`);
126
172
  }
173
+ if (system.description) {
174
+ lines.push(` description ${quoteIfNeeded(system.description)}`);
175
+ }
176
+ if (system.epoch) {
177
+ lines.push(` epoch ${quoteIfNeeded(system.epoch)}`);
178
+ }
179
+ if (system.referencePlane) {
180
+ lines.push(` referencePlane ${quoteIfNeeded(system.referencePlane)}`);
181
+ }
127
182
  lines.push("");
128
183
  lines.push("defaults");
129
184
  lines.push(` view ${system.defaults.view}`);
@@ -158,18 +213,22 @@ function formatAtlasSystem(system) {
158
213
  return lines;
159
214
  }
160
215
  function formatObject(object) {
161
- return formatLines(object.type, object.id, object.properties, object.placement, object.info);
216
+ return formatWorldOrbitObject(object.type, object.id, object);
162
217
  }
163
218
  function formatAtlasObject(object) {
164
- return formatLines(`object ${object.type}`, object.id, object.properties, object.placement, object.info);
219
+ return formatWorldOrbitObject(`object ${object.type}`, object.id, object);
165
220
  }
166
- function formatLines(objectType, id, properties, placement, info) {
221
+ function formatWorldOrbitObject(objectType, id, object) {
167
222
  const lines = [`${objectType} ${id}`];
168
- const fieldLines = [...formatPlacement(placement), ...formatProperties(properties)];
223
+ const fieldLines = [
224
+ ...formatPlacement(object.placement),
225
+ ...formatProperties(object.properties),
226
+ ...formatObjectMetadata(object),
227
+ ];
169
228
  for (const fieldLine of fieldLines) {
170
229
  lines.push(` ${fieldLine}`);
171
230
  }
172
- const infoEntries = Object.entries(info).sort(([left], [right]) => left.localeCompare(right));
231
+ const infoEntries = Object.entries(object.info).sort(([left], [right]) => left.localeCompare(right));
173
232
  if (infoEntries.length > 0) {
174
233
  if (fieldLines.length > 0) {
175
234
  lines.push("");
@@ -179,6 +238,16 @@ function formatLines(objectType, id, properties, placement, info) {
179
238
  lines.push(` ${key} ${quoteIfNeeded(value)}`);
180
239
  }
181
240
  }
241
+ for (const blockName of ["climate", "habitability", "settlement"]) {
242
+ const blockEntries = Object.entries(object.typedBlocks?.[blockName] ?? {}).sort(([left], [right]) => left.localeCompare(right));
243
+ if (blockEntries.length > 0) {
244
+ lines.push("");
245
+ lines.push(` ${blockName}`);
246
+ for (const [key, value] of blockEntries) {
247
+ lines.push(` ${key} ${quoteIfNeeded(value)}`);
248
+ }
249
+ }
250
+ }
182
251
  return lines;
183
252
  }
184
253
  function formatPlacement(placement) {
@@ -209,6 +278,46 @@ function formatProperties(properties) {
209
278
  .sort(compareFieldKeys)
210
279
  .map((key) => `${key} ${formatValue(properties[key])}`);
211
280
  }
281
+ function formatObjectMetadata(object) {
282
+ const lines = [];
283
+ if (object.groups?.length) {
284
+ lines.push(`groups ${object.groups.join(" ")}`);
285
+ }
286
+ if (object.epoch) {
287
+ lines.push(`epoch ${quoteIfNeeded(object.epoch)}`);
288
+ }
289
+ if (object.referencePlane) {
290
+ lines.push(`referencePlane ${quoteIfNeeded(object.referencePlane)}`);
291
+ }
292
+ if (object.tidalLock !== undefined) {
293
+ lines.push(`tidalLock ${object.tidalLock ? "true" : "false"}`);
294
+ }
295
+ if (object.renderHints?.renderLabel !== undefined) {
296
+ lines.push(`renderLabel ${object.renderHints.renderLabel ? "true" : "false"}`);
297
+ }
298
+ if (object.renderHints?.renderOrbit !== undefined) {
299
+ lines.push(`renderOrbit ${object.renderHints.renderOrbit ? "true" : "false"}`);
300
+ }
301
+ if (object.renderHints?.renderPriority !== undefined) {
302
+ lines.push(`renderPriority ${object.renderHints.renderPriority}`);
303
+ }
304
+ if (object.resonance) {
305
+ lines.push(`resonance ${object.resonance.targetObjectId} ${object.resonance.ratio}`);
306
+ }
307
+ for (const rule of object.deriveRules ?? []) {
308
+ lines.push(`derive ${rule.field} ${rule.strategy}`);
309
+ }
310
+ for (const rule of object.validationRules ?? []) {
311
+ lines.push(`validate ${rule.rule}`);
312
+ }
313
+ if (object.lockedFields?.length) {
314
+ lines.push(`locked ${object.lockedFields.join(" ")}`);
315
+ }
316
+ for (const tolerance of object.tolerances ?? []) {
317
+ lines.push(`tolerance ${tolerance.field} ${formatValue(tolerance.value)}`);
318
+ }
319
+ return lines;
320
+ }
212
321
  function formatAtlasViewpoint(viewpoint) {
213
322
  const lines = [`viewpoint ${viewpoint.id}`, ` label ${quoteIfNeeded(viewpoint.label)}`];
214
323
  if (viewpoint.focusObjectId) {
@@ -264,6 +373,50 @@ function formatAtlasAnnotation(annotation) {
264
373
  }
265
374
  return lines;
266
375
  }
376
+ function formatAtlasGroup(group) {
377
+ const lines = [`group ${group.id}`, ` label ${quoteIfNeeded(group.label)}`];
378
+ if (group.summary) {
379
+ lines.push(` summary ${quoteIfNeeded(group.summary)}`);
380
+ }
381
+ if (group.color) {
382
+ lines.push(` color ${quoteIfNeeded(group.color)}`);
383
+ }
384
+ if (group.tags.length > 0) {
385
+ lines.push(` tags ${group.tags.map(quoteIfNeeded).join(" ")}`);
386
+ }
387
+ if (group.hidden) {
388
+ lines.push(" hidden true");
389
+ }
390
+ return lines;
391
+ }
392
+ function formatAtlasRelation(relation) {
393
+ const lines = [`relation ${relation.id}`];
394
+ if (relation.from) {
395
+ lines.push(` from ${quoteIfNeeded(relation.from)}`);
396
+ }
397
+ if (relation.to) {
398
+ lines.push(` to ${quoteIfNeeded(relation.to)}`);
399
+ }
400
+ if (relation.kind) {
401
+ lines.push(` kind ${quoteIfNeeded(relation.kind)}`);
402
+ }
403
+ if (relation.label) {
404
+ lines.push(` label ${quoteIfNeeded(relation.label)}`);
405
+ }
406
+ if (relation.summary) {
407
+ lines.push(` summary ${quoteIfNeeded(relation.summary)}`);
408
+ }
409
+ if (relation.tags.length > 0) {
410
+ lines.push(` tags ${relation.tags.map(quoteIfNeeded).join(" ")}`);
411
+ }
412
+ if (relation.color) {
413
+ lines.push(` color ${quoteIfNeeded(relation.color)}`);
414
+ }
415
+ if (relation.hidden) {
416
+ lines.push(" hidden true");
417
+ }
418
+ return lines;
419
+ }
267
420
  function formatValue(value) {
268
421
  if (Array.isArray(value)) {
269
422
  return value.map((item) => quoteIfNeeded(item)).join(" ");
@@ -309,7 +462,7 @@ function formatDraftLayers(layers) {
309
462
  ? "orbits"
310
463
  : "-orbits");
311
464
  }
312
- for (const key of ["background", "guides", "objects", "labels", "metadata"]) {
465
+ for (const key of ["background", "guides", "relations", "objects", "labels", "metadata"]) {
313
466
  if (layers[key] !== undefined) {
314
467
  tokens.push(layers[key] ? key : `-${key}`);
315
468
  }
@@ -334,6 +487,9 @@ function compareObjects(left, right) {
334
487
  return leftIndex - rightIndex;
335
488
  return left.id.localeCompare(right.id);
336
489
  }
490
+ function compareIdLike(left, right) {
491
+ return left.id.localeCompare(right.id);
492
+ }
337
493
  function objectTypeIndex(objectType) {
338
494
  switch (objectType) {
339
495
  case "star":
@@ -5,10 +5,11 @@ import { WorldOrbitError } from "./errors.js";
5
5
  import { normalizeDocument } from "./normalize.js";
6
6
  import { parseWorldOrbit } from "./parse.js";
7
7
  import { validateDocument } from "./validate.js";
8
- const ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0)?$/i;
8
+ const ATLAS_SCHEMA_PATTERN = /^schema\s+2(?:\.0|\.1)?$/i;
9
+ const ATLAS_SCHEMA_21_PATTERN = /^schema\s+2\.1$/i;
9
10
  const LEGACY_DRAFT_SCHEMA_PATTERN = /^schema\s+2\.0-draft$/i;
10
11
  export function detectWorldOrbitSchemaVersion(source) {
11
- for (const line of source.split(/\r?\n/)) {
12
+ for (const line of stripCommentsForSchemaDetection(source).split(/\r?\n/)) {
12
13
  const trimmed = line.trim();
13
14
  if (!trimmed) {
14
15
  continue;
@@ -16,6 +17,9 @@ export function detectWorldOrbitSchemaVersion(source) {
16
17
  if (LEGACY_DRAFT_SCHEMA_PATTERN.test(trimmed)) {
17
18
  return "2.0-draft";
18
19
  }
20
+ if (ATLAS_SCHEMA_21_PATTERN.test(trimmed)) {
21
+ return "2.1";
22
+ }
19
23
  if (ATLAS_SCHEMA_PATTERN.test(trimmed)) {
20
24
  return "2.0";
21
25
  }
@@ -23,6 +27,49 @@ export function detectWorldOrbitSchemaVersion(source) {
23
27
  }
24
28
  return "1.0";
25
29
  }
30
+ function stripCommentsForSchemaDetection(source) {
31
+ const chars = [...source];
32
+ let inString = false;
33
+ let inBlockComment = false;
34
+ for (let index = 0; index < chars.length; index++) {
35
+ const ch = chars[index];
36
+ const next = chars[index + 1];
37
+ if (inBlockComment) {
38
+ if (ch === "*" && next === "/") {
39
+ chars[index] = " ";
40
+ chars[index + 1] = " ";
41
+ inBlockComment = false;
42
+ index++;
43
+ continue;
44
+ }
45
+ if (ch !== "\n" && ch !== "\r") {
46
+ chars[index] = " ";
47
+ }
48
+ continue;
49
+ }
50
+ if (!inString && ch === "/" && next === "*") {
51
+ chars[index] = " ";
52
+ chars[index + 1] = " ";
53
+ inBlockComment = true;
54
+ index++;
55
+ continue;
56
+ }
57
+ if (!inString && ch === "#") {
58
+ chars[index] = " ";
59
+ let inner = index + 1;
60
+ while (inner < chars.length && chars[inner] !== "\n" && chars[inner] !== "\r") {
61
+ chars[inner] = " ";
62
+ inner++;
63
+ }
64
+ index = inner - 1;
65
+ continue;
66
+ }
67
+ if (ch === '"' && chars[index - 1] !== "\\") {
68
+ inString = !inString;
69
+ }
70
+ }
71
+ return chars.join("");
72
+ }
26
73
  export function loadWorldOrbitSource(source) {
27
74
  const result = loadWorldOrbitSourceWithDiagnostics(source);
28
75
  if (!result.ok || !result.value) {
@@ -33,7 +80,7 @@ export function loadWorldOrbitSource(source) {
33
80
  }
34
81
  export function loadWorldOrbitSourceWithDiagnostics(source) {
35
82
  const schemaVersion = detectWorldOrbitSchemaVersion(source);
36
- if (schemaVersion === "2.0" || schemaVersion === "2.0-draft") {
83
+ if (schemaVersion === "2.0" || schemaVersion === "2.0-draft" || schemaVersion === "2.1") {
37
84
  return loadAtlasSourceWithDiagnostics(source, schemaVersion);
38
85
  }
39
86
  let ast;
@@ -93,25 +140,23 @@ function loadAtlasSourceWithDiagnostics(source, schemaVersion) {
93
140
  diagnostics: [diagnosticFromError(error, "parse", "load.atlas.failed")],
94
141
  };
95
142
  }
96
- let document;
97
- try {
98
- document = materializeAtlasDocument(atlasDocument);
99
- }
100
- catch (error) {
143
+ const atlasDiagnostics = [...atlasDocument.diagnostics];
144
+ if (atlasDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
101
145
  return {
102
146
  ok: false,
103
147
  value: null,
104
- diagnostics: [diagnosticFromError(error, "normalize", "load.atlas.materialize.failed")],
148
+ diagnostics: atlasDiagnostics,
105
149
  };
106
150
  }
151
+ let document;
107
152
  try {
108
- validateDocument(document);
153
+ document = materializeAtlasDocument(atlasDocument);
109
154
  }
110
155
  catch (error) {
111
156
  return {
112
157
  ok: false,
113
158
  value: null,
114
- diagnostics: [diagnosticFromError(error, "validate", "load.atlas.validate.failed")],
159
+ diagnostics: [diagnosticFromError(error, "normalize", "load.atlas.materialize.failed")],
115
160
  };
116
161
  }
117
162
  const loaded = {
@@ -120,11 +165,11 @@ function loadAtlasSourceWithDiagnostics(source, schemaVersion) {
120
165
  document,
121
166
  atlasDocument,
122
167
  draftDocument: atlasDocument,
123
- diagnostics: [...atlasDocument.diagnostics],
168
+ diagnostics: atlasDiagnostics,
124
169
  };
125
170
  return {
126
171
  ok: true,
127
172
  value: loaded,
128
- diagnostics: [...atlasDocument.diagnostics],
173
+ diagnostics: atlasDiagnostics,
129
174
  };
130
175
  }
@@ -26,7 +26,10 @@ export function normalizeDocument(ast) {
26
26
  return {
27
27
  format: "worldorbit",
28
28
  version: "1.0",
29
+ schemaVersion: "1.0",
29
30
  system,
31
+ groups: [],
32
+ relations: [],
30
33
  objects,
31
34
  };
32
35
  }
@@ -41,6 +44,10 @@ function normalizeObject(node) {
41
44
  return {
42
45
  type: "system",
43
46
  id: node.name,
47
+ title: typeof properties.title === "string" ? properties.title : null,
48
+ description: null,
49
+ epoch: null,
50
+ referencePlane: null,
44
51
  properties,
45
52
  info,
46
53
  };