@driftengine/entities 4.8.1 → 4.8.3

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/dist/prefab.d.ts CHANGED
@@ -16,6 +16,13 @@ export interface Prefab {
16
16
  readonly name: string;
17
17
  readonly components: readonly (readonly [ComponentType, Readonly<Record<string, unknown>>])[];
18
18
  }
19
+ /**
20
+ * A prefab, refused here if it names a field one of its components does not have.
21
+ *
22
+ * **At definition rather than at the first spawn**, which is the same refusal `World.add` makes,
23
+ * moved to where a game builds its content: a misspelt field found the first time a rare enemy
24
+ * spawns is found by a player.
25
+ */
19
26
  export declare function definePrefab(name: string, components: readonly (readonly [ComponentType, Readonly<Record<string, unknown>>])[]): Prefab;
20
27
  /**
21
28
  * Make one.
package/dist/prefab.js CHANGED
@@ -1,4 +1,19 @@
1
+ /**
2
+ * A prefab, refused here if it names a field one of its components does not have.
3
+ *
4
+ * **At definition rather than at the first spawn**, which is the same refusal `World.add` makes,
5
+ * moved to where a game builds its content: a misspelt field found the first time a rare enemy
6
+ * spawns is found by a player.
7
+ */
1
8
  export function definePrefab(name, components) {
9
+ for (const [type, values] of components) {
10
+ for (const field of Object.keys(values)) {
11
+ if (type.schema.fields.some((declared) => declared.name === field))
12
+ continue;
13
+ throw new Error(`\`${name}\` gives \`${type.name}\` a field \`${field}\` it does not have. It has ` +
14
+ `${type.schema.fields.map((declared) => `\`${declared.name}\``).join(', ')}.`);
15
+ }
16
+ }
2
17
  return { name, components };
3
18
  }
4
19
  /**
package/dist/store.d.ts CHANGED
@@ -124,6 +124,8 @@ export declare class ComponentStore {
124
124
  * not carry an object per field name it will never hand out.
125
125
  */
126
126
  private viewObject;
127
+ /** The schema's own field names, which a value handed to `add` must be among. */
128
+ private readonly fieldNames;
127
129
  constructor(type: ComponentType, options?: ComponentStoreOptions);
128
130
  get size(): number;
129
131
  /** The entities this store holds, oldest first. Valid up to `size`; the rest is capacity. */
package/dist/store.js CHANGED
@@ -184,12 +184,15 @@ export class ComponentStore {
184
184
  * not carry an object per field name it will never hand out.
185
185
  */
186
186
  viewObject = null;
187
+ /** The schema's own field names, which a value handed to `add` must be among. */
188
+ fieldNames = new Set();
187
189
  constructor(type, options = {}) {
188
190
  this.type = type;
189
191
  const capacity = Math.max(1, options.capacity ?? 16);
190
192
  this.denseArray = new Float64Array(capacity);
191
193
  this.sparse = new Int32Array(64).fill(-1);
192
194
  for (const field of type.schema.fields) {
195
+ this.fieldNames.add(field.name);
193
196
  const inner = optionInner(field.type);
194
197
  const declared = inner ?? field.type;
195
198
  /* A discriminant is an integer, so it takes an integer column rather than being boxed. */
@@ -247,6 +250,16 @@ export class ComponentStore {
247
250
  this.write(entity, field, value);
248
251
  return;
249
252
  }
253
+ /*
254
+ * **A key the schema does not declare is refused before anything is stored**, as `write` and
255
+ * `defineComponent`'s defaults refuse one. The loop below walks the schema and never looked at
256
+ * the other keys, so a misspelt field was dropped without a word and the field it meant took
257
+ * its default; one consumer's had been dropped since the component was written.
258
+ */
259
+ for (const field in values) {
260
+ if (!this.fieldNames.has(field))
261
+ throw new Error(this.noField(field));
262
+ }
250
263
  const at = this.count;
251
264
  this.grow(at + 1);
252
265
  this.growSparse(entityIndex(entity) + 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@driftengine/entities",
3
- "version": "4.8.1",
3
+ "version": "4.8.3",
4
4
  "description": "Entities with generational identity, component storage, systems and scenes",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  ],
28
28
  "sideEffects": false,
29
29
  "dependencies": {
30
- "driftscript": "1.16.0"
30
+ "driftscript": "1.17.0"
31
31
  },
32
32
  "author": "Drift Technologies",
33
33
  "repository": {
package/src/prefab.ts CHANGED
@@ -18,10 +18,26 @@ export interface Prefab {
18
18
  readonly components: readonly (readonly [ComponentType, Readonly<Record<string, unknown>>])[];
19
19
  }
20
20
 
21
+ /**
22
+ * A prefab, refused here if it names a field one of its components does not have.
23
+ *
24
+ * **At definition rather than at the first spawn**, which is the same refusal `World.add` makes,
25
+ * moved to where a game builds its content: a misspelt field found the first time a rare enemy
26
+ * spawns is found by a player.
27
+ */
21
28
  export function definePrefab(
22
29
  name: string,
23
30
  components: readonly (readonly [ComponentType, Readonly<Record<string, unknown>>])[],
24
31
  ): Prefab {
32
+ for (const [type, values] of components) {
33
+ for (const field of Object.keys(values)) {
34
+ if (type.schema.fields.some((declared) => declared.name === field)) continue;
35
+ throw new Error(
36
+ `\`${name}\` gives \`${type.name}\` a field \`${field}\` it does not have. It has ` +
37
+ `${type.schema.fields.map((declared) => `\`${declared.name}\``).join(', ')}.`,
38
+ );
39
+ }
40
+ }
25
41
  return { name, components };
26
42
  }
27
43
 
package/src/store.ts CHANGED
@@ -312,12 +312,16 @@ export class ComponentStore {
312
312
  */
313
313
  private viewObject: ComponentView | null = null;
314
314
 
315
+ /** The schema's own field names, which a value handed to `add` must be among. */
316
+ private readonly fieldNames = new Set<string>();
317
+
315
318
  constructor(type: ComponentType, options: ComponentStoreOptions = {}) {
316
319
  this.type = type;
317
320
  const capacity = Math.max(1, options.capacity ?? 16);
318
321
  this.denseArray = new Float64Array(capacity);
319
322
  this.sparse = new Int32Array(64).fill(-1);
320
323
  for (const field of type.schema.fields) {
324
+ this.fieldNames.add(field.name);
321
325
  const inner = optionInner(field.type);
322
326
  const declared = inner ?? field.type;
323
327
  /* A discriminant is an integer, so it takes an integer column rather than being boxed. */
@@ -381,6 +385,15 @@ export class ComponentStore {
381
385
  for (const [field, value] of Object.entries(values)) this.write(entity, field, value);
382
386
  return;
383
387
  }
388
+ /*
389
+ * **A key the schema does not declare is refused before anything is stored**, as `write` and
390
+ * `defineComponent`'s defaults refuse one. The loop below walks the schema and never looked at
391
+ * the other keys, so a misspelt field was dropped without a word and the field it meant took
392
+ * its default; one consumer's had been dropped since the component was written.
393
+ */
394
+ for (const field in values) {
395
+ if (!this.fieldNames.has(field)) throw new Error(this.noField(field));
396
+ }
384
397
 
385
398
  const at = this.count;
386
399
  this.grow(at + 1);