@codexo/exojs-ldtk 0.15.3 → 0.16.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 +38 -20
- package/dist/esm/LdtkData.d.ts +59 -14
- package/dist/esm/LdtkData.d.ts.map +1 -0
- package/dist/esm/LdtkData.js +8 -16
- package/dist/esm/LdtkData.js.map +1 -1
- package/dist/esm/LdtkMap.d.ts +25 -6
- package/dist/esm/LdtkMap.d.ts.map +1 -0
- package/dist/esm/LdtkMap.js +88 -63
- package/dist/esm/LdtkMap.js.map +1 -1
- package/dist/esm/LdtkProject.d.ts +107 -0
- package/dist/esm/LdtkProject.d.ts.map +1 -0
- package/dist/esm/LdtkProject.js +136 -0
- package/dist/esm/LdtkProject.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +11 -7
- package/dist/esm/ldtkExtension.d.ts +9 -5
- package/dist/esm/ldtkExtension.d.ts.map +1 -0
- package/dist/esm/ldtkExtension.js +25 -22
- package/dist/esm/ldtkExtension.js.map +1 -1
- package/dist/esm/ldtkLevelEntries.d.ts +4 -3
- package/dist/esm/ldtkLevelEntries.d.ts.map +1 -0
- package/dist/esm/ldtkLevelEntries.js +30 -26
- package/dist/esm/ldtkLevelEntries.js.map +1 -1
- package/dist/esm/ldtkParallax.d.ts +22 -0
- package/dist/esm/ldtkParallax.d.ts.map +1 -0
- package/dist/esm/ldtkParallax.js +22 -0
- package/dist/esm/ldtkParallax.js.map +1 -0
- package/dist/esm/ldtkToMapWorld.d.ts +20 -0
- package/dist/esm/ldtkToMapWorld.d.ts.map +1 -0
- package/dist/esm/ldtkToMapWorld.js +76 -0
- package/dist/esm/ldtkToMapWorld.js.map +1 -0
- package/dist/esm/ldtkToTileMap.d.ts +48 -10
- package/dist/esm/ldtkToTileMap.d.ts.map +1 -0
- package/dist/esm/ldtkToTileMap.js +410 -387
- package/dist/esm/ldtkToTileMap.js.map +1 -1
- package/dist/esm/ldtkTypes.d.ts +47 -0
- package/dist/esm/ldtkTypes.d.ts.map +1 -0
- package/dist/esm/ldtkTypes.js +57 -0
- package/dist/esm/ldtkTypes.js.map +1 -0
- package/dist/esm/loadLdtkMap.d.ts +30 -5
- package/dist/esm/loadLdtkMap.d.ts.map +1 -0
- package/dist/esm/loadLdtkMap.js +139 -133
- package/dist/esm/loadLdtkMap.js.map +1 -1
- package/dist/esm/loadLdtkProject.d.ts +17 -0
- package/dist/esm/loadLdtkProject.d.ts.map +1 -0
- package/dist/esm/loadLdtkProject.js +31 -0
- package/dist/esm/loadLdtkProject.js.map +1 -0
- package/dist/esm/public.d.ts +24 -9
- package/dist/esm/public.d.ts.map +1 -0
- package/dist/esm/public.js +11 -0
- package/dist/esm/url.d.ts +16 -0
- package/dist/esm/url.d.ts.map +1 -0
- package/dist/esm/url.js +31 -0
- package/dist/esm/url.js.map +1 -0
- package/dist/esm/validate.d.ts +27 -0
- package/dist/esm/validate.d.ts.map +1 -0
- package/dist/esm/validate.js +327 -0
- package/dist/esm/validate.js.map +1 -0
- package/package.json +10 -18
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/ldtkBinding.d.ts +0 -22
- package/dist/esm/ldtkBinding.js +0 -31
- package/dist/esm/ldtkBinding.js.map +0 -1
- package/dist/esm/register.d.ts +0 -1
- package/dist/esm/register.js +0 -17
- package/dist/esm/register.js.map +0 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { validateLdtkData } from "./validate.js";
|
|
2
|
+
import { LdtkProject } from "./LdtkProject.js";
|
|
3
|
+
import { loadLdtkTileset } from "./loadLdtkMap.js";
|
|
4
|
+
import { Asset } from "@codexo/exojs";
|
|
5
|
+
|
|
6
|
+
//#region src/loadLdtkProject.ts
|
|
7
|
+
/**
|
|
8
|
+
* Read and validate a `.ldtk` document and load every tileset atlas it
|
|
9
|
+
* references, without touching any level payload.
|
|
10
|
+
*
|
|
11
|
+
* External `.ldtkl` levels are deliberately left unfetched - that is the whole
|
|
12
|
+
* point of {@link LdtkProject}: the world layout arrives, the levels arrive
|
|
13
|
+
* when a level runtime asks for them.
|
|
14
|
+
*
|
|
15
|
+
* @throws {import('./validate').LdtkFormatError} for a structurally invalid
|
|
16
|
+
* document, or a tileset whose declared atlas cannot hold a single tile. An
|
|
17
|
+
* embed-atlas tileset (`relPath = null`) is skipped with a warning instead.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
const loadLdtkProject = async (context) => {
|
|
21
|
+
const source = context.source;
|
|
22
|
+
const data = validateLdtkData(await context.dependencies.load(Asset.type("json", source)), source);
|
|
23
|
+
const tilesetEntries = await Promise.all(data.defs.tilesets.map(async (def) => [def.uid, await loadLdtkTileset(def, source, context)]));
|
|
24
|
+
const tilesets = /* @__PURE__ */ new Map();
|
|
25
|
+
for (const [uid, tileset] of tilesetEntries) if (tileset !== null) tilesets.set(uid, tileset);
|
|
26
|
+
return new LdtkProject(source, data, tilesets);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { loadLdtkProject };
|
|
31
|
+
//# sourceMappingURL=loadLdtkProject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadLdtkProject.js","names":[],"sources":["../../src/loadLdtkProject.ts"],"sourcesContent":["import type { AssetFactoryContext } from '@codexo/exojs';\nimport { Asset } from '@codexo/exojs';\nimport type { TileSet } from '@codexo/exojs-tilemap';\n\nimport { LdtkProject } from './LdtkProject';\nimport { loadLdtkTileset } from './loadLdtkMap';\nimport { validateLdtkData } from './validate';\n\n/**\n * Read and validate a `.ldtk` document and load every tileset atlas it\n * references, without touching any level payload.\n *\n * External `.ldtkl` levels are deliberately left unfetched - that is the whole\n * point of {@link LdtkProject}: the world layout arrives, the levels arrive\n * when a level runtime asks for them.\n *\n * @throws {import('./validate').LdtkFormatError} for a structurally invalid\n * document, or a tileset whose declared atlas cannot hold a single tile. An\n * embed-atlas tileset (`relPath = null`) is skipped with a warning instead.\n * @internal\n */\nexport const loadLdtkProject = async (context: AssetFactoryContext): Promise<LdtkProject> => {\n const source = context.source;\n const data = validateLdtkData(await context.dependencies.load(Asset.type('json', source)), source);\n\n const tilesetEntries = await Promise.all(data.defs.tilesets.map(async def => [def.uid, await loadLdtkTileset(def, source, context)] as const));\n\n const tilesets = new Map<number, TileSet>();\n\n for (const [uid, tileset] of tilesetEntries) {\n if (tileset !== null) tilesets.set(uid, tileset);\n }\n\n return new LdtkProject(source, data, tilesets);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqBA,MAAa,kBAAkB,OAAO,YAAuD;CAC3F,MAAM,SAAS,QAAQ;CACvB,MAAM,OAAO,iBAAiB,MAAM,QAAQ,aAAa,KAAK,MAAM,KAAK,QAAQ,MAAM,CAAC,GAAG,MAAM;CAEjG,MAAM,iBAAiB,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,IAAI,OAAM,QAAO,CAAC,IAAI,KAAK,MAAM,gBAAgB,KAAK,QAAQ,OAAO,CAAC,CAAU,CAAC;CAE7I,MAAM,2BAAW,IAAI,IAAqB;CAE1C,KAAK,MAAM,CAAC,KAAK,YAAY,gBAC3B,IAAI,YAAY,MAAM,SAAS,IAAI,KAAK,OAAO;CAGjD,OAAO,IAAI,YAAY,QAAQ,MAAM,QAAQ;AAC/C"}
|
package/dist/esm/public.d.ts
CHANGED
|
@@ -1,24 +1,39 @@
|
|
|
1
|
-
export { ldtkMapBinding } from './ldtkBinding';
|
|
2
1
|
export { ldtkExtension } from './ldtkExtension';
|
|
2
|
+
export { LdtkMapAssetType, ldtkMapType, LdtkProjectAssetType, ldtkProjectType } from './ldtkTypes';
|
|
3
3
|
export { LdtkMap } from './LdtkMap';
|
|
4
|
+
export type { LdtkRuntimeOptions } from './LdtkProject';
|
|
5
|
+
export { LdtkProject } from './LdtkProject';
|
|
6
|
+
export { ldtkToMapWorld } from './ldtkToMapWorld';
|
|
4
7
|
export type { LdtkToTileMapOptions } from './ldtkToTileMap';
|
|
5
|
-
export { getLdtkIntGridValueAt, ldtkIntGridCsvProperty, ldtkIntGridValuesProperty, ldtkToTileMap
|
|
6
|
-
export type { LdtkData, LdtkDefs, LdtkEntityInstance, LdtkFieldInstance, LdtkIntGridValueDef, LdtkLayerDef, LdtkLayerInstance, LdtkLayerType, LdtkLevel, LdtkTileData, LdtkTilesetDef, LdtkWorldData, LdtkWorldLayout, } from './LdtkData';
|
|
7
|
-
export {
|
|
8
|
-
export
|
|
9
|
-
export {
|
|
8
|
+
export { createLdtkIntGridCellSource, getLdtkIntGridValueAt, ldtkIntGridCsvProperty, ldtkIntGridValuesProperty, ldtkToTileMap } from './ldtkToTileMap';
|
|
9
|
+
export type { LdtkData, LdtkDefs, LdtkEntityInstance, LdtkFieldInstance, LdtkIntGridValueDef, LdtkLayerDef, LdtkLayerInstance, LdtkLayerType, LdtkLevel, LdtkLevelNeighbour, LdtkTileData, LdtkTilesetDef, LdtkWorldData, LdtkWorldLayout, } from './LdtkData';
|
|
10
|
+
export { LDTK_FLIP_NONE, LDTK_FLIP_X, LDTK_FLIP_XY, LDTK_FLIP_Y } from './LdtkData';
|
|
11
|
+
export { LdtkFormatError } from './validate';
|
|
12
|
+
export type { ChunkCoord, EllipseObject, MapBounds, MapLevel, MapLevelCancelOptions, MapLevelLoadContext, MapLevelLoadOptions, MapLevelNeighbour, MapLevelProvider, MapObjectDescriptor, MapObjectFactories, MapObjectFactory, MapObjectSpawnerOptions, MapSpawnErrorReason, MapSpawnOptions, MapWorldOptions, MapWorldRuntimeOptions, ObjectLayerOptions, ObjectPoint, ObjectQuery, ObjectSchema, PackedTile, PointObject, PolygonObject, PolylineObject, ReadonlyTileChunk, RectangleObject, ResolvedTile, TileDefinition, TileLayerOptions, TileMapObject, TileMapObjectKind, TileMapOptions, TileMapViewOptions, TileObject, TileProperties, TilePropertyObjectRef, TilePropertyPoint, TilePropertyTileRef, TilePropertyValue, TileSetOptions, TileTransform, UnknownMapObjectPolicy, } from '@codexo/exojs-tilemap';
|
|
13
|
+
export { MapLevelRuntime, MapLevelSide, mapObjectDescriptor, mapObjectDescriptors, MapObjectSpawner, MapSpawnError, MapSpawnSession, MapWorld, MapWorldRuntime, ObjectKind, ObjectLayer, TILE_TRANSFORM_IDENTITY, TileLayer, TileMap, tilemapExtension, TileMapView, TilePropertyKind, TileSet, } from '@codexo/exojs-tilemap';
|
|
10
14
|
import type { LdtkMap } from './LdtkMap';
|
|
15
|
+
import type { LdtkProject } from './LdtkProject';
|
|
11
16
|
declare module '@codexo/exojs' {
|
|
12
|
-
interface
|
|
13
|
-
/** `.ldtk` path
|
|
14
|
-
ldtk:
|
|
17
|
+
interface ExtensionKindMap {
|
|
18
|
+
/** `.ldtk` bare-path loads resolve to the `ldtkMap` type ({@link LdtkMap}). */
|
|
19
|
+
ldtk: 'ldtkMap';
|
|
15
20
|
}
|
|
16
21
|
interface AssetDefinitions {
|
|
22
|
+
ldtkProject: {
|
|
23
|
+
resource: LdtkProject;
|
|
24
|
+
config: {
|
|
25
|
+
source: string;
|
|
26
|
+
};
|
|
27
|
+
/** See `ldtkMap` - `ldtkProjectType` keeps the default leaf too. */
|
|
28
|
+
isValue: true;
|
|
29
|
+
};
|
|
17
30
|
ldtkMap: {
|
|
18
31
|
resource: LdtkMap;
|
|
19
32
|
config: {
|
|
20
33
|
source: string;
|
|
21
34
|
};
|
|
35
|
+
isValue: true;
|
|
22
36
|
};
|
|
23
37
|
}
|
|
24
38
|
}
|
|
39
|
+
//# sourceMappingURL=public.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/public.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGvJ,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGpF,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAK7C,YAAY,EACV,UAAU,EACV,aAAa,EACb,SAAS,EACT,QAAQ,EACR,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,kBAAkB,EAClB,WAAW,EACX,WAAW,EACX,YAAY,EACZ,UAAU,EACV,WAAW,EACX,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,QAAQ,EACR,eAAe,EACf,UAAU,EACV,WAAW,EACX,uBAAuB,EACvB,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,OAAO,GACR,MAAM,uBAAuB,CAAC;AAG/B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,QAAQ,eAAe,CAAC;IAC7B,UAAU,gBAAgB;QACxB,+EAA+E;QAC/E,IAAI,EAAE,SAAS,CAAC;KACjB;IACD,UAAU,gBAAgB;QACxB,WAAW,EAAE;YACX,QAAQ,EAAE,WAAW,CAAC;YACtB,MAAM,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,CAAC;YAC3B,oEAAoE;YACpE,OAAO,EAAE,IAAI,CAAC;SACf,CAAC;QACF,OAAO,EAAE;YACP,QAAQ,EAAE,OAAO,CAAC;YAClB,MAAM,EAAE;gBAAE,MAAM,EAAE,MAAM,CAAA;aAAE,CAAC;YAG3B,OAAO,EAAE,IAAI,CAAC;SACf,CAAC;KACH;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { LDTK_FLIP_NONE, LDTK_FLIP_X, LDTK_FLIP_XY, LDTK_FLIP_Y } from "./LdtkData.js";
|
|
2
|
+
import { createLdtkIntGridCellSource, getLdtkIntGridValueAt, ldtkIntGridCsvProperty, ldtkIntGridValuesProperty, ldtkToTileMap } from "./ldtkToTileMap.js";
|
|
3
|
+
import { ldtkToMapWorld } from "./ldtkToMapWorld.js";
|
|
4
|
+
import { LdtkMap } from "./LdtkMap.js";
|
|
5
|
+
import { LdtkFormatError } from "./validate.js";
|
|
6
|
+
import { LdtkProject } from "./LdtkProject.js";
|
|
7
|
+
import { LdtkMapAssetType, LdtkProjectAssetType, ldtkMapType, ldtkProjectType } from "./ldtkTypes.js";
|
|
8
|
+
import { ldtkExtension } from "./ldtkExtension.js";
|
|
9
|
+
import { MapLevelRuntime, MapLevelSide as MapLevelSide$1, MapObjectSpawner, MapSpawnError, MapSpawnSession, MapWorld as MapWorld$1, MapWorldRuntime as MapWorldRuntime$1, ObjectKind, ObjectLayer as ObjectLayer$1, TILE_TRANSFORM_IDENTITY, TileLayer as TileLayer$1, TileMap as TileMap$1, TileMapView, TilePropertyKind as TilePropertyKind$1, TileSet as TileSet$1, mapObjectDescriptor, mapObjectDescriptors, tilemapExtension as tilemapExtension$1 } from "@codexo/exojs-tilemap";
|
|
10
|
+
|
|
11
|
+
export { LDTK_FLIP_NONE, LDTK_FLIP_X, LDTK_FLIP_XY, LDTK_FLIP_Y, LdtkFormatError, LdtkMap, LdtkMapAssetType, LdtkProject, LdtkProjectAssetType, MapLevelRuntime, MapLevelSide$1 as MapLevelSide, MapObjectSpawner, MapSpawnError, MapSpawnSession, MapWorld$1 as MapWorld, MapWorldRuntime$1 as MapWorldRuntime, ObjectKind, ObjectLayer$1 as ObjectLayer, TILE_TRANSFORM_IDENTITY, TileLayer$1 as TileLayer, TileMap$1 as TileMap, TileMapView, TilePropertyKind$1 as TilePropertyKind, TileSet$1 as TileSet, createLdtkIntGridCellSource, getLdtkIntGridValueAt, ldtkExtension, ldtkIntGridCsvProperty, ldtkIntGridValuesProperty, ldtkMapType, ldtkProjectType, ldtkToMapWorld, ldtkToTileMap, mapObjectDescriptor, mapObjectDescriptors, tilemapExtension$1 as tilemapExtension };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves `ref` (a path read from an LDtk file, e.g. a tileset `relPath` or a
|
|
3
|
+
* level `externalRelPath`) relative to `base` (the resolved location of the
|
|
4
|
+
* file that referenced it).
|
|
5
|
+
*
|
|
6
|
+
* - If `ref` is already absolute (scheme, protocol- or root-relative, or a
|
|
7
|
+
* `data:`/`blob:` URL), it is returned unchanged.
|
|
8
|
+
* - If `base` is an absolute URL, standard `new URL(ref, base)` resolution is
|
|
9
|
+
* used.
|
|
10
|
+
* - Otherwise both are relative ExoJS asset paths; `../`/`./` segments are
|
|
11
|
+
* collapsed via a synthetic origin and the result is returned relative again.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export declare const resolveLdtkUrl: (ref: string, base: string) => string;
|
|
16
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/url.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,EAAE,MAAM,MAAM,KAAG,MAgB1D,CAAC"}
|
package/dist/esm/url.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/url.ts
|
|
2
|
+
/** Matches references that are already absolute and must not be re-resolved: scheme:, protocol-relative `//`, root-relative `/`, and data/blob URLs. */
|
|
3
|
+
const ABSOLUTE_REF = /^(?:[a-z][a-z\d+.-]*:|\/\/|\/)/i;
|
|
4
|
+
/** Matches a base that is itself an absolute URL with a scheme. */
|
|
5
|
+
const ABSOLUTE_BASE = /^[a-z][a-z\d+.-]*:/i;
|
|
6
|
+
/** Synthetic origin used to borrow `URL`'s `../`/`./` collapsing for relative bases. */
|
|
7
|
+
const SYNTHETIC_ORIGIN = "https://exojs.invalid/";
|
|
8
|
+
/**
|
|
9
|
+
* Resolves `ref` (a path read from an LDtk file, e.g. a tileset `relPath` or a
|
|
10
|
+
* level `externalRelPath`) relative to `base` (the resolved location of the
|
|
11
|
+
* file that referenced it).
|
|
12
|
+
*
|
|
13
|
+
* - If `ref` is already absolute (scheme, protocol- or root-relative, or a
|
|
14
|
+
* `data:`/`blob:` URL), it is returned unchanged.
|
|
15
|
+
* - If `base` is an absolute URL, standard `new URL(ref, base)` resolution is
|
|
16
|
+
* used.
|
|
17
|
+
* - Otherwise both are relative ExoJS asset paths; `../`/`./` segments are
|
|
18
|
+
* collapsed via a synthetic origin and the result is returned relative again.
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
const resolveLdtkUrl = (ref, base) => {
|
|
23
|
+
if (ABSOLUTE_REF.test(ref)) return ref;
|
|
24
|
+
if (ABSOLUTE_BASE.test(base)) return new URL(ref, base).href;
|
|
25
|
+
const relative = new URL(ref, SYNTHETIC_ORIGIN + base.replace(/^\/+/, "")).href.slice(22);
|
|
26
|
+
return base.startsWith("/") ? `/${relative}` : relative;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { resolveLdtkUrl };
|
|
31
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","names":[],"sources":["../../src/url.ts"],"sourcesContent":["// Relative-path resolution for LDtk source references (.ldtk → tileset image,\n// .ldtk → external .ldtkl level). LDtk stores every cross-file reference as a\n// path relative to the file that contains it; ExoJS asset sources are themselves\n// often relative (resolved against an asset root configured outside the Loader),\n// so plain `new URL(ref, base)` cannot be used directly when `base` has no\n// scheme - it throws `Invalid URL`. Mirrors the Tiled adapter's `resolveTiledUrl`.\n\n/** Matches references that are already absolute and must not be re-resolved: scheme:, protocol-relative `//`, root-relative `/`, and data/blob URLs. */\nconst ABSOLUTE_REF = /^(?:[a-z][a-z\\d+.-]*:|\\/\\/|\\/)/i;\n\n/** Matches a base that is itself an absolute URL with a scheme. */\nconst ABSOLUTE_BASE = /^[a-z][a-z\\d+.-]*:/i;\n\n/** Synthetic origin used to borrow `URL`'s `../`/`./` collapsing for relative bases. */\nconst SYNTHETIC_ORIGIN = 'https://exojs.invalid/';\n\n/**\n * Resolves `ref` (a path read from an LDtk file, e.g. a tileset `relPath` or a\n * level `externalRelPath`) relative to `base` (the resolved location of the\n * file that referenced it).\n *\n * - If `ref` is already absolute (scheme, protocol- or root-relative, or a\n * `data:`/`blob:` URL), it is returned unchanged.\n * - If `base` is an absolute URL, standard `new URL(ref, base)` resolution is\n * used.\n * - Otherwise both are relative ExoJS asset paths; `../`/`./` segments are\n * collapsed via a synthetic origin and the result is returned relative again.\n *\n * @internal\n */\nexport const resolveLdtkUrl = (ref: string, base: string): string => {\n if (ABSOLUTE_REF.test(ref)) {\n return ref;\n }\n\n if (ABSOLUTE_BASE.test(base)) {\n return new URL(ref, base).href;\n }\n\n const resolved = new URL(ref, SYNTHETIC_ORIGIN + base.replace(/^\\/+/, ''));\n const relative = resolved.href.slice(SYNTHETIC_ORIGIN.length);\n\n // A root-relative base must produce a root-relative result again - dropping\n // the leading slash would make the browser re-resolve the reference against\n // the document base URL (e.g. `/site/assets/x.png` → `/site/site/assets/...`).\n return base.startsWith('/') ? `/${relative}` : relative;\n};\n"],"mappings":";;AAQA,MAAM,eAAe;;AAGrB,MAAM,gBAAgB;;AAGtB,MAAM,mBAAmB;;;;;;;;;;;;;;;AAgBzB,MAAa,kBAAkB,KAAa,SAAyB;CACnE,IAAI,aAAa,KAAK,GAAG,GACvB,OAAO;CAGT,IAAI,cAAc,KAAK,IAAI,GACzB,OAAO,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC;CAI5B,MAAM,WAAW,IADI,IAAI,KAAK,mBAAmB,KAAK,QAAQ,QAAQ,EAAE,CAChD,CAAC,CAAC,KAAK,MAAM,EAAuB;CAK5D,OAAO,KAAK,WAAW,GAAG,IAAI,IAAI,aAAa;AACjD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { LdtkData, LdtkLevel } from './LdtkData';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when an LDtk JSON document does not match the expected shape.
|
|
4
|
+
* `source` is the URL of the file being parsed; `path` is a JSON-path-like
|
|
5
|
+
* pointer (e.g. `levels[0].layerInstances[1].gridTiles[0].px`) to the
|
|
6
|
+
* offending value, or `''` for the document root.
|
|
7
|
+
*/
|
|
8
|
+
export declare class LdtkFormatError extends Error {
|
|
9
|
+
readonly source: string;
|
|
10
|
+
readonly path: string;
|
|
11
|
+
constructor(source: string, path: string, message: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Validate the root of a `.ldtk` file and return it typed as {@link LdtkData}.
|
|
15
|
+
* Throws {@link LdtkFormatError} on any structural problem, naming the source
|
|
16
|
+
* URL and the property path of the offending value.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export declare const validateLdtkData: (raw: unknown, source: string) => LdtkData;
|
|
20
|
+
/**
|
|
21
|
+
* Validate an external `.ldtkl` payload and return it typed as
|
|
22
|
+
* {@link LdtkLevel}. Paths in a thrown {@link LdtkFormatError} are relative to
|
|
23
|
+
* the level object, since that object is the root of the `.ldtkl` file.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export declare const validateLdtkLevelData: (raw: unknown, source: string) => LdtkLevel;
|
|
27
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/validate.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,QAAQ,EAAiB,SAAS,EAAmB,MAAM,YAAY,CAAC;AAEtF;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEV,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAMjE;AAyYD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,OAAO,EAAE,QAAQ,MAAM,KAAG,QAgB/D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,KAAK,OAAO,EAAE,QAAQ,MAAM,KAAG,SAIpE,CAAC"}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
//#region src/validate.ts
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when an LDtk JSON document does not match the expected shape.
|
|
4
|
+
* `source` is the URL of the file being parsed; `path` is a JSON-path-like
|
|
5
|
+
* pointer (e.g. `levels[0].layerInstances[1].gridTiles[0].px`) to the
|
|
6
|
+
* offending value, or `''` for the document root.
|
|
7
|
+
*/
|
|
8
|
+
var LdtkFormatError = class extends Error {
|
|
9
|
+
source;
|
|
10
|
+
path;
|
|
11
|
+
constructor(source, path, message) {
|
|
12
|
+
super(`Invalid LDtk data in "${source}" at ${path === "" ? "<root>" : path}: ${message}`);
|
|
13
|
+
this.name = "LdtkFormatError";
|
|
14
|
+
this.source = source;
|
|
15
|
+
this.path = path;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const describeValue = (value) => {
|
|
19
|
+
if (value === null) return "null";
|
|
20
|
+
if (value === void 0) return "undefined";
|
|
21
|
+
if (Array.isArray(value)) return "an array";
|
|
22
|
+
return typeof value;
|
|
23
|
+
};
|
|
24
|
+
const joinPath = (path, key) => {
|
|
25
|
+
if (typeof key === "number") return `${path}[${key}]`;
|
|
26
|
+
return path === "" ? key : `${path}.${key}`;
|
|
27
|
+
};
|
|
28
|
+
const expectObject = (value, source, path) => {
|
|
29
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new LdtkFormatError(source, path, `expected an object, got ${describeValue(value)}`);
|
|
30
|
+
return value;
|
|
31
|
+
};
|
|
32
|
+
const expectArray = (value, source, path) => {
|
|
33
|
+
if (!Array.isArray(value)) throw new LdtkFormatError(source, path, `expected an array, got ${describeValue(value)}`);
|
|
34
|
+
return value;
|
|
35
|
+
};
|
|
36
|
+
const expectString = (value, source, path) => {
|
|
37
|
+
if (typeof value !== "string") throw new LdtkFormatError(source, path, `expected a string, got ${describeValue(value)}`);
|
|
38
|
+
return value;
|
|
39
|
+
};
|
|
40
|
+
const expectNumber = (value, source, path) => {
|
|
41
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new LdtkFormatError(source, path, `expected a finite number, got ${describeValue(value)}`);
|
|
42
|
+
return value;
|
|
43
|
+
};
|
|
44
|
+
const expectInteger = (value, source, path) => {
|
|
45
|
+
const n = expectNumber(value, source, path);
|
|
46
|
+
if (!Number.isInteger(n)) throw new LdtkFormatError(source, path, `expected an integer, got ${n}`);
|
|
47
|
+
return n;
|
|
48
|
+
};
|
|
49
|
+
const expectNonNegativeInteger = (value, source, path) => {
|
|
50
|
+
const n = expectInteger(value, source, path);
|
|
51
|
+
if (n < 0) throw new LdtkFormatError(source, path, `expected a non-negative integer, got ${n}`);
|
|
52
|
+
return n;
|
|
53
|
+
};
|
|
54
|
+
const expectPositiveInteger = (value, source, path) => {
|
|
55
|
+
const n = expectInteger(value, source, path);
|
|
56
|
+
if (n <= 0) throw new LdtkFormatError(source, path, `expected a positive integer, got ${n}`);
|
|
57
|
+
return n;
|
|
58
|
+
};
|
|
59
|
+
const expectBoolean = (value, source, path) => {
|
|
60
|
+
if (typeof value !== "boolean") throw new LdtkFormatError(source, path, `expected a boolean, got ${describeValue(value)}`);
|
|
61
|
+
};
|
|
62
|
+
/** `[x, y]` pixel/grid pairs (`px`, `src`, `__pivot`) are always two finite numbers. */
|
|
63
|
+
const expectNumberPair = (value, source, path) => {
|
|
64
|
+
const pair = expectArray(value, source, path);
|
|
65
|
+
if (pair.length !== 2) throw new LdtkFormatError(source, path, `expected a pair of numbers, got ${pair.length} entries`);
|
|
66
|
+
expectNumber(pair[0], source, joinPath(path, 0));
|
|
67
|
+
expectNumber(pair[1], source, joinPath(path, 1));
|
|
68
|
+
};
|
|
69
|
+
const optionalNumber = (obj, key, source, path) => {
|
|
70
|
+
if (obj[key] !== void 0) expectNumber(obj[key], source, joinPath(path, key));
|
|
71
|
+
};
|
|
72
|
+
const optionalNonNegativeInteger = (obj, key, source, path) => {
|
|
73
|
+
if (obj[key] !== void 0) expectNonNegativeInteger(obj[key], source, joinPath(path, key));
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* LDtk writes an unset optional as an explicit `null` rather than omitting the
|
|
77
|
+
* key, so a validator that only tolerates `undefined` rejects the files the
|
|
78
|
+
* editor actually produces.
|
|
79
|
+
*/
|
|
80
|
+
const nullableString = (obj, key, source, path) => {
|
|
81
|
+
if (obj[key] !== void 0 && obj[key] !== null) expectString(obj[key], source, joinPath(path, key));
|
|
82
|
+
};
|
|
83
|
+
/** See {@link nullableString}. */
|
|
84
|
+
const nullableInteger = (obj, key, source, path) => {
|
|
85
|
+
if (obj[key] !== void 0 && obj[key] !== null) expectInteger(obj[key], source, joinPath(path, key));
|
|
86
|
+
};
|
|
87
|
+
const optionalBoolean = (obj, key, source, path) => {
|
|
88
|
+
if (obj[key] !== void 0) expectBoolean(obj[key], source, joinPath(path, key));
|
|
89
|
+
};
|
|
90
|
+
const eachEntry = (value, source, path, visit) => {
|
|
91
|
+
const array = expectArray(value, source, path);
|
|
92
|
+
for (let i = 0; i < array.length; i++) visit(array[i], joinPath(path, i));
|
|
93
|
+
};
|
|
94
|
+
const LAYER_TYPES = [
|
|
95
|
+
"Tiles",
|
|
96
|
+
"IntGrid",
|
|
97
|
+
"Entities",
|
|
98
|
+
"AutoLayer"
|
|
99
|
+
];
|
|
100
|
+
const WORLD_LAYOUTS = [
|
|
101
|
+
"Free",
|
|
102
|
+
"GridVania",
|
|
103
|
+
"LinearHorizontal",
|
|
104
|
+
"LinearVertical"
|
|
105
|
+
];
|
|
106
|
+
const validateLayerType = (value, source, path) => {
|
|
107
|
+
const type = expectString(value, source, path);
|
|
108
|
+
if (!LAYER_TYPES.includes(type)) throw new LdtkFormatError(source, path, `unknown layer type "${type}" (expected one of ${LAYER_TYPES.join(", ")})`);
|
|
109
|
+
};
|
|
110
|
+
const validateTilesetDef = (raw, source, path) => {
|
|
111
|
+
const def = expectObject(raw, source, path);
|
|
112
|
+
expectInteger(def.uid, source, joinPath(path, "uid"));
|
|
113
|
+
expectString(def.identifier, source, joinPath(path, "identifier"));
|
|
114
|
+
const relPathPath = joinPath(path, "relPath");
|
|
115
|
+
if (def.relPath !== null) expectString(def.relPath, source, relPathPath);
|
|
116
|
+
expectPositiveInteger(def.tileGridSize, source, joinPath(path, "tileGridSize"));
|
|
117
|
+
expectNonNegativeInteger(def.pxWid, source, joinPath(path, "pxWid"));
|
|
118
|
+
expectNonNegativeInteger(def.pxHei, source, joinPath(path, "pxHei"));
|
|
119
|
+
optionalNonNegativeInteger(def, "spacing", source, path);
|
|
120
|
+
optionalNonNegativeInteger(def, "padding", source, path);
|
|
121
|
+
};
|
|
122
|
+
const validateIntGridValueDef = (raw, source, path) => {
|
|
123
|
+
const value = expectObject(raw, source, path);
|
|
124
|
+
expectNumber(value.value, source, joinPath(path, "value"));
|
|
125
|
+
const identifierPath = joinPath(path, "identifier");
|
|
126
|
+
if (value.identifier !== null) expectString(value.identifier, source, identifierPath);
|
|
127
|
+
expectString(value.color, source, joinPath(path, "color"));
|
|
128
|
+
};
|
|
129
|
+
const validateLayerDef = (raw, source, path) => {
|
|
130
|
+
const def = expectObject(raw, source, path);
|
|
131
|
+
expectInteger(def.uid, source, joinPath(path, "uid"));
|
|
132
|
+
expectString(def.identifier, source, joinPath(path, "identifier"));
|
|
133
|
+
validateLayerType(def.type, source, joinPath(path, "type"));
|
|
134
|
+
expectNonNegativeInteger(def.gridSize, source, joinPath(path, "gridSize"));
|
|
135
|
+
optionalNumber(def, "pxOffsetX", source, path);
|
|
136
|
+
optionalNumber(def, "pxOffsetY", source, path);
|
|
137
|
+
optionalNumber(def, "parallaxFactorX", source, path);
|
|
138
|
+
optionalNumber(def, "parallaxFactorY", source, path);
|
|
139
|
+
optionalBoolean(def, "parallaxScaling", source, path);
|
|
140
|
+
if (def.intGridValues !== void 0) eachEntry(def.intGridValues, source, joinPath(path, "intGridValues"), (item, itemPath) => validateIntGridValueDef(item, source, itemPath));
|
|
141
|
+
};
|
|
142
|
+
const validateDefs = (raw, source, path) => {
|
|
143
|
+
const defs = expectObject(raw, source, path);
|
|
144
|
+
eachEntry(defs.tilesets, source, joinPath(path, "tilesets"), (item, itemPath) => validateTilesetDef(item, source, itemPath));
|
|
145
|
+
eachEntry(defs.layers, source, joinPath(path, "layers"), (item, itemPath) => validateLayerDef(item, source, itemPath));
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Validate one field-instance `__value` against the shape its `__type` declares.
|
|
149
|
+
*
|
|
150
|
+
* `null` is LDtk's "not set" and always passes - the conversion drops the
|
|
151
|
+
* property. Everything else is checked here because the conversion pass casts
|
|
152
|
+
* rather than probes: a `Point` whose value is not `{cx, cy}` used to reach the
|
|
153
|
+
* runtime as a point with `undefined` coordinates, indistinguishable from a
|
|
154
|
+
* genuine one until something read it.
|
|
155
|
+
*
|
|
156
|
+
* A `__type` this package does not model (a future LDtk field type, or an
|
|
157
|
+
* `Array<T>` element type of one) is left unchecked: forward compatibility
|
|
158
|
+
* matters more than rejecting a value the conversion already skips.
|
|
159
|
+
*/
|
|
160
|
+
const validateFieldValue = (typeName, value, source, path) => {
|
|
161
|
+
if (value === null || value === void 0) return;
|
|
162
|
+
switch (typeName) {
|
|
163
|
+
case "Int":
|
|
164
|
+
expectInteger(value, source, path);
|
|
165
|
+
return;
|
|
166
|
+
case "Float":
|
|
167
|
+
expectNumber(value, source, path);
|
|
168
|
+
return;
|
|
169
|
+
case "Bool":
|
|
170
|
+
expectBoolean(value, source, path);
|
|
171
|
+
return;
|
|
172
|
+
case "String":
|
|
173
|
+
case "Multilines":
|
|
174
|
+
case "Color":
|
|
175
|
+
case "FilePath":
|
|
176
|
+
case "Enum":
|
|
177
|
+
expectString(value, source, path);
|
|
178
|
+
return;
|
|
179
|
+
case "Point": {
|
|
180
|
+
const point = expectObject(value, source, path);
|
|
181
|
+
expectNumber(point.cx, source, joinPath(path, "cx"));
|
|
182
|
+
expectNumber(point.cy, source, joinPath(path, "cy"));
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
case "EntityRef": {
|
|
186
|
+
const ref = expectObject(value, source, path);
|
|
187
|
+
for (const key of [
|
|
188
|
+
"entityIid",
|
|
189
|
+
"layerIid",
|
|
190
|
+
"levelIid",
|
|
191
|
+
"worldIid"
|
|
192
|
+
]) expectString(ref[key], source, joinPath(path, key));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
case "Tile": {
|
|
196
|
+
const tile = expectObject(value, source, path);
|
|
197
|
+
expectInteger(tile.tilesetUid, source, joinPath(path, "tilesetUid"));
|
|
198
|
+
for (const key of [
|
|
199
|
+
"x",
|
|
200
|
+
"y",
|
|
201
|
+
"w",
|
|
202
|
+
"h"
|
|
203
|
+
]) expectNumber(tile[key], source, joinPath(path, key));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
default: if (typeName.startsWith("Array<") && typeName.endsWith(">")) {
|
|
207
|
+
const elementType = typeName.slice(6, -1);
|
|
208
|
+
eachEntry(value, source, path, (item, itemPath) => validateFieldValue(elementType, item, source, itemPath));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* A field instance is validated down to its identifier, declared type and the
|
|
214
|
+
* shape of `__value` that type implies - see {@link validateFieldValue}.
|
|
215
|
+
*/
|
|
216
|
+
const validateFieldInstance = (raw, source, path) => {
|
|
217
|
+
const field = expectObject(raw, source, path);
|
|
218
|
+
expectString(field.__identifier, source, joinPath(path, "__identifier"));
|
|
219
|
+
const typeName = expectString(field.__type, source, joinPath(path, "__type"));
|
|
220
|
+
validateFieldValue(typeName, field.__value, source, joinPath(path, "__value"));
|
|
221
|
+
};
|
|
222
|
+
const validateFieldInstances = (value, source, path) => {
|
|
223
|
+
eachEntry(value, source, path, (item, itemPath) => validateFieldInstance(item, source, itemPath));
|
|
224
|
+
};
|
|
225
|
+
const validateTile = (raw, source, path) => {
|
|
226
|
+
const tile = expectObject(raw, source, path);
|
|
227
|
+
expectNumberPair(tile.px, source, joinPath(path, "px"));
|
|
228
|
+
expectNumberPair(tile.src, source, joinPath(path, "src"));
|
|
229
|
+
expectNumber(tile.f, source, joinPath(path, "f"));
|
|
230
|
+
expectNumber(tile.t, source, joinPath(path, "t"));
|
|
231
|
+
};
|
|
232
|
+
const validateTiles = (value, source, path) => {
|
|
233
|
+
eachEntry(value, source, path, (item, itemPath) => validateTile(item, source, itemPath));
|
|
234
|
+
};
|
|
235
|
+
const validateEntityInstance = (raw, source, path) => {
|
|
236
|
+
const entity = expectObject(raw, source, path);
|
|
237
|
+
expectString(entity.__identifier, source, joinPath(path, "__identifier"));
|
|
238
|
+
expectString(entity.__type, source, joinPath(path, "__type"));
|
|
239
|
+
expectNumberPair(entity.px, source, joinPath(path, "px"));
|
|
240
|
+
expectNumber(entity.width, source, joinPath(path, "width"));
|
|
241
|
+
expectNumber(entity.height, source, joinPath(path, "height"));
|
|
242
|
+
expectNumberPair(entity.__pivot, source, joinPath(path, "__pivot"));
|
|
243
|
+
expectString(entity.iid, source, joinPath(path, "iid"));
|
|
244
|
+
expectInteger(entity.defUid, source, joinPath(path, "defUid"));
|
|
245
|
+
validateFieldInstances(entity.fieldInstances, source, joinPath(path, "fieldInstances"));
|
|
246
|
+
};
|
|
247
|
+
const validateLayerInstance = (raw, source, path) => {
|
|
248
|
+
const layer = expectObject(raw, source, path);
|
|
249
|
+
expectString(layer.__identifier, source, joinPath(path, "__identifier"));
|
|
250
|
+
validateLayerType(layer.__type, source, joinPath(path, "__type"));
|
|
251
|
+
expectNonNegativeInteger(layer.__cWid, source, joinPath(path, "__cWid"));
|
|
252
|
+
expectNonNegativeInteger(layer.__cHei, source, joinPath(path, "__cHei"));
|
|
253
|
+
expectNonNegativeInteger(layer.__gridSize, source, joinPath(path, "__gridSize"));
|
|
254
|
+
expectInteger(layer.layerDefUid, source, joinPath(path, "layerDefUid"));
|
|
255
|
+
expectInteger(layer.levelId, source, joinPath(path, "levelId"));
|
|
256
|
+
expectBoolean(layer.visible, source, joinPath(path, "visible"));
|
|
257
|
+
expectString(layer.iid, source, joinPath(path, "iid"));
|
|
258
|
+
nullableInteger(layer, "__tilesetDefUid", source, path);
|
|
259
|
+
if (layer.gridTiles !== void 0) validateTiles(layer.gridTiles, source, joinPath(path, "gridTiles"));
|
|
260
|
+
if (layer.autoLayerTiles !== void 0) validateTiles(layer.autoLayerTiles, source, joinPath(path, "autoLayerTiles"));
|
|
261
|
+
if (layer.entityInstances !== void 0) eachEntry(layer.entityInstances, source, joinPath(path, "entityInstances"), (item, itemPath) => validateEntityInstance(item, source, itemPath));
|
|
262
|
+
if (layer.intGridCsv !== void 0) eachEntry(layer.intGridCsv, source, joinPath(path, "intGridCsv"), (item, itemPath) => expectNumber(item, source, itemPath));
|
|
263
|
+
optionalNumber(layer, "pxOffsetX", source, path);
|
|
264
|
+
optionalNumber(layer, "pxOffsetY", source, path);
|
|
265
|
+
optionalNumber(layer, "opacity", source, path);
|
|
266
|
+
};
|
|
267
|
+
const validateLevel = (raw, source, path) => {
|
|
268
|
+
const level = expectObject(raw, source, path);
|
|
269
|
+
expectString(level.identifier, source, joinPath(path, "identifier"));
|
|
270
|
+
expectInteger(level.uid, source, joinPath(path, "uid"));
|
|
271
|
+
expectString(level.iid, source, joinPath(path, "iid"));
|
|
272
|
+
expectNumber(level.worldX, source, joinPath(path, "worldX"));
|
|
273
|
+
expectNumber(level.worldY, source, joinPath(path, "worldY"));
|
|
274
|
+
expectNumber(level.pxWid, source, joinPath(path, "pxWid"));
|
|
275
|
+
expectNumber(level.pxHei, source, joinPath(path, "pxHei"));
|
|
276
|
+
nullableString(level, "externalRelPath", source, path);
|
|
277
|
+
if (level.__neighbours !== void 0 && level.__neighbours !== null) eachEntry(level.__neighbours, source, joinPath(path, "__neighbours"), (item, itemPath) => {
|
|
278
|
+
const neighbour = expectObject(item, source, itemPath);
|
|
279
|
+
expectString(neighbour.levelIid, source, joinPath(itemPath, "levelIid"));
|
|
280
|
+
expectString(neighbour.dir, source, joinPath(itemPath, "dir"));
|
|
281
|
+
});
|
|
282
|
+
if (level.fieldInstances !== void 0) validateFieldInstances(level.fieldInstances, source, joinPath(path, "fieldInstances"));
|
|
283
|
+
const layerInstancesPath = joinPath(path, "layerInstances");
|
|
284
|
+
if (level.layerInstances !== null) eachEntry(level.layerInstances, source, layerInstancesPath, (item, itemPath) => validateLayerInstance(item, source, itemPath));
|
|
285
|
+
};
|
|
286
|
+
const validateWorld = (raw, source, path) => {
|
|
287
|
+
const world = expectObject(raw, source, path);
|
|
288
|
+
expectString(world.identifier, source, joinPath(path, "identifier"));
|
|
289
|
+
expectString(world.iid, source, joinPath(path, "iid"));
|
|
290
|
+
expectNumber(world.worldGridWidth, source, joinPath(path, "worldGridWidth"));
|
|
291
|
+
expectNumber(world.worldGridHeight, source, joinPath(path, "worldGridHeight"));
|
|
292
|
+
const layoutPath = joinPath(path, "worldLayout");
|
|
293
|
+
if (world.worldLayout !== null) {
|
|
294
|
+
const layout = expectString(world.worldLayout, source, layoutPath);
|
|
295
|
+
if (!WORLD_LAYOUTS.includes(layout)) throw new LdtkFormatError(source, layoutPath, `unknown world layout "${layout}" (expected one of ${WORLD_LAYOUTS.join(", ")} or null)`);
|
|
296
|
+
}
|
|
297
|
+
eachEntry(world.levels, source, joinPath(path, "levels"), (item, itemPath) => validateLevel(item, source, itemPath));
|
|
298
|
+
};
|
|
299
|
+
/**
|
|
300
|
+
* Validate the root of a `.ldtk` file and return it typed as {@link LdtkData}.
|
|
301
|
+
* Throws {@link LdtkFormatError} on any structural problem, naming the source
|
|
302
|
+
* URL and the property path of the offending value.
|
|
303
|
+
* @internal
|
|
304
|
+
*/
|
|
305
|
+
const validateLdtkData = (raw, source) => {
|
|
306
|
+
const root = expectObject(raw, source, "");
|
|
307
|
+
expectString(root.jsonVersion, source, "jsonVersion");
|
|
308
|
+
optionalNumber(root, "defaultGridSize", source, "");
|
|
309
|
+
validateDefs(root.defs, source, "defs");
|
|
310
|
+
eachEntry(root.levels, source, "levels", (item, itemPath) => validateLevel(item, source, itemPath));
|
|
311
|
+
if (root.worlds !== void 0) eachEntry(root.worlds, source, "worlds", (item, itemPath) => validateWorld(item, source, itemPath));
|
|
312
|
+
return raw;
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Validate an external `.ldtkl` payload and return it typed as
|
|
316
|
+
* {@link LdtkLevel}. Paths in a thrown {@link LdtkFormatError} are relative to
|
|
317
|
+
* the level object, since that object is the root of the `.ldtkl` file.
|
|
318
|
+
* @internal
|
|
319
|
+
*/
|
|
320
|
+
const validateLdtkLevelData = (raw, source) => {
|
|
321
|
+
validateLevel(raw, source, "");
|
|
322
|
+
return raw;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
//#endregion
|
|
326
|
+
export { LdtkFormatError, validateLdtkData, validateLdtkLevelData };
|
|
327
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","names":[],"sources":["../../src/validate.ts"],"sourcesContent":["// Runtime validation for LDtk JSON (`.ldtk` / `.ldtkl`) documents. Both entry\n// points take `unknown` (the result of `JSON.parse`) and either return the same\n// value typed as `LdtkData` / `LdtkLevel`, or throw an {@link LdtkFormatError}\n// carrying the source URL and a JSON-path-like pointer to the offending value.\n//\n// Deliberately leaner than the Tiled adapter's `validate.ts`: it checks the\n// fields declared as required by this package's `LdtkData` model plus the\n// optional ones the adapter actually reads, and nothing else. Field-instance\n// `__value`s are checked against the shape their `__type` declares, because the\n// conversion pass casts rather than probes; a `__type` this package does not\n// model is left alone so a future LDtk field type still loads.\n//\n// Unlike the Tiled validator, this one asserts in place and hands the caller\n// back the very object it was given rather than rebuilding a normalised copy.\n// That keeps `LdtkMap.data` byte-for-byte what LDtk wrote, matching the\n// \"unknown fields are not stripped at parse time\" contract documented on\n// `LdtkData`.\n\nimport type { LdtkData, LdtkLayerType, LdtkLevel, LdtkWorldLayout } from './LdtkData';\n\n/**\n * Thrown when an LDtk JSON document does not match the expected shape.\n * `source` is the URL of the file being parsed; `path` is a JSON-path-like\n * pointer (e.g. `levels[0].layerInstances[1].gridTiles[0].px`) to the\n * offending value, or `''` for the document root.\n */\nexport class LdtkFormatError extends Error {\n public readonly source: string;\n public readonly path: string;\n\n public constructor(source: string, path: string, message: string) {\n super(`Invalid LDtk data in \"${source}\" at ${path === '' ? '<root>' : path}: ${message}`);\n this.name = 'LdtkFormatError';\n this.source = source;\n this.path = path;\n }\n}\n\n// ── Primitive helpers ────────────────────────────────────────────────────────\n\nconst describeValue = (value: unknown): string => {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n if (Array.isArray(value)) return 'an array';\n return typeof value;\n};\n\nconst joinPath = (path: string, key: string | number): string => {\n if (typeof key === 'number') return `${path}[${key}]`;\n return path === '' ? key : `${path}.${key}`;\n};\n\nconst expectObject = (value: unknown, source: string, path: string): Record<string, unknown> => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new LdtkFormatError(source, path, `expected an object, got ${describeValue(value)}`);\n }\n return value as Record<string, unknown>;\n};\n\nconst expectArray = (value: unknown, source: string, path: string): readonly unknown[] => {\n if (!Array.isArray(value)) {\n throw new LdtkFormatError(source, path, `expected an array, got ${describeValue(value)}`);\n }\n return value as readonly unknown[];\n};\n\nconst expectString = (value: unknown, source: string, path: string): string => {\n if (typeof value !== 'string') {\n throw new LdtkFormatError(source, path, `expected a string, got ${describeValue(value)}`);\n }\n return value;\n};\n\nconst expectNumber = (value: unknown, source: string, path: string): number => {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new LdtkFormatError(source, path, `expected a finite number, got ${describeValue(value)}`);\n }\n return value;\n};\n\nconst expectInteger = (value: unknown, source: string, path: string): number => {\n const n = expectNumber(value, source, path);\n if (!Number.isInteger(n)) {\n throw new LdtkFormatError(source, path, `expected an integer, got ${n}`);\n }\n return n;\n};\n\nconst expectNonNegativeInteger = (value: unknown, source: string, path: string): number => {\n const n = expectInteger(value, source, path);\n if (n < 0) {\n throw new LdtkFormatError(source, path, `expected a non-negative integer, got ${n}`);\n }\n return n;\n};\n\nconst expectPositiveInteger = (value: unknown, source: string, path: string): number => {\n const n = expectInteger(value, source, path);\n if (n <= 0) {\n throw new LdtkFormatError(source, path, `expected a positive integer, got ${n}`);\n }\n return n;\n};\n\nconst expectBoolean = (value: unknown, source: string, path: string): void => {\n if (typeof value !== 'boolean') {\n throw new LdtkFormatError(source, path, `expected a boolean, got ${describeValue(value)}`);\n }\n};\n\n/** `[x, y]` pixel/grid pairs (`px`, `src`, `__pivot`) are always two finite numbers. */\nconst expectNumberPair = (value: unknown, source: string, path: string): void => {\n const pair = expectArray(value, source, path);\n if (pair.length !== 2) {\n throw new LdtkFormatError(source, path, `expected a pair of numbers, got ${pair.length} entries`);\n }\n expectNumber(pair[0], source, joinPath(path, 0));\n expectNumber(pair[1], source, joinPath(path, 1));\n};\n\nconst optionalNumber = (obj: Record<string, unknown>, key: string, source: string, path: string): void => {\n if (obj[key] !== undefined) expectNumber(obj[key], source, joinPath(path, key));\n};\n\nconst optionalNonNegativeInteger = (obj: Record<string, unknown>, key: string, source: string, path: string): void => {\n if (obj[key] !== undefined) expectNonNegativeInteger(obj[key], source, joinPath(path, key));\n};\n\n/**\n * LDtk writes an unset optional as an explicit `null` rather than omitting the\n * key, so a validator that only tolerates `undefined` rejects the files the\n * editor actually produces.\n */\nconst nullableString = (obj: Record<string, unknown>, key: string, source: string, path: string): void => {\n if (obj[key] !== undefined && obj[key] !== null) expectString(obj[key], source, joinPath(path, key));\n};\n\n/** See {@link nullableString}. */\nconst nullableInteger = (obj: Record<string, unknown>, key: string, source: string, path: string): void => {\n if (obj[key] !== undefined && obj[key] !== null) expectInteger(obj[key], source, joinPath(path, key));\n};\n\nconst optionalBoolean = (obj: Record<string, unknown>, key: string, source: string, path: string): void => {\n if (obj[key] !== undefined) expectBoolean(obj[key], source, joinPath(path, key));\n};\n\nconst eachEntry = (value: unknown, source: string, path: string, visit: (item: unknown, itemPath: string) => void): void => {\n const array = expectArray(value, source, path);\n for (let i = 0; i < array.length; i++) {\n visit(array[i], joinPath(path, i));\n }\n};\n\n// ── Definitions ──────────────────────────────────────────────────────────────\n\nconst LAYER_TYPES: readonly LdtkLayerType[] = ['Tiles', 'IntGrid', 'Entities', 'AutoLayer'];\nconst WORLD_LAYOUTS: readonly LdtkWorldLayout[] = ['Free', 'GridVania', 'LinearHorizontal', 'LinearVertical'];\n\nconst validateLayerType = (value: unknown, source: string, path: string): void => {\n const type = expectString(value, source, path);\n if (!LAYER_TYPES.includes(type as LdtkLayerType)) {\n throw new LdtkFormatError(source, path, `unknown layer type \"${type}\" (expected one of ${LAYER_TYPES.join(', ')})`);\n }\n};\n\nconst validateTilesetDef = (raw: unknown, source: string, path: string): void => {\n const def = expectObject(raw, source, path);\n\n expectInteger(def.uid, source, joinPath(path, 'uid'));\n expectString(def.identifier, source, joinPath(path, 'identifier'));\n\n // `relPath` is null for an embedded (\"embed atlas\") tileset, which the loader\n // skips - anything other than a string or null is malformed.\n const relPathPath = joinPath(path, 'relPath');\n if (def.relPath !== null) expectString(def.relPath, source, relPathPath);\n\n // A zero or negative grid size would make the loader's column arithmetic\n // divide by zero and yield a non-finite tile count.\n expectPositiveInteger(def.tileGridSize, source, joinPath(path, 'tileGridSize'));\n expectNonNegativeInteger(def.pxWid, source, joinPath(path, 'pxWid'));\n expectNonNegativeInteger(def.pxHei, source, joinPath(path, 'pxHei'));\n optionalNonNegativeInteger(def, 'spacing', source, path);\n optionalNonNegativeInteger(def, 'padding', source, path);\n};\n\nconst validateIntGridValueDef = (raw: unknown, source: string, path: string): void => {\n const value = expectObject(raw, source, path);\n\n expectNumber(value.value, source, joinPath(path, 'value'));\n const identifierPath = joinPath(path, 'identifier');\n if (value.identifier !== null) expectString(value.identifier, source, identifierPath);\n expectString(value.color, source, joinPath(path, 'color'));\n};\n\nconst validateLayerDef = (raw: unknown, source: string, path: string): void => {\n const def = expectObject(raw, source, path);\n\n expectInteger(def.uid, source, joinPath(path, 'uid'));\n expectString(def.identifier, source, joinPath(path, 'identifier'));\n validateLayerType(def.type, source, joinPath(path, 'type'));\n expectNonNegativeInteger(def.gridSize, source, joinPath(path, 'gridSize'));\n optionalNumber(def, 'pxOffsetX', source, path);\n optionalNumber(def, 'pxOffsetY', source, path);\n optionalNumber(def, 'parallaxFactorX', source, path);\n optionalNumber(def, 'parallaxFactorY', source, path);\n optionalBoolean(def, 'parallaxScaling', source, path);\n\n if (def.intGridValues !== undefined) {\n eachEntry(def.intGridValues, source, joinPath(path, 'intGridValues'), (item, itemPath) => validateIntGridValueDef(item, source, itemPath));\n }\n};\n\nconst validateDefs = (raw: unknown, source: string, path: string): void => {\n const defs = expectObject(raw, source, path);\n\n eachEntry(defs.tilesets, source, joinPath(path, 'tilesets'), (item, itemPath) => validateTilesetDef(item, source, itemPath));\n eachEntry(defs.layers, source, joinPath(path, 'layers'), (item, itemPath) => validateLayerDef(item, source, itemPath));\n};\n\n// ── Instances ────────────────────────────────────────────────────────────────\n\n/**\n * Validate one field-instance `__value` against the shape its `__type` declares.\n *\n * `null` is LDtk's \"not set\" and always passes - the conversion drops the\n * property. Everything else is checked here because the conversion pass casts\n * rather than probes: a `Point` whose value is not `{cx, cy}` used to reach the\n * runtime as a point with `undefined` coordinates, indistinguishable from a\n * genuine one until something read it.\n *\n * A `__type` this package does not model (a future LDtk field type, or an\n * `Array<T>` element type of one) is left unchecked: forward compatibility\n * matters more than rejecting a value the conversion already skips.\n */\nconst validateFieldValue = (typeName: string, value: unknown, source: string, path: string): void => {\n if (value === null || value === undefined) return;\n\n switch (typeName) {\n case 'Int':\n expectInteger(value, source, path);\n return;\n\n case 'Float':\n expectNumber(value, source, path);\n return;\n\n case 'Bool':\n expectBoolean(value, source, path);\n return;\n\n case 'String':\n case 'Multilines':\n case 'Color':\n case 'FilePath':\n case 'Enum':\n expectString(value, source, path);\n return;\n\n case 'Point': {\n const point = expectObject(value, source, path);\n\n expectNumber(point.cx, source, joinPath(path, 'cx'));\n expectNumber(point.cy, source, joinPath(path, 'cy'));\n\n return;\n }\n\n case 'EntityRef': {\n const ref = expectObject(value, source, path);\n\n for (const key of ['entityIid', 'layerIid', 'levelIid', 'worldIid'] as const) {\n expectString(ref[key], source, joinPath(path, key));\n }\n\n return;\n }\n\n case 'Tile': {\n const tile = expectObject(value, source, path);\n\n expectInteger(tile.tilesetUid, source, joinPath(path, 'tilesetUid'));\n\n for (const key of ['x', 'y', 'w', 'h'] as const) {\n expectNumber(tile[key], source, joinPath(path, key));\n }\n\n return;\n }\n\n default:\n if (typeName.startsWith('Array<') && typeName.endsWith('>')) {\n const elementType = typeName.slice('Array<'.length, -1);\n\n eachEntry(value, source, path, (item, itemPath) => validateFieldValue(elementType, item, source, itemPath));\n }\n }\n};\n\n/**\n * A field instance is validated down to its identifier, declared type and the\n * shape of `__value` that type implies - see {@link validateFieldValue}.\n */\nconst validateFieldInstance = (raw: unknown, source: string, path: string): void => {\n const field = expectObject(raw, source, path);\n\n expectString(field.__identifier, source, joinPath(path, '__identifier'));\n\n const typeName = expectString(field.__type, source, joinPath(path, '__type'));\n\n validateFieldValue(typeName, field.__value, source, joinPath(path, '__value'));\n};\n\nconst validateFieldInstances = (value: unknown, source: string, path: string): void => {\n eachEntry(value, source, path, (item, itemPath) => validateFieldInstance(item, source, itemPath));\n};\n\nconst validateTile = (raw: unknown, source: string, path: string): void => {\n const tile = expectObject(raw, source, path);\n\n expectNumberPair(tile.px, source, joinPath(path, 'px'));\n expectNumberPair(tile.src, source, joinPath(path, 'src'));\n expectNumber(tile.f, source, joinPath(path, 'f'));\n expectNumber(tile.t, source, joinPath(path, 't'));\n};\n\nconst validateTiles = (value: unknown, source: string, path: string): void => {\n eachEntry(value, source, path, (item, itemPath) => validateTile(item, source, itemPath));\n};\n\nconst validateEntityInstance = (raw: unknown, source: string, path: string): void => {\n const entity = expectObject(raw, source, path);\n\n expectString(entity.__identifier, source, joinPath(path, '__identifier'));\n expectString(entity.__type, source, joinPath(path, '__type'));\n expectNumberPair(entity.px, source, joinPath(path, 'px'));\n expectNumber(entity.width, source, joinPath(path, 'width'));\n expectNumber(entity.height, source, joinPath(path, 'height'));\n expectNumberPair(entity.__pivot, source, joinPath(path, '__pivot'));\n expectString(entity.iid, source, joinPath(path, 'iid'));\n expectInteger(entity.defUid, source, joinPath(path, 'defUid'));\n validateFieldInstances(entity.fieldInstances, source, joinPath(path, 'fieldInstances'));\n};\n\nconst validateLayerInstance = (raw: unknown, source: string, path: string): void => {\n const layer = expectObject(raw, source, path);\n\n expectString(layer.__identifier, source, joinPath(path, '__identifier'));\n validateLayerType(layer.__type, source, joinPath(path, '__type'));\n expectNonNegativeInteger(layer.__cWid, source, joinPath(path, '__cWid'));\n expectNonNegativeInteger(layer.__cHei, source, joinPath(path, '__cHei'));\n expectNonNegativeInteger(layer.__gridSize, source, joinPath(path, '__gridSize'));\n expectInteger(layer.layerDefUid, source, joinPath(path, 'layerDefUid'));\n expectInteger(layer.levelId, source, joinPath(path, 'levelId'));\n expectBoolean(layer.visible, source, joinPath(path, 'visible'));\n expectString(layer.iid, source, joinPath(path, 'iid'));\n\n nullableInteger(layer, '__tilesetDefUid', source, path);\n if (layer.gridTiles !== undefined) {\n validateTiles(layer.gridTiles, source, joinPath(path, 'gridTiles'));\n }\n if (layer.autoLayerTiles !== undefined) {\n validateTiles(layer.autoLayerTiles, source, joinPath(path, 'autoLayerTiles'));\n }\n if (layer.entityInstances !== undefined) {\n eachEntry(layer.entityInstances, source, joinPath(path, 'entityInstances'), (item, itemPath) => validateEntityInstance(item, source, itemPath));\n }\n if (layer.intGridCsv !== undefined) {\n eachEntry(layer.intGridCsv, source, joinPath(path, 'intGridCsv'), (item, itemPath) => expectNumber(item, source, itemPath));\n }\n optionalNumber(layer, 'pxOffsetX', source, path);\n optionalNumber(layer, 'pxOffsetY', source, path);\n optionalNumber(layer, 'opacity', source, path);\n};\n\nconst validateLevel = (raw: unknown, source: string, path: string): void => {\n const level = expectObject(raw, source, path);\n\n expectString(level.identifier, source, joinPath(path, 'identifier'));\n expectInteger(level.uid, source, joinPath(path, 'uid'));\n expectString(level.iid, source, joinPath(path, 'iid'));\n expectNumber(level.worldX, source, joinPath(path, 'worldX'));\n expectNumber(level.worldY, source, joinPath(path, 'worldY'));\n expectNumber(level.pxWid, source, joinPath(path, 'pxWid'));\n expectNumber(level.pxHei, source, joinPath(path, 'pxHei'));\n nullableString(level, 'externalRelPath', source, path);\n\n // Same explicit-null rule as the fields above: an isolated level can arrive\n // with a null here rather than an empty array.\n if (level.__neighbours !== undefined && level.__neighbours !== null) {\n eachEntry(level.__neighbours, source, joinPath(path, '__neighbours'), (item, itemPath) => {\n const neighbour = expectObject(item, source, itemPath);\n expectString(neighbour.levelIid, source, joinPath(itemPath, 'levelIid'));\n expectString(neighbour.dir, source, joinPath(itemPath, 'dir'));\n });\n }\n\n if (level.fieldInstances !== undefined) {\n validateFieldInstances(level.fieldInstances, source, joinPath(path, 'fieldInstances'));\n }\n\n // `null` marks a level whose layers live in a separate `.ldtkl` file; the\n // loader resolves those before conversion.\n const layerInstancesPath = joinPath(path, 'layerInstances');\n if (level.layerInstances !== null) {\n eachEntry(level.layerInstances, source, layerInstancesPath, (item, itemPath) => validateLayerInstance(item, source, itemPath));\n }\n};\n\nconst validateWorld = (raw: unknown, source: string, path: string): void => {\n const world = expectObject(raw, source, path);\n\n expectString(world.identifier, source, joinPath(path, 'identifier'));\n expectString(world.iid, source, joinPath(path, 'iid'));\n expectNumber(world.worldGridWidth, source, joinPath(path, 'worldGridWidth'));\n expectNumber(world.worldGridHeight, source, joinPath(path, 'worldGridHeight'));\n\n const layoutPath = joinPath(path, 'worldLayout');\n if (world.worldLayout !== null) {\n const layout = expectString(world.worldLayout, source, layoutPath);\n if (!WORLD_LAYOUTS.includes(layout as LdtkWorldLayout)) {\n throw new LdtkFormatError(source, layoutPath, `unknown world layout \"${layout}\" (expected one of ${WORLD_LAYOUTS.join(', ')} or null)`);\n }\n }\n\n eachEntry(world.levels, source, joinPath(path, 'levels'), (item, itemPath) => validateLevel(item, source, itemPath));\n};\n\n// ── Entry points ─────────────────────────────────────────────────────────────\n\n/**\n * Validate the root of a `.ldtk` file and return it typed as {@link LdtkData}.\n * Throws {@link LdtkFormatError} on any structural problem, naming the source\n * URL and the property path of the offending value.\n * @internal\n */\nexport const validateLdtkData = (raw: unknown, source: string): LdtkData => {\n const root = expectObject(raw, source, '');\n\n expectString(root.jsonVersion, source, 'jsonVersion');\n optionalNumber(root, 'defaultGridSize', source, '');\n validateDefs(root.defs, source, 'defs');\n\n // Both root shapes carry `levels`: it holds every level in a single-world\n // document and is present-but-empty once `worlds[]` is used.\n eachEntry(root.levels, source, 'levels', (item, itemPath) => validateLevel(item, source, itemPath));\n\n if (root.worlds !== undefined) {\n eachEntry(root.worlds, source, 'worlds', (item, itemPath) => validateWorld(item, source, itemPath));\n }\n\n return raw as LdtkData;\n};\n\n/**\n * Validate an external `.ldtkl` payload and return it typed as\n * {@link LdtkLevel}. Paths in a thrown {@link LdtkFormatError} are relative to\n * the level object, since that object is the root of the `.ldtkl` file.\n * @internal\n */\nexport const validateLdtkLevelData = (raw: unknown, source: string): LdtkLevel => {\n validateLevel(raw, source, '');\n\n return raw as LdtkLevel;\n};\n"],"mappings":"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"}
|