@driftengine/entities 4.7.4 → 4.8.1

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 CHANGED
@@ -45,6 +45,15 @@ runSchedule(world, schedule, tick);
45
45
  **The declaration is enforced.** A system that writes something it did not declare is refused
46
46
  naming both — a schedule derived from a lie is worse than no schedule. `writes` implies `reads`.
47
47
 
48
+ **A system that throws is skipped, and the others still run.** By default the failure is logged once
49
+ per system per world. Pass a reporter as the fourth argument to take every failure, every tick, with
50
+ the system's name, the tick and the error, and nothing is logged; throw from it to make a failure
51
+ fatal while developing:
52
+
53
+ ```ts
54
+ runSchedule(world, schedule, tick, ({ system, tick, error }) => overlay.add(system, tick, error));
55
+ ```
56
+
48
57
  Order is declaration order, adjusted only by `after`, which is topologically sorted with
49
58
  declaration order as the tie-break. A cycle in `after` is refused naming the systems.
50
59
 
package/dist/index.d.ts CHANGED
@@ -25,7 +25,7 @@ export type { WorldSnapshot } from './world.ts';
25
25
  export { World, createWorldSnapshot } from './world.ts';
26
26
  export type { SystemDefinition, SystemView } from './system.ts';
27
27
  export { BoundSystem } from './system.ts';
28
- export type { Schedule } from './schedule.ts';
28
+ export type { Schedule, SystemFailure, SystemFailureReporter } from './schedule.ts';
29
29
  export { buildSchedule, runSchedule } from './schedule.ts';
30
30
  export type { Prefab } from './prefab.ts';
31
31
  export { definePrefab, instantiate } from './prefab.ts';
@@ -37,8 +37,21 @@ export declare function buildSchedule(systems: readonly SystemDefinition[]): Sch
37
37
  * function no longer throws, which is what `AGENTS.md` requires of anything the frame loop calls.
38
38
  *
39
39
  * **What it gives up** is the stack reaching a debugger's uncaught-exception break, which is how
40
- * somebody working on a system would rather meet it. **What would change it** is a consumer wanting
41
- * failures fatal in development; the answer then is a reporter this takes as a parameter rather than
42
- * a rule chosen here for everybody.
40
+ * somebody working on a system would rather meet it. So `report` takes the decision back: given one,
41
+ * every failure goes to it, every tick it happens, with the system's name, the tick and the error as
42
+ * thrown, and nothing is logged. A game can count failures, show them in a debug overlay, send them
43
+ * to its own telemetry, or throw from it to make a failure fatal in development, which stops the
44
+ * schedule there. Without one, the failure is said once per system per world, as above.
43
45
  */
44
- export declare function runSchedule(world: World, schedule: Schedule, tick: number): void;
46
+ export declare function runSchedule(world: World, schedule: Schedule, tick: number, report?: SystemFailureReporter): void;
47
+ /** One system's failure in one step, as `runSchedule` hands it to a reporter. */
48
+ export interface SystemFailure {
49
+ /** The failing system's name, as its definition gave it. */
50
+ readonly system: string;
51
+ /** The tick it failed on: the `tick` the schedule was run with. */
52
+ readonly tick: number;
53
+ /** What the system threw, unchanged. */
54
+ readonly error: unknown;
55
+ }
56
+ /** Called with every failure, every tick. Throwing from it stops the schedule at that system. */
57
+ export type SystemFailureReporter = (failure: SystemFailure) => void;
package/dist/schedule.js CHANGED
@@ -117,11 +117,13 @@ const reported = new WeakMap();
117
117
  * function no longer throws, which is what `AGENTS.md` requires of anything the frame loop calls.
118
118
  *
119
119
  * **What it gives up** is the stack reaching a debugger's uncaught-exception break, which is how
120
- * somebody working on a system would rather meet it. **What would change it** is a consumer wanting
121
- * failures fatal in development; the answer then is a reporter this takes as a parameter rather than
122
- * a rule chosen here for everybody.
120
+ * somebody working on a system would rather meet it. So `report` takes the decision back: given one,
121
+ * every failure goes to it, every tick it happens, with the system's name, the tick and the error as
122
+ * thrown, and nothing is logged. A game can count failures, show them in a debug overlay, send them
123
+ * to its own telemetry, or throw from it to make a failure fatal in development, which stops the
124
+ * schedule there. Without one, the failure is said once per system per world, as above.
123
125
  */
