@adobe/ecs-persistence 0.0.1 → 0.1.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 +45 -27
- package/dist/index.js +318 -80
- package/dist/index.js.map +1 -1
- package/dist/types/decode.d.ts +7 -4
- package/dist/types/encode.d.ts +2 -2
- package/dist/types/format/format.d.ts +4 -4
- package/dist/types/guide/01-saving.d.ts +40 -0
- package/dist/types/guide/02-copying.d.ts +23 -0
- package/dist/types/guide/03-chunks.d.ts +23 -0
- package/dist/types/guide/04-settings.d.ts +45 -0
- package/dist/types/index.d.ts +2 -3
- package/dist/types/match/keys.d.ts +36 -0
- package/dist/types/match/match.d.ts +13 -0
- package/dist/types/program/contents.d.ts +5 -3
- package/dist/types/program/program.d.ts +12 -5
- package/dist/types/selection/selection.d.ts +10 -3
- package/dist/types/snapshot.d.ts +15 -5
- package/dist/types/spawn.d.ts +22 -13
- package/package.json +10 -3
- package/src/decode.ts +6 -5
- package/src/encode.ts +2 -2
- package/src/format/format.ts +8 -8
- package/src/guide/01-saving.md +18 -0
- package/src/guide/01-saving.out +4 -0
- package/src/guide/01-saving.ts +55 -0
- package/src/guide/02-copying.md +15 -0
- package/src/guide/02-copying.out +4 -0
- package/src/guide/02-copying.ts +70 -0
- package/src/guide/03-chunks.md +15 -0
- package/src/guide/03-chunks.out +3 -0
- package/src/guide/03-chunks.ts +75 -0
- package/src/guide/04-settings.md +15 -0
- package/src/guide/04-settings.out +4 -0
- package/src/guide/04-settings.ts +75 -0
- package/src/guide/README.md +10 -0
- package/src/index.ts +2 -3
- package/src/match/keys.ts +105 -0
- package/src/match/match.ts +165 -0
- package/src/program/contents.ts +5 -3
- package/src/program/program.ts +26 -15
- package/src/selection/selection.ts +22 -8
- package/src/snapshot.ts +16 -8
- package/src/spawn.ts +36 -17
- package/src/persist.ts +0 -15
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["header"],"sources":["../src/persist.ts","../src/program/program.ts","../src/values/values.ts","../src/format/format.ts","../src/program/contents.ts","../src/decode.ts","../src/encode.ts","../src/selection/selection.ts","../src/snapshot.ts","../src/spawn.ts"],"sourcesContent":["// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { Component, Components } from \"@adobe/ecs\";\n\n/**\n * The trait marking a component or resource to persist, its value the stable name saved data names it by: unlike its\n * key's id, the name stays the same across versions and moves of its module. Not on a derived component, which its\n * rule derives again once its entity is loaded.\n */\nexport const persist = Symbol();\n\ndeclare module \"@adobe/ecs\" {\n interface Traits<N> {\n readonly [persist]: N extends Component.Key ? Components[N] extends { readonly derived: true } ? never : string : string;\n }\n}\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport { Component, Resource, type Schema } from \"@adobe/ecs\";\nimport { persist } from \"../persist.js\";\n\n/** A key persisted, with its name and schema. */\nexport type Persisted<K extends symbol = symbol> = { readonly name: string; readonly key: K; readonly schema: Schema };\n\n/** What the program persists: its components and resources by name. */\nexport type Program = {\n readonly components: ReadonlyMap<string, Persisted<Component.Key>>;\n readonly resources: ReadonlyMap<string, Persisted<Resource.Key>>;\n};\n\n/** The keys of `declarations` with a `persist` trait by name, or why one can't persist: it isn't data, or a name is taken. */\nconst namedIn = <K extends symbol>(declarations: ReadonlyMap<K, Component.Declared>, kind: string): ReadonlyMap<string, Persisted<K>> | string => {\n const named = new Map<string, Persisted<K>>();\n for (const [key, declaration] of declarations) {\n const name: unknown = declaration[persist];\n if (typeof name !== \"string\") continue;\n if (declaration.type === undefined) return `The ${kind} persisted as \"${name}\" isn't data: it has no schema.`;\n if (named.has(name)) return `Two ${kind}s are persisted as \"${name}\".`;\n named.set(name, { name, key, schema: declaration.type });\n }\n return named;\n};\n\n/** What the program persists, as its modules registered so far declare it, or why it can't. */\nexport const programOf = (): Program | string => {\n const components = namedIn(Component.declarations(), \"component\");\n if (typeof components === \"string\") return components;\n const resources = namedIn(Resource.declarations(), \"resource\");\n if (typeof resources === \"string\") return resources;\n return { components, resources };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { Schema } from \"@adobe/ecs\";\n\n// Values of components stored as values, strings and other data that isn't a fixed layout, as JSON their schema reads\n// back: a map as its entries, a set as its items, numbers JSON lacks as text, and a union's value as its member's\n// index and its JSON.\n\n/** A number JSON can't hold, as text: `NaN`, `Infinity`, `-Infinity` or `-0`. */\nconst numberToJson = (value: number): number | string =>\n Number.isNaN(value) ? \"NaN\" : value === Infinity ? \"Infinity\" : value === -Infinity ? \"-Infinity\" : Object.is(value, -0) ? \"-0\" : value;\n\nconst numberFromJson = (json: unknown): unknown => {\n if (typeof json !== \"string\") return json;\n switch (json) {\n case \"NaN\": return NaN;\n case \"Infinity\": return Infinity;\n case \"-Infinity\": return -Infinity;\n case \"-0\": return -0;\n default: return json;\n }\n};\n\n/** Whether a value is of the JSON kind a schema's values are, to pick a union's member. */\nconst fits = (schema: Schema, value: unknown): boolean => {\n if (\"const\" in schema) return Object.is(schema.const, value) || JSON.stringify(schema.const) === JSON.stringify(value);\n if (\"anyOf\" in schema) return schema.anyOf.some(member => fits(member, value));\n if (\"allOf\" in schema) return schema.allOf.every(member => fits(member, value));\n if (!(\"type\" in schema)) return true;\n switch (schema.type) {\n case \"number\": case \"integer\": return typeof value === \"number\";\n case \"string\": return typeof value === \"string\";\n case \"boolean\": return typeof value === \"boolean\";\n case \"null\": return value === null;\n case \"array\": return Array.isArray(value);\n case \"set\": return value instanceof Set;\n case \"map\": return value instanceof Map;\n case \"object\": return typeof value === \"object\" && value !== null && !Array.isArray(value) && !(value instanceof Set) && !(value instanceof Map);\n default: return true;\n }\n};\n\n/** A value of `schema` as JSON. */\nexport const toJson = (schema: Schema, value: unknown): unknown => {\n if (\"anyOf\" in schema) {\n const index = schema.anyOf.findIndex(candidate => fits(candidate, value));\n const member = schema.anyOf[index];\n return member === undefined ? [-1, value] : [index, toJson(member, value)];\n }\n if (typeof value === \"number\") return numberToJson(value);\n if (!(\"type\" in schema)) return value;\n switch (schema.type) {\n case \"array\": return Array.isArray(value) ? value.map(item => toJson(schema.items, item)) : value;\n case \"set\": return value instanceof Set ? Array.from(value, item => toJson(schema.items, item)) : value;\n case \"map\": return value instanceof Map ? Array.from(value, ([key, item]) => [toJson(schema.keys, key), toJson(schema.values, item)]) : value;\n case \"object\": {\n if (typeof value !== \"object\" || value === null) return value;\n const json: { [name: string]: unknown } = {};\n for (const name of Object.keys(value)) {\n const field = schema.properties[name];\n const item: unknown = Reflect.get(value, name);\n if (item !== undefined) json[name] = field === undefined ? item : toJson(field, item);\n }\n return json;\n }\n default: return value;\n }\n};\n\n/** A value of `schema` read back from its JSON. */\nexport const fromJson = (schema: Schema, json: unknown): unknown => {\n if (\"anyOf\" in schema) {\n if (!Array.isArray(json) || typeof json[0] !== \"number\") return json;\n const member = schema.anyOf[json[0]];\n return member === undefined ? json[1] : fromJson(member, json[1]);\n }\n if (\"const\" in schema) return schema.const;\n if (!(\"type\" in schema)) return json;\n switch (schema.type) {\n case \"number\": case \"integer\": return numberFromJson(json);\n case \"array\": return Array.isArray(json) ? json.map(item => fromJson(schema.items, item)) : json;\n case \"set\": return Array.isArray(json) ? new Set(json.map(item => fromJson(schema.items, item))) : json;\n case \"map\": return Array.isArray(json)\n ? new Map(json.map(entry => Array.isArray(entry) ? [fromJson(schema.keys, entry[0]), fromJson(schema.values, entry[1])] : [entry, undefined]))\n : json;\n case \"object\": {\n if (typeof json !== \"object\" || json === null || Array.isArray(json)) return json;\n const value: { [name: string]: unknown } = {};\n for (const name of Object.keys(json)) {\n const field = schema.properties[name];\n const item: unknown = Reflect.get(json, name);\n value[name] = field === undefined ? item : fromJson(field, item);\n }\n return value;\n }\n default: return json;\n }\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { Component, Resource, Schema, World } from \"@adobe/ecs\";\nimport type { Failure } from \"../failure.js\";\nimport type { Contents } from \"../program/contents.js\";\nimport { programOf, type Persisted } from \"../program/program.js\";\nimport { fromJson, toJson } from \"../values/values.js\";\n\n/**\n * The binary form of a snapshot, little-endian:\n * - 16 bytes: `ECSP`, the format's version as a u32, the header's length in bytes as a u32, and 4 bytes of zeros;\n * - the header, JSON text padded with spaces to a multiple of 8 bytes: each component and resource by name with its\n * schema, and each table's count and where its entities and columns are;\n * - blocks, each starting at a multiple of 8 bytes: a table's entities as f64s, a column's bytes as its components'\n * layout holds them, and a column of values as JSON text.\n * A loader views the blocks of bytes where they are, in the buffer it's given.\n */\n\nconst magic = [0x45, 0x43, 0x53, 0x50];\nconst version = 1;\nconst prefix = 16;\n\n/** Where a column's data is. */\ntype ColumnJson =\n | { readonly kind: \"bytes\"; readonly offset: number; readonly length: number; readonly stride: number }\n | { readonly kind: \"values\"; readonly offset: number; readonly length: number }\n | { readonly kind: \"value\"; readonly value: unknown };\n\ntype Header = {\n readonly components: readonly { readonly name: string; readonly schema: Schema }[];\n readonly resources: readonly { readonly name: string; readonly schema: Schema; readonly value: unknown }[];\n readonly tables: readonly { readonly count: number; readonly entities: number; readonly columns: readonly ({ readonly component: number } & ColumnJson)[] }[];\n};\n\nconst aligned = (offset: number): number => Math.ceil(offset / 8) * 8;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** A snapshot's contents as bytes. */\nexport const encodeContents = (snapshot: Contents): Uint8Array => {\n const blocks: Uint8Array[] = [];\n let offset = 0;\n /** Places a block, returning its offset from the first block. */\n const place = (bytes: Uint8Array): number => {\n const at = offset;\n blocks.push(bytes);\n offset = aligned(offset + bytes.byteLength);\n return at;\n };\n const indexOf = new Map(snapshot.components.map((persisted, index) => [persisted.key, index]));\n const tables = snapshot.tables.map(table => {\n const entities = place(new Uint8Array(table.entities.buffer, table.entities.byteOffset, table.entities.byteLength));\n const columns = snapshot.components.flatMap((persisted): ({ readonly component: number } & ColumnJson)[] => {\n const key: symbol = persisted.key;\n const column: World.Table.Column | undefined = Reflect.get(table.columns, key);\n const component = indexOf.get(persisted.key) ?? -1;\n if (column === undefined) return [];\n if (column.kind === \"bytes\") return [{ component, kind: \"bytes\", offset: place(column.bytes), length: column.bytes.byteLength, stride: column.stride }];\n if (column.kind === \"value\") return [{ component, kind: \"value\", value: toJson(persisted.schema, column.value) }];\n const text = encoder.encode(JSON.stringify(column.values.map(value => toJson(persisted.schema, value))));\n return [{ component, kind: \"values\", offset: place(text), length: text.byteLength }];\n });\n return { count: table.count, entities, columns };\n });\n const header: Header = {\n components: snapshot.components.map(({ name, schema }) => ({ name, schema })),\n resources: snapshot.resources.map(({ persisted, value }) => ({ name: persisted.name, schema: persisted.schema, value: toJson(persisted.schema, value) })),\n tables,\n };\n const text = encoder.encode(JSON.stringify(header));\n const headerLength = aligned(text.byteLength);\n const bytes = new Uint8Array(prefix + headerLength + offset);\n bytes.set(magic, 0);\n const view = new DataView(bytes.buffer);\n view.setUint32(4, version, true);\n view.setUint32(8, headerLength, true);\n bytes.set(text, prefix);\n bytes.fill(0x20, prefix + text.byteLength, prefix + headerLength);\n let at = prefix + headerLength;\n for (const block of blocks) {\n bytes.set(block, at);\n at = aligned(at + block.byteLength);\n }\n return bytes;\n};\n\nconst isObject = (value: unknown): value is object => typeof value === \"object\" && value !== null;\n\n/** The header's JSON, checked enough to read it without casts, or null. */\nconst headerOf = (json: unknown): Header | null => {\n if (!isObject(json)) return null;\n const components: unknown = Reflect.get(json, \"components\");\n const resources: unknown = Reflect.get(json, \"resources\");\n const tables: unknown = Reflect.get(json, \"tables\");\n if (!Array.isArray(components) || !Array.isArray(resources) || !Array.isArray(tables)) return null;\n const named = (item: unknown): boolean => isObject(item) && typeof Reflect.get(item, \"name\") === \"string\" && isObject(Reflect.get(item, \"schema\"));\n if (!components.every(named) || !resources.every(named)) return null;\n const isColumn = (item: unknown): boolean => {\n if (!isObject(item) || typeof Reflect.get(item, \"component\") !== \"number\") return false;\n const kind: unknown = Reflect.get(item, \"kind\");\n if (kind === \"value\") return true;\n const numbers = kind === \"bytes\" ? [\"offset\", \"length\", \"stride\"] : kind === \"values\" ? [\"offset\", \"length\"] : null;\n return numbers !== null && numbers.every(name => typeof Reflect.get(item, name) === \"number\");\n };\n const isTable = (item: unknown): boolean => {\n if (!isObject(item) || typeof Reflect.get(item, \"count\") !== \"number\" || typeof Reflect.get(item, \"entities\") !== \"number\") return false;\n const columns: unknown = Reflect.get(item, \"columns\");\n return Array.isArray(columns) && columns.every(isColumn);\n };\n if (!tables.every(isTable)) return null;\n // Checked above: each item has the fields the header's type names, of their types.\n const header = json as Header;\n return header;\n};\n\n/** Whether two schemas are the same, by their JSON in their properties' order, which places a struct's fields. */\nconst sameSchema = (a: Schema, b: Schema): boolean => JSON.stringify(a) === JSON.stringify(b);\n\n/**\n * The snapshot contents `bytes` holds, its components and resources by name as this program persists them, its blocks of\n * bytes viewed where they are; or why it can't be read: it isn't one, or names what this program doesn't persist, or\n * persists with another schema.\n */\nexport const decodeContents = (bytes: Uint8Array): Contents | Failure => {\n if (bytes.byteLength < prefix || magic.some((byte, at) => bytes[at] !== byte)) return { failure: \"These bytes aren't a snapshot.\" };\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const found = view.getUint32(4, true);\n if (found !== version) return { failure: `This snapshot's format is version ${found}; this package reads version ${version}.` };\n const headerLength = view.getUint32(8, true);\n let json: unknown;\n try {\n json = JSON.parse(decoder.decode(bytes.subarray(prefix, prefix + headerLength)));\n }\n catch {\n return { failure: \"This snapshot's header isn't JSON.\" };\n }\n const header = headerOf(json);\n if (header === null) return { failure: \"This snapshot's header isn't one this package writes.\" };\n const program = programOf();\n if (typeof program === \"string\") return { failure: program };\n const components: Persisted<Component.Key>[] = [];\n for (const { name, schema } of header.components) {\n const persisted = program.components.get(name);\n if (persisted === undefined) return { failure: `This snapshot holds component \"${name}\", which this program doesn't persist.` };\n if (!sameSchema(persisted.schema, schema)) return { failure: `Component \"${name}\" has another schema here than in this snapshot.` };\n components.push(persisted);\n }\n const resources: { persisted: Persisted<Resource.Key>; value: unknown }[] = [];\n for (const { name, schema, value } of header.resources) {\n const persisted = program.resources.get(name);\n if (persisted === undefined) return { failure: `This snapshot holds resource \"${name}\", which this program doesn't persist.` };\n if (!sameSchema(persisted.schema, schema)) return { failure: `Resource \"${name}\" has another schema here than in this snapshot.` };\n resources.push({ persisted, value: fromJson(schema, value) });\n }\n // Blocks are 8-byte aligned in the buffer the format was written to; one given at another offset is copied once.\n const blocks = (bytes.byteOffset + prefix + headerLength) % 8 === 0 ? bytes : bytes.slice();\n const base = blocks.byteOffset + prefix + headerLength;\n const end = blocks.byteOffset + blocks.byteLength;\n const inBounds = (offset: number, length: number): boolean => offset >= 0 && length >= 0 && base + offset + length <= end;\n const tables: World.Table[] = [];\n for (const table of header.tables) {\n if (!inBounds(table.entities, table.count * 8)) return { failure: \"This snapshot's blocks are cut short.\" };\n const columns: { [key: symbol]: World.Table.Column } = {};\n for (const column of table.columns) {\n const persisted = components[column.component];\n if (persisted === undefined) return { failure: \"This snapshot names a component it doesn't hold.\" };\n if (column.kind === \"value\") columns[persisted.key] = { kind: \"value\", value: fromJson(persisted.schema, column.value) };\n else if (!inBounds(column.offset, column.length)) return { failure: \"This snapshot's blocks are cut short.\" };\n else if (column.kind === \"bytes\") columns[persisted.key] = { kind: \"bytes\", bytes: new Uint8Array(blocks.buffer, base + column.offset, column.length), stride: column.stride };\n else {\n let values: unknown;\n try {\n values = JSON.parse(decoder.decode(new Uint8Array(blocks.buffer, base + column.offset, column.length)));\n }\n catch {\n return { failure: `Component \"${persisted.name}\"'s values in this snapshot aren't JSON.` };\n }\n if (!Array.isArray(values)) return { failure: `Component \"${persisted.name}\"'s values in this snapshot aren't a list.` };\n columns[persisted.key] = { kind: \"values\", values: values.map(value => fromJson(persisted.schema, value)) };\n }\n }\n tables.push({ count: table.count, entities: new Float64Array(blocks.buffer, base + table.entities, table.count), columns });\n }\n return { components, tables, resources };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { Component, Resource, World } from \"@adobe/ecs\";\nimport type { Persisted } from \"./program.js\";\n\n/** Where a snapshot holds what it holds, a key only this package uses. */\nexport const contents: unique symbol = Symbol(\"snapshot contents\");\n\n/**\n * The persisted part of some of a world's entities: of each archetype, its entities and a column of each persisted\n * component it holds, keyed by this program's keys; and the values of persisted resources.\n */\nexport type Contents = {\n /** Each persisted component a table of it may hold. */\n readonly components: readonly Persisted<Component.Key>[];\n readonly tables: readonly World.Table[];\n readonly resources: readonly { readonly persisted: Persisted<Resource.Key>; readonly value: unknown }[];\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { Failure } from \"./failure.js\";\nimport { decodeContents } from \"./format/format.js\";\nimport { contents } from \"./program/contents.js\";\nimport type { Snapshot } from \"./snapshot.js\";\n\n/**\n * The snapshot `bytes` holds, its columns of bytes viewed where they are in `bytes`, copied once only when `bytes`\n * doesn't start at a multiple of 8; or why it can't be read: it isn't one, or names a component or resource this\n * program doesn't persist, or persists with another schema.\n */\nexport const decode = (bytes: Uint8Array): Snapshot | Failure => {\n const decoded = decodeContents(bytes);\n return \"failure\" in decoded ? decoded : { [contents]: decoded };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport { encodeContents } from \"./format/format.js\";\nimport { contents } from \"./program/contents.js\";\nimport type { Snapshot } from \"./snapshot.js\";\n\n/**\n * A snapshot as bytes, little-endian: a header naming each persisted component and resource with its schema, then\n * 8-byte-aligned blocks of each table's entities and columns, its bytes columns copied whole.\n */\nexport const encode = (snapshot: Snapshot): Uint8Array => encodeContents(snapshot[contents]);\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport { Component, System, type Entity, type Resource, type World } from \"@adobe/ecs\";\nimport type { Failure } from \"../failure.js\";\nimport type { Contents } from \"../program/contents.js\";\nimport type { Program } from \"../program/program.js\";\n\n/**\n * Which entities a snapshot takes: those matching `with` and `not`, every one holding a persisted component by\n * default; or `root` and every entity it owns, directly or through others, by persisted owner references.\n */\nexport type Selection =\n | { readonly with?: readonly Component.Key[]; readonly not?: readonly Component.Key[]; readonly resources?: boolean }\n | { readonly root: Entity };\n\n/** Which tables a snapshot reads: those of its persisted components, matching `with` and `not`, and its resources. */\ntype Reading = {\n readonly components: readonly Component.Key[];\n readonly with?: readonly Component.Key[];\n readonly not?: readonly Component.Key[];\n readonly resources?: readonly Resource.Key[];\n};\n\n/** A readonly system reading the tables `declaration` names. */\nconst tablesSystemOf = (declaration: Reading) =>\n System.create({ readonly: true, tables: declaration }, ({ tables }) => tables);\n\n/** The rows of `table` whose entities `keep` takes, as a table of their own. */\nconst rowsOf = (table: World.Table, keep: (entity: number) => boolean): World.Table | null => {\n const rows: number[] = [];\n for (let row = 0; row < table.count; row++) if (keep(table.entities[row])) rows.push(row);\n if (rows.length === table.count) return table;\n if (rows.length === 0) return null;\n const columns: { [key: symbol]: World.Table.Column } = {};\n for (const key of Object.getOwnPropertySymbols(table.columns)) {\n const column: World.Table.Column | undefined = Reflect.get(table.columns, key);\n if (column === undefined) continue;\n if (column.kind === \"value\") columns[key] = column;\n else if (column.kind === \"values\") columns[key] = { kind: \"values\", values: rows.map(row => column.values[row]) };\n else {\n const { stride } = column;\n const bytes = new Uint8Array(rows.length * stride);\n rows.forEach((row, at) => bytes.set(column.bytes.subarray(row * stride, (row + 1) * stride), at * stride));\n columns[key] = { kind: \"bytes\", bytes, stride };\n }\n }\n return { count: rows.length, entities: Float64Array.from(rows, row => table.entities[row]), columns };\n};\n\n/** The entities `root` owns, itself included, through the owner references among `owners` in the tables. */\nconst subtreeOf = (tables: readonly World.Table[], owners: readonly Component.Key[], root: Entity): ReadonlySet<number> => {\n const children = new Map<number, number[]>();\n for (const table of tables) {\n for (const reference of owners) {\n const key: symbol = reference;\n const column: World.Table.Column | undefined = Reflect.get(table.columns, key);\n if (column?.kind !== \"bytes\") continue;\n const view = new DataView(column.bytes.buffer, column.bytes.byteOffset, column.bytes.byteLength);\n for (let row = 0; row < table.count; row++) {\n const owner = view.getFloat64(row * column.stride, true);\n const list = children.get(owner);\n if (list === undefined) children.set(owner, [table.entities[row]]);\n else list.push(table.entities[row]);\n }\n }\n }\n const taken = new Set<number>([root]);\n const waiting: number[] = [root];\n for (let next = waiting.pop(); next !== undefined; next = waiting.pop()) {\n for (const child of children.get(next) ?? []) {\n if (taken.has(child)) continue;\n taken.add(child);\n waiting.push(child);\n }\n }\n return taken;\n};\n\n/** The persisted part of the entities `selection` takes from `world`, on its thread, or why it can't be read. */\nexport const select = async (world: World, program: Program, selection: Selection): Promise<Contents | Failure> => {\n const components = [...program.components.values()];\n const keys = components.map(persisted => persisted.key);\n const subtree = \"root\" in selection;\n const resources = subtree || selection.resources === false ? [] : [...program.resources.values()];\n const declaration: Reading = subtree\n ? { components: keys }\n : { components: keys, with: selection.with ?? [], not: selection.not ?? [], resources: resources.map(persisted => persisted.key) };\n const read = await world.run(tablesSystemOf(declaration)).catch((error: unknown) => ({ failure: error instanceof Error ? error.message : String(error) }));\n if (\"failure\" in read) return read;\n let tables = read.archetypes;\n if (subtree) {\n const owners = keys.filter(key => Component.declarations().get(key)?.reference === \"owner\");\n const taken = subtreeOf(tables, owners, selection.root);\n tables = tables.flatMap(table => rowsOf(table, entity => taken.has(entity)) ?? []);\n }\n return { components, tables, resources: resources.map(persisted => ({ persisted, value: read.resources[persisted.key] })) };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { World } from \"@adobe/ecs\";\nimport type { Failure } from \"./failure.js\";\nimport { contents, type Contents } from \"./program/contents.js\";\nimport { programOf } from \"./program/program.js\";\nimport { select, type Selection } from \"./selection/selection.js\";\n\n/** The persisted part of some of a world's entities and its persisted resources, which `encode` and `spawn` take. */\nexport type Snapshot = { readonly failure?: undefined; readonly [contents]: Contents };\n\n/**\n * A snapshot of some of `world`'s entities, read on its thread: by default every entity holding a persisted\n * component, and the persisted resources; with `with` and `not`, those a query matches, and the resources unless\n * `resources` is false; with `root`, it and every entity it owns, through persisted owner references, without\n * resources. Fails, as data, when the program's persisted keys can't be saved or the world can't be read.\n */\nexport const snapshot = async (input: { readonly world: World } & Selection): Promise<Snapshot | Failure> => {\n const program = programOf();\n if (typeof program === \"string\") return { failure: program };\n const taken = await select(input.world, program, input);\n return \"failure\" in taken ? taken : { [contents]: taken };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport { System, type Entity, type World } from \"@adobe/ecs\";\nimport type { Failure } from \"./failure.js\";\nimport { contents } from \"./program/contents.js\";\nimport type { Snapshot } from \"./snapshot.js\";\n\n/**\n * What `spawn` made: how many of the snapshot's entities it didn't spawn, those owned by an entity outside it; and,\n * unless asked not to, `entities`, each of the snapshot's entities to the one spawned for it, or null for one it\n * didn't spawn.\n */\nexport type Spawned<Entities extends boolean = true> = { readonly failure?: undefined; readonly dropped: number }\n & (Entities extends false ? {} : { readonly entities: ReadonlyMap<Entity, Entity | null> });\n\nconst isTables = (value: unknown): value is readonly World.Table[] => Array.isArray(value) && value.every(table =>\n typeof table === \"object\" && table !== null && typeof Reflect.get(table, \"count\") === \"number\" && Reflect.get(table, \"entities\") instanceof Float64Array\n && typeof Reflect.get(table, \"columns\") === \"object\");\n\nconst isKeyed = (value: unknown): value is { readonly [key: symbol]: unknown } => typeof value === \"object\" && value !== null;\n\n/** Spawns its input's tables and sets its resources, a snapshot's, rewriting their entities. */\nconst spawnTables = System.create({}, ({ commands }, input: { readonly tables: unknown; readonly resources: unknown }) => {\n if (!isTables(input.tables) || !isKeyed(input.resources)) throw new Error(\"A snapshot holds tables and resources.\");\n return commands.spawnTables(input.tables, input.resources);\n});\n\n/**\n * Spawns an entity in `world` for each of the snapshot's, with its persisted components, and sets the persisted\n * resources, on the world's thread, as the world's references would leave them had the entities outside the snapshot\n * despawned: each entity value naming one of the snapshot's names the one spawned for it; an entity owned, directly\n * or through others, by an entity outside the snapshot isn't spawned, which the result counts; a weak reference to\n * one is left out; and any other entity value naming one is `Entity.none`. With `entities: false`, the result leaves\n * out its map of entities, which costs about 85 ms and 50 MB at a million. Fails, as data, for a column or\n * resource that doesn't fit, spawning nothing.\n */\nexport function spawn(input: { readonly world: World; readonly snapshot: Snapshot; readonly entities: false }): Promise<Spawned<false> | Failure>;\nexport function spawn(input: { readonly world: World; readonly snapshot: Snapshot; readonly entities?: true }): Promise<Spawned | Failure>;\nexport async function spawn(input: { readonly world: World; readonly snapshot: Snapshot; readonly entities?: boolean }): Promise<Spawned<boolean> | Failure> {\n const { tables, resources } = input.snapshot[contents];\n const values: { [key: symbol]: unknown } = {};\n for (const { persisted, value } of resources) values[persisted.key] = value;\n const spawned = await input.world.run(spawnTables, { tables, resources: values }).catch((error: unknown) => ({ failure: error instanceof Error ? error.message : String(error) }));\n if (\"failure\" in spawned) return spawned;\n if (input.entities === false) return { dropped: spawned.dropped };\n return { dropped: spawned.dropped, entities: entitiesOf(tables, spawned.entities) };\n}\n\n/** Each of the tables' entities to the one spawned for it, or null for one that wasn't. */\nconst entitiesOf = (tables: readonly World.Table[], spawned: readonly Float64Array[]): ReadonlyMap<Entity, Entity | null> => {\n const entities = new Map<Entity, Entity | null>();\n tables.forEach((table, t) => {\n const made = spawned[t];\n for (let row = 0; row < table.count; row++) {\n // A table's entities and `spawnTables`' are entities, `Entity.none`, 0, for a row it didn't spawn.\n const entity = made[row] as Entity;\n entities.set(table.entities[row] as Entity, entity === 0 ? null : entity);\n }\n });\n return entities;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["header"],"sources":["../src/program/program.ts","../src/values/values.ts","../src/format/format.ts","../src/program/contents.ts","../src/decode.ts","../src/encode.ts","../src/selection/selection.ts","../src/snapshot.ts","../src/match/keys.ts","../src/match/match.ts","../src/spawn.ts"],"sourcesContent":["// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport { Component, Resource, type Schema, type Traits } from \"@adobe/ecs\";\n\n/** A key a trait covers, with the name the trait saves it under and its schema. */\nexport type Persisted<K extends symbol = symbol> = { readonly name: string; readonly key: K; readonly schema: Schema };\n\n/** What a trait covers: its components and resources by name. */\nexport type Program = {\n readonly by: symbol;\n readonly components: ReadonlyMap<string, Persisted<Component.Key>>;\n readonly resources: ReadonlyMap<string, Persisted<Resource.Key>>;\n};\n\ntype Keyed = Component.Key | Resource.Key;\n\n/** A trait of an application's own whose value names a key: its key a symbol, its value a string. */\nexport type NamingTrait = { readonly [T in keyof Traits<Keyed>]: T extends symbol ? [Traits<Keyed>[T]] extends [string] ? T : never : never }[keyof Traits<Keyed>];\n\n/**\n * The keys of `declarations` with the trait `by`, by the name it gives each, or why one can't be saved: the trait's\n * value isn't a string, or the key isn't data, is derived, or has a name another has.\n */\nconst namedIn = <K extends symbol>(declarations: ReadonlyMap<K, Component.Declared>, kind: string, by: symbol): ReadonlyMap<string, Persisted<K>> | string => {\n const named = new Map<string, Persisted<K>>();\n for (const [key, declaration] of declarations) {\n const name: unknown = Reflect.get(declaration, by);\n if (name === undefined) continue;\n if (typeof name !== \"string\") return `A ${kind}'s value of the trait isn't a name: ${String(name)}.`;\n if (declaration.type === undefined) return `The ${kind} the trait names \"${name}\" isn't data: it has no schema.`;\n if (declaration.derived === true) return `The ${kind} the trait names \"${name}\" is derived, which its rule derives again once its entity is loaded.`;\n if (named.has(name)) return `The trait names two ${kind}s \"${name}\".`;\n named.set(name, { name, key, schema: declaration.type });\n }\n return named;\n};\n\n/** What the trait `by` covers, as the modules registered so far declare it, or why it can't be saved. */\nexport const programOf = (by: unknown): Program | string => {\n if (typeof by !== \"symbol\") return \"`by` is the key of the trait to save by, a symbol.\";\n const components = namedIn(Component.declarations(), \"component\", by);\n if (typeof components === \"string\") return components;\n const resources = namedIn(Resource.declarations(), \"resource\", by);\n if (typeof resources === \"string\") return resources;\n return { by, components, resources };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { Schema } from \"@adobe/ecs\";\n\n// Values of components stored as values, strings and other data that isn't a fixed layout, as JSON their schema reads\n// back: a map as its entries, a set as its items, numbers JSON lacks as text, and a union's value as its member's\n// index and its JSON.\n\n/** A number JSON can't hold, as text: `NaN`, `Infinity`, `-Infinity` or `-0`. */\nconst numberToJson = (value: number): number | string =>\n Number.isNaN(value) ? \"NaN\" : value === Infinity ? \"Infinity\" : value === -Infinity ? \"-Infinity\" : Object.is(value, -0) ? \"-0\" : value;\n\nconst numberFromJson = (json: unknown): unknown => {\n if (typeof json !== \"string\") return json;\n switch (json) {\n case \"NaN\": return NaN;\n case \"Infinity\": return Infinity;\n case \"-Infinity\": return -Infinity;\n case \"-0\": return -0;\n default: return json;\n }\n};\n\n/** Whether a value is of the JSON kind a schema's values are, to pick a union's member. */\nconst fits = (schema: Schema, value: unknown): boolean => {\n if (\"const\" in schema) return Object.is(schema.const, value) || JSON.stringify(schema.const) === JSON.stringify(value);\n if (\"anyOf\" in schema) return schema.anyOf.some(member => fits(member, value));\n if (\"allOf\" in schema) return schema.allOf.every(member => fits(member, value));\n if (!(\"type\" in schema)) return true;\n switch (schema.type) {\n case \"number\": case \"integer\": return typeof value === \"number\";\n case \"string\": return typeof value === \"string\";\n case \"boolean\": return typeof value === \"boolean\";\n case \"null\": return value === null;\n case \"array\": return Array.isArray(value);\n case \"set\": return value instanceof Set;\n case \"map\": return value instanceof Map;\n case \"object\": return typeof value === \"object\" && value !== null && !Array.isArray(value) && !(value instanceof Set) && !(value instanceof Map);\n default: return true;\n }\n};\n\n/** A value of `schema` as JSON. */\nexport const toJson = (schema: Schema, value: unknown): unknown => {\n if (\"anyOf\" in schema) {\n const index = schema.anyOf.findIndex(candidate => fits(candidate, value));\n const member = schema.anyOf[index];\n return member === undefined ? [-1, value] : [index, toJson(member, value)];\n }\n if (typeof value === \"number\") return numberToJson(value);\n if (!(\"type\" in schema)) return value;\n switch (schema.type) {\n case \"array\": return Array.isArray(value) ? value.map(item => toJson(schema.items, item)) : value;\n case \"set\": return value instanceof Set ? Array.from(value, item => toJson(schema.items, item)) : value;\n case \"map\": return value instanceof Map ? Array.from(value, ([key, item]) => [toJson(schema.keys, key), toJson(schema.values, item)]) : value;\n case \"object\": {\n if (typeof value !== \"object\" || value === null) return value;\n const json: { [name: string]: unknown } = {};\n for (const name of Object.keys(value)) {\n const field = schema.properties[name];\n const item: unknown = Reflect.get(value, name);\n if (item !== undefined) json[name] = field === undefined ? item : toJson(field, item);\n }\n return json;\n }\n default: return value;\n }\n};\n\n/** A value of `schema` read back from its JSON. */\nexport const fromJson = (schema: Schema, json: unknown): unknown => {\n if (\"anyOf\" in schema) {\n if (!Array.isArray(json) || typeof json[0] !== \"number\") return json;\n const member = schema.anyOf[json[0]];\n return member === undefined ? json[1] : fromJson(member, json[1]);\n }\n if (\"const\" in schema) return schema.const;\n if (!(\"type\" in schema)) return json;\n switch (schema.type) {\n case \"number\": case \"integer\": return numberFromJson(json);\n case \"array\": return Array.isArray(json) ? json.map(item => fromJson(schema.items, item)) : json;\n case \"set\": return Array.isArray(json) ? new Set(json.map(item => fromJson(schema.items, item))) : json;\n case \"map\": return Array.isArray(json)\n ? new Map(json.map(entry => Array.isArray(entry) ? [fromJson(schema.keys, entry[0]), fromJson(schema.values, entry[1])] : [entry, undefined]))\n : json;\n case \"object\": {\n if (typeof json !== \"object\" || json === null || Array.isArray(json)) return json;\n const value: { [name: string]: unknown } = {};\n for (const name of Object.keys(json)) {\n const field = schema.properties[name];\n const item: unknown = Reflect.get(json, name);\n value[name] = field === undefined ? item : fromJson(field, item);\n }\n return value;\n }\n default: return json;\n }\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { Component, Resource, Schema, World } from \"@adobe/ecs\";\nimport type { Failure } from \"../failure.js\";\nimport type { Contents } from \"../program/contents.js\";\nimport { programOf, type Persisted } from \"../program/program.js\";\nimport { fromJson, toJson } from \"../values/values.js\";\n\n/**\n * The binary form of a snapshot, little-endian:\n * - 16 bytes: `ECSP`, the format's version as a u32, the header's length in bytes as a u32, and 4 bytes of zeros;\n * - the header, JSON text padded with spaces to a multiple of 8 bytes: each component and resource by name with its\n * schema, and each table's count and where its entities and columns are;\n * - blocks, each starting at a multiple of 8 bytes: a table's entities as f64s, a column's bytes as its components'\n * layout holds them, and a column of values as JSON text.\n * A loader views the blocks of bytes where they are, in the buffer it's given.\n */\n\nconst magic = [0x45, 0x43, 0x53, 0x50];\nconst version = 1;\nconst prefix = 16;\n\n/** Where a column's data is. */\ntype ColumnJson =\n | { readonly kind: \"bytes\"; readonly offset: number; readonly length: number; readonly stride: number }\n | { readonly kind: \"values\"; readonly offset: number; readonly length: number }\n | { readonly kind: \"value\"; readonly value: unknown };\n\ntype Header = {\n readonly components: readonly { readonly name: string; readonly schema: Schema }[];\n readonly resources: readonly { readonly name: string; readonly schema: Schema; readonly value: unknown }[];\n readonly tables: readonly { readonly count: number; readonly entities: number; readonly columns: readonly ({ readonly component: number } & ColumnJson)[] }[];\n};\n\nconst aligned = (offset: number): number => Math.ceil(offset / 8) * 8;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** A snapshot's contents as bytes. */\nexport const encodeContents = (snapshot: Contents): Uint8Array => {\n const blocks: Uint8Array[] = [];\n let offset = 0;\n /** Places a block, returning its offset from the first block. */\n const place = (bytes: Uint8Array): number => {\n const at = offset;\n blocks.push(bytes);\n offset = aligned(offset + bytes.byteLength);\n return at;\n };\n const indexOf = new Map(snapshot.components.map((persisted, index) => [persisted.key, index]));\n const tables = snapshot.tables.map(table => {\n const entities = place(new Uint8Array(table.entities.buffer, table.entities.byteOffset, table.entities.byteLength));\n const columns = snapshot.components.flatMap((persisted): ({ readonly component: number } & ColumnJson)[] => {\n const key: symbol = persisted.key;\n const column: World.Table.Column | undefined = Reflect.get(table.columns, key);\n const component = indexOf.get(persisted.key) ?? -1;\n if (column === undefined) return [];\n if (column.kind === \"bytes\") return [{ component, kind: \"bytes\", offset: place(column.bytes), length: column.bytes.byteLength, stride: column.stride }];\n if (column.kind === \"value\") return [{ component, kind: \"value\", value: toJson(persisted.schema, column.value) }];\n const text = encoder.encode(JSON.stringify(column.values.map(value => toJson(persisted.schema, value))));\n return [{ component, kind: \"values\", offset: place(text), length: text.byteLength }];\n });\n return { count: table.count, entities, columns };\n });\n const header: Header = {\n components: snapshot.components.map(({ name, schema }) => ({ name, schema })),\n resources: snapshot.resources.map(({ persisted, value }) => ({ name: persisted.name, schema: persisted.schema, value: toJson(persisted.schema, value) })),\n tables,\n };\n const text = encoder.encode(JSON.stringify(header));\n const headerLength = aligned(text.byteLength);\n const bytes = new Uint8Array(prefix + headerLength + offset);\n bytes.set(magic, 0);\n const view = new DataView(bytes.buffer);\n view.setUint32(4, version, true);\n view.setUint32(8, headerLength, true);\n bytes.set(text, prefix);\n bytes.fill(0x20, prefix + text.byteLength, prefix + headerLength);\n let at = prefix + headerLength;\n for (const block of blocks) {\n bytes.set(block, at);\n at = aligned(at + block.byteLength);\n }\n return bytes;\n};\n\nconst isObject = (value: unknown): value is object => typeof value === \"object\" && value !== null;\n\n/** The header's JSON, checked enough to read it without casts, or null. */\nconst headerOf = (json: unknown): Header | null => {\n if (!isObject(json)) return null;\n const components: unknown = Reflect.get(json, \"components\");\n const resources: unknown = Reflect.get(json, \"resources\");\n const tables: unknown = Reflect.get(json, \"tables\");\n if (!Array.isArray(components) || !Array.isArray(resources) || !Array.isArray(tables)) return null;\n const named = (item: unknown): boolean => isObject(item) && typeof Reflect.get(item, \"name\") === \"string\" && isObject(Reflect.get(item, \"schema\"));\n if (!components.every(named) || !resources.every(named)) return null;\n const isColumn = (item: unknown): boolean => {\n if (!isObject(item) || typeof Reflect.get(item, \"component\") !== \"number\") return false;\n const kind: unknown = Reflect.get(item, \"kind\");\n if (kind === \"value\") return true;\n const numbers = kind === \"bytes\" ? [\"offset\", \"length\", \"stride\"] : kind === \"values\" ? [\"offset\", \"length\"] : null;\n return numbers !== null && numbers.every(name => typeof Reflect.get(item, name) === \"number\");\n };\n const isTable = (item: unknown): boolean => {\n if (!isObject(item) || typeof Reflect.get(item, \"count\") !== \"number\" || typeof Reflect.get(item, \"entities\") !== \"number\") return false;\n const columns: unknown = Reflect.get(item, \"columns\");\n return Array.isArray(columns) && columns.every(isColumn);\n };\n if (!tables.every(isTable)) return null;\n // Checked above: each item has the fields the header's type names, of their types.\n const header = json as Header;\n return header;\n};\n\n/** Whether two schemas are the same, by their JSON in their properties' order, which places a struct's fields. */\nconst sameSchema = (a: Schema, b: Schema): boolean => JSON.stringify(a) === JSON.stringify(b);\n\n/**\n * The snapshot contents `bytes` holds, its components and resources by the names the trait `by` gives them, its blocks\n * of bytes viewed where they are; or why it can't be read: it isn't one, or names what the trait doesn't cover, or\n * covers with another schema.\n */\nexport const decodeContents = (bytes: Uint8Array, by: unknown): Contents | Failure => {\n if (bytes.byteLength < prefix || magic.some((byte, at) => bytes[at] !== byte)) return { failure: \"These bytes aren't a snapshot.\" };\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const found = view.getUint32(4, true);\n if (found !== version) return { failure: `This snapshot's format is version ${found}; this package reads version ${version}.` };\n const headerLength = view.getUint32(8, true);\n let json: unknown;\n try {\n json = JSON.parse(decoder.decode(bytes.subarray(prefix, prefix + headerLength)));\n }\n catch {\n return { failure: \"This snapshot's header isn't JSON.\" };\n }\n const header = headerOf(json);\n if (header === null) return { failure: \"This snapshot's header isn't one this package writes.\" };\n const program = programOf(by);\n if (typeof program === \"string\") return { failure: program };\n const components: Persisted<Component.Key>[] = [];\n for (const { name, schema } of header.components) {\n const persisted = program.components.get(name);\n if (persisted === undefined) return { failure: `This snapshot holds component \"${name}\", which the trait doesn't cover.` };\n if (!sameSchema(persisted.schema, schema)) return { failure: `Component \"${name}\" has another schema here than in this snapshot.` };\n components.push(persisted);\n }\n const resources: { persisted: Persisted<Resource.Key>; value: unknown }[] = [];\n for (const { name, schema, value } of header.resources) {\n const persisted = program.resources.get(name);\n if (persisted === undefined) return { failure: `This snapshot holds resource \"${name}\", which the trait doesn't cover.` };\n if (!sameSchema(persisted.schema, schema)) return { failure: `Resource \"${name}\" has another schema here than in this snapshot.` };\n resources.push({ persisted, value: fromJson(schema, value) });\n }\n // Blocks are 8-byte aligned in the buffer the format was written to; one given at another offset is copied once.\n const blocks = (bytes.byteOffset + prefix + headerLength) % 8 === 0 ? bytes : bytes.slice();\n const base = blocks.byteOffset + prefix + headerLength;\n const end = blocks.byteOffset + blocks.byteLength;\n const inBounds = (offset: number, length: number): boolean => offset >= 0 && length >= 0 && base + offset + length <= end;\n const tables: World.Table[] = [];\n for (const table of header.tables) {\n if (!inBounds(table.entities, table.count * 8)) return { failure: \"This snapshot's blocks are cut short.\" };\n const columns: { [key: symbol]: World.Table.Column } = {};\n for (const column of table.columns) {\n const persisted = components[column.component];\n if (persisted === undefined) return { failure: \"This snapshot names a component it doesn't hold.\" };\n if (column.kind === \"value\") columns[persisted.key] = { kind: \"value\", value: fromJson(persisted.schema, column.value) };\n else if (!inBounds(column.offset, column.length)) return { failure: \"This snapshot's blocks are cut short.\" };\n else if (column.kind === \"bytes\") columns[persisted.key] = { kind: \"bytes\", bytes: new Uint8Array(blocks.buffer, base + column.offset, column.length), stride: column.stride };\n else {\n let values: unknown;\n try {\n values = JSON.parse(decoder.decode(new Uint8Array(blocks.buffer, base + column.offset, column.length)));\n }\n catch {\n return { failure: `Component \"${persisted.name}\"'s values in this snapshot aren't JSON.` };\n }\n if (!Array.isArray(values)) return { failure: `Component \"${persisted.name}\"'s values in this snapshot aren't a list.` };\n columns[persisted.key] = { kind: \"values\", values: values.map(value => fromJson(persisted.schema, value)) };\n }\n }\n tables.push({ count: table.count, entities: new Float64Array(blocks.buffer, base + table.entities, table.count), columns });\n }\n return { by: program.by, components, tables, resources };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { Component, Resource, World } from \"@adobe/ecs\";\nimport type { Persisted } from \"./program.js\";\n\n/** Where a snapshot holds what it holds, a key only this package uses. */\nexport const contents: unique symbol = Symbol(\"snapshot contents\");\n\n/**\n * What a trait covers of some of a world's entities: of each archetype, its entities and a column of each component\n * the trait covers that it holds, keyed by this program's keys; and the values of the resources it covers.\n */\nexport type Contents = {\n /** The key of the trait it was taken or read by. */\n readonly by: symbol;\n /** Each component the trait covers that a table of it may hold. */\n readonly components: readonly Persisted<Component.Key>[];\n readonly tables: readonly World.Table[];\n readonly resources: readonly { readonly persisted: Persisted<Resource.Key>; readonly value: unknown }[];\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { Failure } from \"./failure.js\";\nimport { decodeContents } from \"./format/format.js\";\nimport { contents } from \"./program/contents.js\";\nimport type { NamingTrait } from \"./program/program.js\";\nimport type { Snapshot } from \"./snapshot.js\";\n\n/**\n * The snapshot `bytes` holds, read by the trait `by`, its columns of bytes viewed where they are in `bytes`, copied once\n * only when `bytes` doesn't start at a multiple of 8; or why it can't be read: it isn't one, `by` isn't a symbol, or it\n * names a component or resource the trait doesn't cover, or covers with another schema.\n */\nexport const decode = (bytes: Uint8Array, options: { readonly by: NamingTrait }): Snapshot | Failure => {\n const decoded = decodeContents(bytes, options.by);\n return \"failure\" in decoded ? decoded : { [contents]: decoded };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport { encodeContents } from \"./format/format.js\";\nimport { contents } from \"./program/contents.js\";\nimport type { Snapshot } from \"./snapshot.js\";\n\n/**\n * A snapshot as bytes, little-endian: a header naming each component and resource it holds by its trait's name for it,\n * with its schema, then 8-byte-aligned blocks of each table's entities and columns, its bytes columns copied whole.\n */\nexport const encode = (snapshot: Snapshot): Uint8Array => encodeContents(snapshot[contents]);\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport { Component, System, type Entity, type Resource, type World } from \"@adobe/ecs\";\nimport type { Failure } from \"../failure.js\";\nimport type { Contents } from \"../program/contents.js\";\nimport type { Program } from \"../program/program.js\";\n\n/** Values of shared components, which archetypes hold one of each: `where` as a system's of values. */\ntype Where = { readonly [K in Component.With<\"shared\">]?: Component<K> };\n\n/**\n * Which entities a snapshot takes: those matching `with`, `not` and `where`, every one holding a component its trait\n * covers by default; or `root` and every entity it owns, directly or through others, by owner references its trait\n * covers.\n */\nexport type Selection =\n | { readonly with?: readonly Component.Key[]; readonly not?: readonly Component.Key[]; readonly where?: Where; readonly resources?: boolean }\n | { readonly root: Entity };\n\n/** Which tables a snapshot reads: those of the components its trait covers, matching `with`, `not` and `where`, and its resources. */\ntype Reading = {\n readonly components: readonly Component.Key[];\n readonly with?: readonly Component.Key[];\n readonly not?: readonly Component.Key[];\n readonly where?: Where;\n readonly resources?: readonly Resource.Key[];\n};\n\n/** A readonly system reading the tables `declaration` names. */\nconst tablesSystemOf = (declaration: Reading) =>\n System.create({ readonly: true, tables: declaration }, ({ tables }) => tables);\n\n/** The rows of `table` whose entities `keep` takes, as a table of their own. */\nconst rowsOf = (table: World.Table, keep: (entity: number) => boolean): World.Table | null => {\n const rows: number[] = [];\n for (let row = 0; row < table.count; row++) if (keep(table.entities[row])) rows.push(row);\n if (rows.length === table.count) return table;\n if (rows.length === 0) return null;\n const columns: { [key: symbol]: World.Table.Column } = {};\n for (const key of Object.getOwnPropertySymbols(table.columns)) {\n const column: World.Table.Column | undefined = Reflect.get(table.columns, key);\n if (column === undefined) continue;\n if (column.kind === \"value\") columns[key] = column;\n else if (column.kind === \"values\") columns[key] = { kind: \"values\", values: rows.map(row => column.values[row]) };\n else {\n const { stride } = column;\n const bytes = new Uint8Array(rows.length * stride);\n rows.forEach((row, at) => bytes.set(column.bytes.subarray(row * stride, (row + 1) * stride), at * stride));\n columns[key] = { kind: \"bytes\", bytes, stride };\n }\n }\n return { count: rows.length, entities: Float64Array.from(rows, row => table.entities[row]), columns };\n};\n\n/** The entities `root` owns, itself included, through the owner references among `owners` in the tables. */\nconst subtreeOf = (tables: readonly World.Table[], owners: readonly Component.Key[], root: Entity): ReadonlySet<number> => {\n const children = new Map<number, number[]>();\n for (const table of tables) {\n for (const reference of owners) {\n const key: symbol = reference;\n const column: World.Table.Column | undefined = Reflect.get(table.columns, key);\n if (column?.kind !== \"bytes\") continue;\n const view = new DataView(column.bytes.buffer, column.bytes.byteOffset, column.bytes.byteLength);\n for (let row = 0; row < table.count; row++) {\n const owner = view.getFloat64(row * column.stride, true);\n const list = children.get(owner);\n if (list === undefined) children.set(owner, [table.entities[row]]);\n else list.push(table.entities[row]);\n }\n }\n }\n const taken = new Set<number>([root]);\n const waiting: number[] = [root];\n for (let next = waiting.pop(); next !== undefined; next = waiting.pop()) {\n for (const child of children.get(next) ?? []) {\n if (taken.has(child)) continue;\n taken.add(child);\n waiting.push(child);\n }\n }\n return taken;\n};\n\n/** What `program`'s trait covers of the entities `selection` takes from `world`, on its thread, or why it can't be read. */\nexport const select = async (world: World, program: Program, selection: Selection): Promise<Contents | Failure> => {\n const components = [...program.components.values()];\n const keys = components.map(persisted => persisted.key);\n const subtree = \"root\" in selection;\n const resources = subtree || selection.resources === false ? [] : [...program.resources.values()];\n const declaration: Reading = subtree\n ? { components: keys }\n : { components: keys, with: selection.with ?? [], not: selection.not ?? [], where: selection.where ?? {}, resources: resources.map(persisted => persisted.key) };\n const failed = (error: unknown): Failure => ({ failure: error instanceof Error ? error.message : String(error) });\n let system: ReturnType<typeof tablesSystemOf>;\n try {\n system = tablesSystemOf(declaration);\n }\n catch (error) {\n // A `where` naming what isn't a shared component, or giving one a value not of its type.\n return failed(error);\n }\n const read = await world.run(system).catch(failed);\n if (\"failure\" in read) return read;\n let tables = read.archetypes;\n if (subtree) {\n const owners = keys.filter(key => Component.declarations().get(key)?.reference === \"owner\");\n const taken = subtreeOf(tables, owners, selection.root);\n tables = tables.flatMap(table => rowsOf(table, entity => taken.has(entity)) ?? []);\n }\n return { by: program.by, components, tables, resources: resources.map(persisted => ({ persisted, value: read.resources[persisted.key] })) };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport type { World } from \"@adobe/ecs\";\nimport type { Failure } from \"./failure.js\";\nimport { contents, type Contents } from \"./program/contents.js\";\nimport { programOf, type NamingTrait } from \"./program/program.js\";\nimport { select, type Selection } from \"./selection/selection.js\";\n\n/**\n * What a trait covers of some of a world's entities and of its resources, which `encode` and `spawn` take, and the\n * trait it was taken or read by.\n */\nexport type Snapshot = { readonly failure?: undefined; readonly [contents]: Contents };\n\n/**\n * A snapshot of some of `world`'s entities, read on its thread, by the trait `by`: a trait of the application's own,\n * whose value on a component's or resource's declaration is the name it's saved under. Of each entity it takes, it\n * takes the components the trait covers. By default it takes every entity holding one of them, and the resources the\n * trait covers; with `with` and `not`, those a query matches, and the resources unless `resources` is false; with\n * `where`, those of the archetypes holding its values of shared components, as a system's `where` of values keeps it to\n * them, read whole, so a snapshot of one value's entities costs what they hold, not the world; with `root`, it and every\n * entity it owns, through owner references the trait covers, without resources. Fails, as data, when `by` isn't a\n * symbol, what the trait covers can't be saved, `where` names what isn't a shared component or gives one a value not\n * of its type, or the world can't be read.\n */\nexport const snapshot = async (input: { readonly world: World; readonly by: NamingTrait } & Selection): Promise<Snapshot | Failure> => {\n const program = programOf(input.by);\n if (typeof program === \"string\") return { failure: program };\n const taken = await select(input.world, program, input);\n return \"failure\" in taken ? taken : { [contents]: taken };\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\n\n/** A table's key, a column of bytes for each part, `stride` bytes per row: a row's key is its bytes in each. */\nexport type KeyColumns = readonly { readonly bytes: Uint8Array; readonly stride: number }[];\n\n/**\n * Keys of rows of tables, each with a number: an open-addressing table of slots, linear probing, at most half full,\n * each slot two words, a key's hash and its entry plus one, or 0 for none, so a probe reads its slots until a hash\n * matches. An entry is a row of the tables it was added from, whose bytes are its key.\n */\nexport type KeyTable = {\n readonly mask: number;\n readonly slots: Int32Array;\n readonly tables: Int32Array;\n readonly rows: Int32Array;\n readonly values: Float64Array;\n readonly columns: KeyColumns[];\n count: number;\n};\n\n/** A table for up to `capacity` keys. */\nexport const createKeyTable = (capacity: number): KeyTable => {\n let size = 16;\n while (size < 2 * capacity) size *= 2;\n return {\n mask: size - 1,\n slots: new Int32Array(2 * size),\n tables: new Int32Array(capacity),\n rows: new Int32Array(capacity),\n values: new Float64Array(capacity),\n columns: [],\n count: 0,\n };\n};\n\n/** FNV-1a over a row's bytes in each column, then mixed so its low bits, which pick a slot, depend on all of them. */\nconst hashOf = (columns: KeyColumns, row: number): number => {\n let hash = 0x811c9dc5;\n for (let c = 0; c < columns.length; c++) {\n const { bytes, stride } = columns[c];\n for (let at = row * stride, end = at + stride; at < end; at++) hash = Math.imul(hash ^ bytes[at], 0x01000193);\n }\n hash ^= hash >>> 16;\n hash = Math.imul(hash, 0x85ebca6b);\n hash ^= hash >>> 13;\n hash = Math.imul(hash, 0xc2b2ae35);\n return hash ^ (hash >>> 16);\n};\n\nconst equalAt = (a: KeyColumns, rowA: number, b: KeyColumns, rowB: number): boolean => {\n if (a.length !== b.length) return false;\n for (let c = 0; c < a.length; c++) {\n const { bytes: left, stride } = a[c];\n const { bytes: right, stride: other } = b[c];\n if (stride !== other) return false;\n for (let k = 0, i = rowA * stride, j = rowB * stride; k < stride; k++) if (left[i + k] !== right[j + k]) return false;\n }\n return true;\n};\n\n/** The slot holding `row`'s key, or the empty one where it would go. */\nconst slotOf = (table: KeyTable, columns: KeyColumns, row: number, hash: number): number => {\n const { mask, slots, tables, rows } = table;\n for (let slot = hash & mask; ; slot = (slot + 1) & mask) {\n const entry = slots[2 * slot + 1] - 1;\n if (entry < 0) return slot;\n if (slots[2 * slot] === hash && equalAt(table.columns[tables[entry]], rows[entry], columns, row)) return slot;\n }\n};\n\n/**\n * Begins adding the rows of a table of `columns`; `addKey` then takes rows of it. Each table's columns are kept, since\n * its entries' keys are their rows' bytes.\n */\nexport const addKeyColumns = (table: KeyTable, columns: KeyColumns): void => {\n table.columns.push(columns);\n};\n\n/**\n * Adds row `row` of the table `addKeyColumns` began last, with `value`, unless a key equal to its is there: whether\n * it was added. Takes at most the capacity the table was created with.\n */\nexport const addKey = (table: KeyTable, row: number, value: number): boolean => {\n const at = table.columns.length - 1;\n const columns = table.columns[at];\n const hash = hashOf(columns, row);\n const slot = slotOf(table, columns, row, hash);\n if (table.slots[2 * slot + 1] !== 0) return false;\n const entry = table.count++;\n table.slots[2 * slot] = hash;\n table.slots[2 * slot + 1] = entry + 1;\n table.tables[entry] = at;\n table.rows[entry] = row;\n table.values[entry] = value;\n return true;\n};\n\n/**\n * The entry whose key is equal to row `row`'s of `columns`, or -1 for none. `guess` is an entry to compare first, such\n * as the one after the entry found for the row before: rows in the order they were added are found without hashing.\n */\nexport const findKey = (table: KeyTable, columns: KeyColumns, row: number, guess: number): number => {\n if (guess >= 0 && guess < table.count && equalAt(table.columns[table.tables[guess]], table.rows[guess], columns, row)) return guess;\n return table.slots[2 * slotOf(table, columns, row, hashOf(columns, row)) + 1] - 1;\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport { Index, System, type Component, type Schema, type World } from \"@adobe/ecs\";\nimport type { Failure } from \"../failure.js\";\nimport type { Contents } from \"../program/contents.js\";\nimport { toJson } from \"../values/values.js\";\nimport { addKey, addKeyColumns, createKeyTable, findKey, type KeyColumns } from \"./keys.js\";\n\n/** An index whose entities are the same in two worlds where their keys are: a unique one. */\nexport type Matching = Index<{ readonly unique: true }>;\n\n/** What a match reads of a world: the tables of the entities an index holds, with its key's columns. */\nconst keysSystemOf = (declaration: Index.Declared) => System.create({\n readonly: true,\n tables: { components: declaration.key, with: [...declaration.key, ...declaration.components.with ?? []], not: declaration.components.not ?? [] },\n}, ({ tables }) => tables.archetypes);\n\n/** A part of an index's key: its component, and that component's schema as the snapshot holds it. */\ntype Part = { readonly key: Component.Key; readonly schema: Schema };\n\n/** Bytes as text, a character each, so equal bytes give equal text and a row's are a slice of its column's. */\nconst bytesDecoder = new TextDecoder(\"latin1\");\n\n/**\n * Each row's key in a table, as text, or null for a table without a column of each part: each part's column, its bytes\n * in the world's layout, which two worlds of one program share, or its value as JSON by its schema. Equal keys give\n * equal text.\n */\nconst keysOf = (table: World.Table, parts: readonly Part[]): ((row: number) => string) | null => {\n const columns: ((row: number) => string)[] = [];\n for (const { key, schema } of parts) {\n const column = table.columns[key];\n if (column === undefined) return null;\n if (column.kind === \"bytes\") {\n const text = bytesDecoder.decode(column.bytes);\n const { stride } = column;\n columns.push(row => text.slice(row * stride, (row + 1) * stride));\n }\n else if (column.kind === \"values\") columns.push(row => JSON.stringify(toJson(schema, column.values[row])));\n else {\n const text = JSON.stringify(toJson(schema, column.value));\n columns.push(() => text);\n }\n }\n const [only] = columns;\n return columns.length === 1 && only !== undefined ? only : row => columns.map(part => part(row)).join(\"\\u0000\");\n};\n\n/** Whether a table holds the entities an index holds: every key part and `with` component, and none in `not`. */\nconst covers = (table: World.Table, declaration: Index.Declared): boolean =>\n [...declaration.key, ...declaration.components.with ?? []].every(key => table.columns[key] !== undefined)\n && (declaration.components.not ?? []).every(key => table.columns[key] === undefined);\n\n/**\n * Adds to `existing` each of the snapshot's entities an index holds, to the world's with its key, or fails, as data,\n * for two of the snapshot's with one key; `name` is the index's.\n */\ntype Matched = (name: string, existing: Map<number, number>) => Promise<Failure | undefined>;\n\nconst twoWithOneKey = (name: string): Failure => ({ failure: `Index \"${name}\" can't match: two of the snapshot's entities have one key.` });\n\nconst readWorld = (world: World, declaration: Index.Declared): Promise<readonly World.Table[] | Failure> =>\n world.run(keysSystemOf(declaration)).catch((error: unknown) => ({ failure: error instanceof Error ? error.message : String(error) }));\n\n/** Two of the world's entities with one key: the one with the lower entity index, as its unique index keeps. */\nconst keep = (existing: Map<number, number>, entity: number, candidate: number): void => {\n const kept = existing.get(entity);\n if (kept === undefined || (candidate >>> 0) < (kept >>> 0)) existing.set(entity, candidate);\n};\n\n/** A table's columns of each part, or null for one without a column of bytes for each. */\nconst bytesOf = (table: World.Table, parts: readonly Part[]): KeyColumns | null => {\n const columns: KeyColumns[number][] = [];\n for (const { key } of parts) {\n const column = table.columns[key];\n if (column === undefined || column.kind !== \"bytes\") return null;\n columns.push(column);\n }\n return columns;\n};\n\n/**\n * Matches by the bytes of keys' columns, without making text of them, when every table of the snapshot holding the\n * index's entities has columns of bytes for its key; null otherwise. The world's tables, of the same program, then do too.\n */\nconst matchBytes = (world: World, from: Contents, declaration: Index.Declared, parts: readonly Part[]): Matched | null => {\n const tables = from.tables.filter(table => covers(table, declaration));\n const columns = tables.map(table => bytesOf(table, parts));\n if (columns.some(item => item === null)) return null;\n return async (name, existing) => {\n const saved = createKeyTable(tables.reduce((sum, table) => sum + table.count, 0));\n for (let t = 0; t < tables.length; t++) {\n const keys = columns[t];\n if (keys === null) continue;\n addKeyColumns(saved, keys);\n const { entities, count } = tables[t];\n for (let row = 0; row < count; row++) if (!addKey(saved, row, entities[row])) return twoWithOneKey(name);\n }\n if (saved.count === 0) return undefined;\n const read = await readWorld(world, declaration);\n if (\"failure\" in read) return read;\n for (const table of read) {\n const keys = bytesOf(table, parts);\n if (keys === null) continue;\n for (let row = 0, entry = -1; row < table.count; row++) {\n entry = findKey(saved, keys, row, entry + 1);\n if (entry >= 0) keep(existing, saved.values[entry], table.entities[row]);\n }\n }\n return undefined;\n };\n};\n\n/** Matches by keys as text, which any column gives. */\nconst matchText = (world: World, from: Contents, declaration: Index.Declared, parts: readonly Part[]): Matched => async (name, existing) => {\n const saved = new Map<string, number>();\n for (const table of from.tables) {\n if (!covers(table, declaration)) continue;\n const keyAt = keysOf(table, parts);\n if (keyAt === null) continue;\n for (let row = 0; row < table.count; row++) {\n const key = keyAt(row);\n if (saved.has(key)) return twoWithOneKey(name);\n saved.set(key, table.entities[row]);\n }\n }\n if (saved.size === 0) return undefined;\n const read = await readWorld(world, declaration);\n if (\"failure\" in read) return read;\n for (const table of read) {\n const keyAt = keysOf(table, parts);\n if (keyAt === null) continue;\n for (let row = 0; row < table.count; row++) {\n const entity = saved.get(keyAt(row));\n if (entity !== undefined) keep(existing, entity, table.entities[row]);\n }\n }\n return undefined;\n};\n\n/**\n * Each of the snapshot's entities that `indexes` say is the same as one of `world`'s, to that one: an index holds both,\n * with equal keys. Fails, as data, for an index whose key, `with` or `not` names a component the snapshot's trait doesn't\n * cover, or two of the snapshot's entities one index holds with one key.\n */\nexport const existingFor = async (world: World, from: Contents, indexes: readonly Matching[]): Promise<ReadonlyMap<number, number> | Failure> => {\n const existing = new Map<number, number>();\n const persisted = new Map(from.components.map(item => [item.key, item]));\n for (const index of indexes) {\n const declaration = Index.declarationOf(index);\n if (declaration === undefined) return { failure: `${index.name} isn't an index.` };\n const parts: Part[] = [];\n for (const key of declaration.key) {\n const found = persisted.get(key);\n if (found === undefined) return { failure: `Index \"${index.name}\" can't match: a part of its key isn't one the snapshot's trait covers, so the snapshot holds no key for it.` };\n parts.push({ key, schema: found.schema });\n }\n if ([...declaration.components.with ?? [], ...declaration.components.not ?? []].some(key => !persisted.has(key))) {\n return { failure: `Index \"${index.name}\" can't match: its with or not names a component the snapshot's trait doesn't cover, so the snapshot can't tell which of its entities the index holds.` };\n }\n const matched = matchBytes(world, from, declaration, parts) ?? matchText(world, from, declaration, parts);\n const found = await matched(index.name, existing);\n if (found !== undefined) return found;\n }\n return existing;\n};\n","// © 2026 Adobe. MIT License. See /LICENSE for details.\nimport { System, type Entity, type World } from \"@adobe/ecs\";\nimport type { Failure } from \"./failure.js\";\nimport { existingFor, type Matching } from \"./match/match.js\";\nimport { contents } from \"./program/contents.js\";\nimport type { Snapshot } from \"./snapshot.js\";\n\n/**\n * What `spawn` made: how many of the snapshot's entities it didn't spawn, those owned by an entity outside it; and,\n * unless asked not to, `entities`, each of the snapshot's entities to the one spawned for it, the one `match` found\n * for it, or null for one it didn't spawn.\n */\nexport type Spawned<Entities extends boolean = true> = { readonly failure?: undefined; readonly dropped: number }\n & (Entities extends false ? {} : { readonly entities: ReadonlyMap<Entity, Entity | null> });\n\nconst isTables = (value: unknown): value is readonly World.Table[] => Array.isArray(value) && value.every(table =>\n typeof table === \"object\" && table !== null && typeof Reflect.get(table, \"count\") === \"number\" && Reflect.get(table, \"entities\") instanceof Float64Array\n && typeof Reflect.get(table, \"columns\") === \"object\");\n\nconst isKeyed = (value: unknown): value is { readonly [key: symbol]: unknown } => typeof value === \"object\" && value !== null;\n\n/** `spawnTables` takes entities by their numbers: a map of the snapshot's to the world's holds only numbers. */\nconst isEntities = (value: unknown): value is ReadonlyMap<Entity, Entity> => {\n if (!(value instanceof Map)) return false;\n for (const [saved, found] of value) if (typeof saved !== \"number\" || typeof found !== \"number\") return false;\n return true;\n};\n\n/** Spawns its input's tables and sets its resources, a snapshot's, rewriting their entities, those `existing` maps standing for theirs. */\nconst spawnTables = System.create({}, ({ commands }, input: { readonly tables: unknown; readonly resources: unknown; readonly existing: unknown }) => {\n if (!isTables(input.tables) || !isKeyed(input.resources) || !isEntities(input.existing)) throw new Error(\"A snapshot holds tables and resources.\");\n return commands.spawnTables(input.tables, { resources: input.resources, existing: input.existing });\n});\n\n/**\n * Spawns an entity in `world` for each of the snapshot's, with the components its trait covers, and sets the resources\n * it holds, on the world's thread, as the world's references would leave them had the entities outside the snapshot\n * despawned: each entity value naming one of the snapshot's names the one spawned for it; an entity owned, directly\n * or through others, by an entity outside the snapshot isn't spawned, which the result counts; a weak reference to\n * one is left out; and any other entity value naming one is `Entity.none`.\n * - `match`: unique indexes saying which of the snapshot's entities are already in `world`: one an index holds there,\n * with the key of one of the snapshot's it holds, stands for it, keeping its own values, and isn't spawned again. It\n * fails for an index whose key, `with` or `not` names a component the snapshot's trait doesn't cover, or two of the\n * snapshot's entities with one key.\n * - `entities: false` leaves out the result's map of entities, which costs about 85 ms and 50 MB at a million.\n *\n * Fails, as data, for a column or resource that doesn't fit, spawning nothing.\n */\nexport function spawn(input: Spawning & { readonly entities: false }): Promise<Spawned<false> | Failure>;\nexport function spawn(input: Spawning & { readonly entities?: true }): Promise<Spawned | Failure>;\nexport async function spawn(input: Spawning & { readonly entities?: boolean }): Promise<Spawned<boolean> | Failure> {\n const from = input.snapshot[contents];\n const { tables, resources } = from;\n const values: { [key: symbol]: unknown } = {};\n for (const { persisted, value } of resources) values[persisted.key] = value;\n const existing = input.match === undefined || input.match.length === 0 ? new Map<number, number>() : await existingFor(input.world, from, input.match);\n if (\"failure\" in existing) return existing;\n const spawned = await input.world.run(spawnTables, { tables, resources: values, existing }).catch((error: unknown) => ({ failure: error instanceof Error ? error.message : String(error) }));\n if (\"failure\" in spawned) return spawned;\n if (input.entities === false) return { dropped: spawned.dropped };\n return { dropped: spawned.dropped, entities: entitiesOf(tables, spawned.entities) };\n}\n\n/** What `spawn` takes: the world spawned into, the snapshot, and the indexes saying which of its entities are there already. */\ntype Spawning = { readonly world: World; readonly snapshot: Snapshot; readonly match?: readonly Matching[] };\n\n/** Each of the tables' entities to the one spawned for it, the one existing for it, or null for one that wasn't. */\nconst entitiesOf = (tables: readonly World.Table[], spawned: readonly Float64Array[]): ReadonlyMap<Entity, Entity | null> => {\n const entities = new Map<Entity, Entity | null>();\n tables.forEach((table, t) => {\n const made = spawned[t];\n for (let row = 0; row < table.count; row++) {\n // A table's entities and `spawnTables`' are entities, `Entity.none`, 0, for a row it didn't spawn.\n const entity = made[row] as Entity;\n entities.set(table.entities[row] as Entity, entity === 0 ? null : entity);\n }\n });\n return entities;\n};\n"],"mappings":"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"}
|
package/dist/types/decode.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { Failure } from "./failure.js";
|
|
2
|
+
import type { NamingTrait } from "./program/program.js";
|
|
2
3
|
import type { Snapshot } from "./snapshot.js";
|
|
3
4
|
/**
|
|
4
|
-
* The snapshot `bytes` holds, its columns of bytes viewed where they are in `bytes`, copied once
|
|
5
|
-
* doesn't start at a multiple of 8; or why it can't be read: it isn't one,
|
|
6
|
-
*
|
|
5
|
+
* The snapshot `bytes` holds, read by the trait `by`, its columns of bytes viewed where they are in `bytes`, copied once
|
|
6
|
+
* only when `bytes` doesn't start at a multiple of 8; or why it can't be read: it isn't one, `by` isn't a symbol, or it
|
|
7
|
+
* names a component or resource the trait doesn't cover, or covers with another schema.
|
|
7
8
|
*/
|
|
8
|
-
export declare const decode: (bytes: Uint8Array
|
|
9
|
+
export declare const decode: (bytes: Uint8Array, options: {
|
|
10
|
+
readonly by: NamingTrait;
|
|
11
|
+
}) => Snapshot | Failure;
|
package/dist/types/encode.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Snapshot } from "./snapshot.js";
|
|
2
2
|
/**
|
|
3
|
-
* A snapshot as bytes, little-endian: a header naming each
|
|
4
|
-
* 8-byte-aligned blocks of each table's entities and columns, its bytes columns copied whole.
|
|
3
|
+
* A snapshot as bytes, little-endian: a header naming each component and resource it holds by its trait's name for it,
|
|
4
|
+
* with its schema, then 8-byte-aligned blocks of each table's entities and columns, its bytes columns copied whole.
|
|
5
5
|
*/
|
|
6
6
|
export declare const encode: (snapshot: Snapshot) => Uint8Array;
|
|
@@ -3,8 +3,8 @@ import type { Contents } from "../program/contents.js";
|
|
|
3
3
|
/** A snapshot's contents as bytes. */
|
|
4
4
|
export declare const encodeContents: (snapshot: Contents) => Uint8Array;
|
|
5
5
|
/**
|
|
6
|
-
* The snapshot contents `bytes` holds, its components and resources by
|
|
7
|
-
* bytes viewed where they are; or why it can't be read: it isn't one, or names what
|
|
8
|
-
*
|
|
6
|
+
* The snapshot contents `bytes` holds, its components and resources by the names the trait `by` gives them, its blocks
|
|
7
|
+
* of bytes viewed where they are; or why it can't be read: it isn't one, or names what the trait doesn't cover, or
|
|
8
|
+
* covers with another schema.
|
|
9
9
|
*/
|
|
10
|
-
export declare const decodeContents: (bytes: Uint8Array) => Contents | Failure;
|
|
10
|
+
export declare const decodeContents: (bytes: Uint8Array, by: unknown) => Contents | Failure;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Entity, type Vec3 } from "@adobe/ecs";
|
|
2
|
+
/** Saved in a file, under its name there. */
|
|
3
|
+
declare const file: unique symbol;
|
|
4
|
+
declare const position: unique symbol;
|
|
5
|
+
declare const name: unique symbol;
|
|
6
|
+
declare const target: unique symbol;
|
|
7
|
+
declare const selected: unique symbol;
|
|
8
|
+
declare const level: unique symbol;
|
|
9
|
+
declare module "@adobe/ecs" {
|
|
10
|
+
interface Traits<N> {
|
|
11
|
+
readonly [file]: string;
|
|
12
|
+
}
|
|
13
|
+
interface Components {
|
|
14
|
+
[position]: {
|
|
15
|
+
type: Vec3;
|
|
16
|
+
[file]: "position";
|
|
17
|
+
};
|
|
18
|
+
[name]: {
|
|
19
|
+
type: string;
|
|
20
|
+
[file]: "name";
|
|
21
|
+
};
|
|
22
|
+
[target]: {
|
|
23
|
+
type: Entity;
|
|
24
|
+
reference: "weak";
|
|
25
|
+
[file]: "target";
|
|
26
|
+
};
|
|
27
|
+
[selected]: {
|
|
28
|
+
type: true;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
interface Resources {
|
|
32
|
+
[level]: {
|
|
33
|
+
type: number;
|
|
34
|
+
default: 1;
|
|
35
|
+
[file]: "level";
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export declare const main: () => Promise<void>;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Entity } from "@adobe/ecs";
|
|
2
|
+
/** Saved in a file, under its name there. */
|
|
3
|
+
declare const file: unique symbol;
|
|
4
|
+
declare const label: unique symbol;
|
|
5
|
+
declare const parent: unique symbol;
|
|
6
|
+
declare module "@adobe/ecs" {
|
|
7
|
+
interface Traits<N> {
|
|
8
|
+
readonly [file]: string;
|
|
9
|
+
}
|
|
10
|
+
interface Components {
|
|
11
|
+
[label]: {
|
|
12
|
+
type: string;
|
|
13
|
+
[file]: "label";
|
|
14
|
+
};
|
|
15
|
+
[parent]: {
|
|
16
|
+
type: Entity;
|
|
17
|
+
reference: "owner";
|
|
18
|
+
[file]: "parent";
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export declare const main: () => Promise<void>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Vec2 } from "@adobe/ecs";
|
|
2
|
+
/** Saved in a file, under its name there. */
|
|
3
|
+
declare const file: unique symbol;
|
|
4
|
+
declare const chunk: unique symbol;
|
|
5
|
+
declare const tree: unique symbol;
|
|
6
|
+
declare module "@adobe/ecs" {
|
|
7
|
+
interface Traits<N> {
|
|
8
|
+
readonly [file]: string;
|
|
9
|
+
}
|
|
10
|
+
interface Components {
|
|
11
|
+
[chunk]: {
|
|
12
|
+
type: Vec2.I32;
|
|
13
|
+
shared: true;
|
|
14
|
+
[file]: "chunk";
|
|
15
|
+
};
|
|
16
|
+
[tree]: {
|
|
17
|
+
type: number;
|
|
18
|
+
[file]: "tree";
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export declare const main: () => Promise<void>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type Vec3 } from "@adobe/ecs";
|
|
2
|
+
/** Saved in the document's file, under its name there. */
|
|
3
|
+
declare const file: unique symbol;
|
|
4
|
+
/** Saved in the user's own settings, under its name there. */
|
|
5
|
+
declare const settings: unique symbol;
|
|
6
|
+
declare const voxel: unique symbol;
|
|
7
|
+
declare const material: unique symbol;
|
|
8
|
+
declare const size: unique symbol;
|
|
9
|
+
declare const tool: unique symbol;
|
|
10
|
+
declare const panelOpen: unique symbol;
|
|
11
|
+
declare module "@adobe/ecs" {
|
|
12
|
+
interface Traits<N> {
|
|
13
|
+
readonly [file]: string;
|
|
14
|
+
readonly [settings]: string;
|
|
15
|
+
}
|
|
16
|
+
interface Components {
|
|
17
|
+
[voxel]: {
|
|
18
|
+
type: Vec3.I32;
|
|
19
|
+
[file]: "voxel";
|
|
20
|
+
};
|
|
21
|
+
[material]: {
|
|
22
|
+
type: string;
|
|
23
|
+
[file]: "material";
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
interface Resources {
|
|
27
|
+
[size]: {
|
|
28
|
+
type: number;
|
|
29
|
+
default: 16;
|
|
30
|
+
[file]: "size";
|
|
31
|
+
};
|
|
32
|
+
[tool]: {
|
|
33
|
+
type: string;
|
|
34
|
+
default: "select";
|
|
35
|
+
[settings]: "tool";
|
|
36
|
+
};
|
|
37
|
+
[panelOpen]: {
|
|
38
|
+
type: boolean;
|
|
39
|
+
default: false;
|
|
40
|
+
[settings]: "panel open";
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export declare const main: () => Promise<void>;
|
|
45
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Saving, loading and copying the entities of `@adobe/ecs` worlds: snapshots of
|
|
3
|
-
*
|
|
2
|
+
* Saving, loading and copying the entities of `@adobe/ecs` worlds: snapshots of the components and resources a trait of
|
|
3
|
+
* the application's own covers, as binary columns.
|
|
4
4
|
* @module @adobe/ecs-persistence
|
|
5
5
|
*/
|
|
6
6
|
export { decode } from "./decode.js";
|
|
7
7
|
export { encode } from "./encode.js";
|
|
8
8
|
export type { Failure } from "./failure.js";
|
|
9
|
-
export { persist } from "./persist.js";
|
|
10
9
|
export { snapshot, type Snapshot } from "./snapshot.js";
|
|
11
10
|
export { spawn, type Spawned } from "./spawn.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** A table's key, a column of bytes for each part, `stride` bytes per row: a row's key is its bytes in each. */
|
|
2
|
+
export type KeyColumns = readonly {
|
|
3
|
+
readonly bytes: Uint8Array;
|
|
4
|
+
readonly stride: number;
|
|
5
|
+
}[];
|
|
6
|
+
/**
|
|
7
|
+
* Keys of rows of tables, each with a number: an open-addressing table of slots, linear probing, at most half full,
|
|
8
|
+
* each slot two words, a key's hash and its entry plus one, or 0 for none, so a probe reads its slots until a hash
|
|
9
|
+
* matches. An entry is a row of the tables it was added from, whose bytes are its key.
|
|
10
|
+
*/
|
|
11
|
+
export type KeyTable = {
|
|
12
|
+
readonly mask: number;
|
|
13
|
+
readonly slots: Int32Array;
|
|
14
|
+
readonly tables: Int32Array;
|
|
15
|
+
readonly rows: Int32Array;
|
|
16
|
+
readonly values: Float64Array;
|
|
17
|
+
readonly columns: KeyColumns[];
|
|
18
|
+
count: number;
|
|
19
|
+
};
|
|
20
|
+
/** A table for up to `capacity` keys. */
|
|
21
|
+
export declare const createKeyTable: (capacity: number) => KeyTable;
|
|
22
|
+
/**
|
|
23
|
+
* Begins adding the rows of a table of `columns`; `addKey` then takes rows of it. Each table's columns are kept, since
|
|
24
|
+
* its entries' keys are their rows' bytes.
|
|
25
|
+
*/
|
|
26
|
+
export declare const addKeyColumns: (table: KeyTable, columns: KeyColumns) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Adds row `row` of the table `addKeyColumns` began last, with `value`, unless a key equal to its is there: whether
|
|
29
|
+
* it was added. Takes at most the capacity the table was created with.
|
|
30
|
+
*/
|
|
31
|
+
export declare const addKey: (table: KeyTable, row: number, value: number) => boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The entry whose key is equal to row `row`'s of `columns`, or -1 for none. `guess` is an entry to compare first, such
|
|
34
|
+
* as the one after the entry found for the row before: rows in the order they were added are found without hashing.
|
|
35
|
+
*/
|
|
36
|
+
export declare const findKey: (table: KeyTable, columns: KeyColumns, row: number, guess: number) => number;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Index, type World } from "@adobe/ecs";
|
|
2
|
+
import type { Failure } from "../failure.js";
|
|
3
|
+
import type { Contents } from "../program/contents.js";
|
|
4
|
+
/** An index whose entities are the same in two worlds where their keys are: a unique one. */
|
|
5
|
+
export type Matching = Index<{
|
|
6
|
+
readonly unique: true;
|
|
7
|
+
}>;
|
|
8
|
+
/**
|
|
9
|
+
* Each of the snapshot's entities that `indexes` say is the same as one of `world`'s, to that one: an index holds both,
|
|
10
|
+
* with equal keys. Fails, as data, for an index whose key, `with` or `not` names a component the snapshot's trait doesn't
|
|
11
|
+
* cover, or two of the snapshot's entities one index holds with one key.
|
|
12
|
+
*/
|
|
13
|
+
export declare const existingFor: (world: World, from: Contents, indexes: readonly Matching[]) => Promise<ReadonlyMap<number, number> | Failure>;
|
|
@@ -3,11 +3,13 @@ import type { Persisted } from "./program.js";
|
|
|
3
3
|
/** Where a snapshot holds what it holds, a key only this package uses. */
|
|
4
4
|
export declare const contents: unique symbol;
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* What a trait covers of some of a world's entities: of each archetype, its entities and a column of each component
|
|
7
|
+
* the trait covers that it holds, keyed by this program's keys; and the values of the resources it covers.
|
|
8
8
|
*/
|
|
9
9
|
export type Contents = {
|
|
10
|
-
/**
|
|
10
|
+
/** The key of the trait it was taken or read by. */
|
|
11
|
+
readonly by: symbol;
|
|
12
|
+
/** Each component the trait covers that a table of it may hold. */
|
|
11
13
|
readonly components: readonly Persisted<Component.Key>[];
|
|
12
14
|
readonly tables: readonly World.Table[];
|
|
13
15
|
readonly resources: readonly {
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
import { Component, Resource, type Schema } from "@adobe/ecs";
|
|
2
|
-
/** A key
|
|
1
|
+
import { Component, Resource, type Schema, type Traits } from "@adobe/ecs";
|
|
2
|
+
/** A key a trait covers, with the name the trait saves it under and its schema. */
|
|
3
3
|
export type Persisted<K extends symbol = symbol> = {
|
|
4
4
|
readonly name: string;
|
|
5
5
|
readonly key: K;
|
|
6
6
|
readonly schema: Schema;
|
|
7
7
|
};
|
|
8
|
-
/** What
|
|
8
|
+
/** What a trait covers: its components and resources by name. */
|
|
9
9
|
export type Program = {
|
|
10
|
+
readonly by: symbol;
|
|
10
11
|
readonly components: ReadonlyMap<string, Persisted<Component.Key>>;
|
|
11
12
|
readonly resources: ReadonlyMap<string, Persisted<Resource.Key>>;
|
|
12
13
|
};
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
type Keyed = Component.Key | Resource.Key;
|
|
15
|
+
/** A trait of an application's own whose value names a key: its key a symbol, its value a string. */
|
|
16
|
+
export type NamingTrait = {
|
|
17
|
+
readonly [T in keyof Traits<Keyed>]: T extends symbol ? [Traits<Keyed>[T]] extends [string] ? T : never : never;
|
|
18
|
+
}[keyof Traits<Keyed>];
|
|
19
|
+
/** What the trait `by` covers, as the modules registered so far declare it, or why it can't be saved. */
|
|
20
|
+
export declare const programOf: (by: unknown) => Program | string;
|
|
21
|
+
export {};
|
|
@@ -2,16 +2,23 @@ import { Component, type Entity, type World } from "@adobe/ecs";
|
|
|
2
2
|
import type { Failure } from "../failure.js";
|
|
3
3
|
import type { Contents } from "../program/contents.js";
|
|
4
4
|
import type { Program } from "../program/program.js";
|
|
5
|
+
/** Values of shared components, which archetypes hold one of each: `where` as a system's of values. */
|
|
6
|
+
type Where = {
|
|
7
|
+
readonly [K in Component.With<"shared">]?: Component<K>;
|
|
8
|
+
};
|
|
5
9
|
/**
|
|
6
|
-
* Which entities a snapshot takes: those matching `with` and `
|
|
7
|
-
* default; or `root` and every entity it owns, directly or through others, by
|
|
10
|
+
* Which entities a snapshot takes: those matching `with`, `not` and `where`, every one holding a component its trait
|
|
11
|
+
* covers by default; or `root` and every entity it owns, directly or through others, by owner references its trait
|
|
12
|
+
* covers.
|
|
8
13
|
*/
|
|
9
14
|
export type Selection = {
|
|
10
15
|
readonly with?: readonly Component.Key[];
|
|
11
16
|
readonly not?: readonly Component.Key[];
|
|
17
|
+
readonly where?: Where;
|
|
12
18
|
readonly resources?: boolean;
|
|
13
19
|
} | {
|
|
14
20
|
readonly root: Entity;
|
|
15
21
|
};
|
|
16
|
-
/**
|
|
22
|
+
/** What `program`'s trait covers of the entities `selection` takes from `world`, on its thread, or why it can't be read. */
|
|
17
23
|
export declare const select: (world: World, program: Program, selection: Selection) => Promise<Contents | Failure>;
|
|
24
|
+
export {};
|