124
- export function runSchedule(world, schedule, tick) {
126
+ export function runSchedule(world, schedule, tick, report) {
125
127
  let forWorld = bound.get(world);
126
128
  if (forWorld === undefined) {
127
129
  forWorld = new Map();
@@ -140,7 +142,10 @@ export function runSchedule(world, schedule, tick) {
140
142
  system.run();
141
143
  }
142
144
  catch (error) {
143
- announce(world, definition, error);
145
+ if (report === undefined)
146
+ announce(world, definition, error);
147
+ else
148
+ report({ system: definition.name, tick, error });
144
149
  }
145
150
  }
146
151
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@driftengine/entities",
3
- "version": "4.7.4",
3
+ "version": "4.8.1",
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.14.0"
30
+ "driftscript": "1.16.0"
31
31
  },
32
32
  "author": "Drift Technologies",
33
33
  "repository": {
package/src/index.ts CHANGED
@@ -35,7 +35,7 @@ export { World, createWorldSnapshot } from './world.ts';
35
35
  export type { SystemDefinition, SystemView } from './system.ts';
36
36
  export { BoundSystem } from './system.ts';
37
37
 
38
- export type { Schedule } from './schedule.ts';
38
+ export type { Schedule, SystemFailure, SystemFailureReporter } from './schedule.ts';
39
39
  export { buildSchedule, runSchedule } from './schedule.ts';
40
40
 
41
41
  export type { Prefab } from './prefab.ts';
package/src/schedule.ts CHANGED
@@ -158,11 +158,18 @@ const reported = new WeakMap<World, Set<SystemDefinition>>();
158
158
  * function no longer throws, which is what `AGENTS.md` requires of anything the frame loop calls.
159
159
  *
160
160
  * **What it gives up** is the stack reaching a debugger's uncaught-exception break, which is how
161
- * somebody working on a system would rather meet it. **What would change it** is a consumer wanting
162
- * failures fatal in development; the answer then is a reporter this takes as a parameter rather than
163
- * a rule chosen here for everybody.
161
+ * somebody working on a system would rather meet it. So `report` takes the decision back: given one,
162
+ * every failure goes to it, every tick it happens, with the system's name, the tick and the error as
163
+ * thrown, and nothing is logged. A game can count failures, show them in a debug overlay, send them
164
+ * to its own telemetry, or throw from it to make a failure fatal in development, which stops the
165
+ * schedule there. Without one, the failure is said once per system per world, as above.
164
166
  */
165
- export function runSchedule(world: World, schedule: Schedule, tick: number): void {
167
+ export function runSchedule(
168
+ world: World,
169
+ schedule: Schedule,
170
+ tick: number,
171
+ report?: SystemFailureReporter,
172
+ ): void {
166
173
  let forWorld = bound.get(world);
167
174
  if (forWorld === undefined) {
168
175
  forWorld = new Map();
@@ -181,11 +188,25 @@ export function runSchedule(world: World, schedule: Schedule, tick: number): voi
181
188
  try {
182
189
  system.run();
183
190
  } catch (error) {
184
- announce(world, definition, error);
191
+ if (report === undefined) announce(world, definition, error);
192
+ else report({ system: definition.name, tick, error });
185
193
  }
186
194
  }
187
195
  }
188
196
 
197
+ /** One system's failure in one step, as `runSchedule` hands it to a reporter. */
198
+ export interface SystemFailure {
199
+ /** The failing system's name, as its definition gave it. */
200
+ readonly system: string;
201
+ /** The tick it failed on: the `tick` the schedule was run with. */
202
+ readonly tick: number;
203
+ /** What the system threw, unchanged. */
204
+ readonly error: unknown;
205
+ }
206
+
207
+ /** Called with every failure, every tick. Throwing from it stops the schedule at that system. */
208
+ export type SystemFailureReporter = (failure: SystemFailure) => void;
209
+
189
210
  /** Say what failed, once per system per world. */
190
211
  function announce(world: World, definition: SystemDefinition, error: unknown): void {
191
212
  let seen = reported.get(world);