@ecsia/scheduler 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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -0
  3. package/dist/commands/apply.d.ts +49 -0
  4. package/dist/commands/apply.d.ts.map +1 -0
  5. package/dist/commands/apply.js +211 -0
  6. package/dist/commands/apply.js.map +1 -0
  7. package/dist/commands/buffer.d.ts +53 -0
  8. package/dist/commands/buffer.d.ts.map +1 -0
  9. package/dist/commands/buffer.js +66 -0
  10. package/dist/commands/buffer.js.map +1 -0
  11. package/dist/commands/encode.d.ts +30 -0
  12. package/dist/commands/encode.d.ts.map +1 -0
  13. package/dist/commands/encode.js +108 -0
  14. package/dist/commands/encode.js.map +1 -0
  15. package/dist/commands/fields.d.ts +22 -0
  16. package/dist/commands/fields.d.ts.map +1 -0
  17. package/dist/commands/fields.js +123 -0
  18. package/dist/commands/fields.js.map +1 -0
  19. package/dist/commands/index.d.ts +10 -0
  20. package/dist/commands/index.d.ts.map +1 -0
  21. package/dist/commands/index.js +6 -0
  22. package/dist/commands/index.js.map +1 -0
  23. package/dist/commands/op.d.ts +16 -0
  24. package/dist/commands/op.d.ts.map +1 -0
  25. package/dist/commands/op.js +43 -0
  26. package/dist/commands/op.js.map +1 -0
  27. package/dist/executor/guards.d.ts +8 -0
  28. package/dist/executor/guards.d.ts.map +1 -0
  29. package/dist/executor/guards.js +61 -0
  30. package/dist/executor/guards.js.map +1 -0
  31. package/dist/executor/index.d.ts +9 -0
  32. package/dist/executor/index.d.ts.map +1 -0
  33. package/dist/executor/index.js +6 -0
  34. package/dist/executor/index.js.map +1 -0
  35. package/dist/executor/run-wave.d.ts +19 -0
  36. package/dist/executor/run-wave.d.ts.map +1 -0
  37. package/dist/executor/run-wave.js +38 -0
  38. package/dist/executor/run-wave.js.map +1 -0
  39. package/dist/executor/scheduler.d.ts +41 -0
  40. package/dist/executor/scheduler.d.ts.map +1 -0
  41. package/dist/executor/scheduler.js +71 -0
  42. package/dist/executor/scheduler.js.map +1 -0
  43. package/dist/executor/seams.d.ts +38 -0
  44. package/dist/executor/seams.d.ts.map +1 -0
  45. package/dist/executor/seams.js +16 -0
  46. package/dist/executor/seams.js.map +1 -0
  47. package/dist/executor/update-threaded.d.ts +17 -0
  48. package/dist/executor/update-threaded.d.ts.map +1 -0
  49. package/dist/executor/update-threaded.js +67 -0
  50. package/dist/executor/update-threaded.js.map +1 -0
  51. package/dist/executor/update.d.ts +4 -0
  52. package/dist/executor/update.d.ts.map +1 -0
  53. package/dist/executor/update.js +23 -0
  54. package/dist/executor/update.js.map +1 -0
  55. package/dist/graph/dag.d.ts +16 -0
  56. package/dist/graph/dag.d.ts.map +1 -0
  57. package/dist/graph/dag.js +118 -0
  58. package/dist/graph/dag.js.map +1 -0
  59. package/dist/graph/edges.d.ts +20 -0
  60. package/dist/graph/edges.d.ts.map +1 -0
  61. package/dist/graph/edges.js +181 -0
  62. package/dist/graph/edges.js.map +1 -0
  63. package/dist/graph/index.d.ts +8 -0
  64. package/dist/graph/index.d.ts.map +1 -0
  65. package/dist/graph/index.js +5 -0
  66. package/dist/graph/index.js.map +1 -0
  67. package/dist/graph/waves.d.ts +30 -0
  68. package/dist/graph/waves.d.ts.map +1 -0
  69. package/dist/graph/waves.js +133 -0
  70. package/dist/graph/waves.js.map +1 -0
  71. package/dist/graph/weights.d.ts +7 -0
  72. package/dist/graph/weights.d.ts.map +1 -0
  73. package/dist/graph/weights.js +10 -0
  74. package/dist/graph/weights.js.map +1 -0
  75. package/dist/index.d.ts +9 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +21 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/internal.d.ts +13 -0
  80. package/dist/internal.d.ts.map +1 -0
  81. package/dist/internal.js +12 -0
  82. package/dist/internal.js.map +1 -0
  83. package/dist/planner/access.d.ts +18 -0
  84. package/dist/planner/access.d.ts.map +1 -0
  85. package/dist/planner/access.js +92 -0
  86. package/dist/planner/access.js.map +1 -0
  87. package/dist/planner/define-system.d.ts +14 -0
  88. package/dist/planner/define-system.d.ts.map +1 -0
  89. package/dist/planner/define-system.js +30 -0
  90. package/dist/planner/define-system.js.map +1 -0
  91. package/dist/planner/index.d.ts +6 -0
  92. package/dist/planner/index.d.ts.map +1 -0
  93. package/dist/planner/index.js +4 -0
  94. package/dist/planner/index.js.map +1 -0
  95. package/dist/planner/types.d.ts +74 -0
  96. package/dist/planner/types.d.ts.map +1 -0
  97. package/dist/planner/types.js +6 -0
  98. package/dist/planner/types.js.map +1 -0
  99. package/dist/workers/atomics-shim.d.ts +8 -0
  100. package/dist/workers/atomics-shim.d.ts.map +1 -0
  101. package/dist/workers/atomics-shim.js +14 -0
  102. package/dist/workers/atomics-shim.js.map +1 -0
  103. package/dist/workers/index.d.ts +13 -0
  104. package/dist/workers/index.d.ts.map +1 -0
  105. package/dist/workers/index.js +10 -0
  106. package/dist/workers/index.js.map +1 -0
  107. package/dist/workers/manifest.d.ts +67 -0
  108. package/dist/workers/manifest.d.ts.map +1 -0
  109. package/dist/workers/manifest.js +5 -0
  110. package/dist/workers/manifest.js.map +1 -0
  111. package/dist/workers/pool.d.ts +60 -0
  112. package/dist/workers/pool.d.ts.map +1 -0
  113. package/dist/workers/pool.js +313 -0
  114. package/dist/workers/pool.js.map +1 -0
  115. package/dist/workers/reservation.d.ts +18 -0
  116. package/dist/workers/reservation.d.ts.map +1 -0
  117. package/dist/workers/reservation.js +41 -0
  118. package/dist/workers/reservation.js.map +1 -0
  119. package/dist/workers/wave-sync.d.ts +18 -0
  120. package/dist/workers/wave-sync.d.ts.map +1 -0
  121. package/dist/workers/wave-sync.js +88 -0
  122. package/dist/workers/wave-sync.js.map +1 -0
  123. package/dist/workers/worker-entry.d.ts +2 -0
  124. package/dist/workers/worker-entry.d.ts.map +1 -0
  125. package/dist/workers/worker-entry.js +187 -0
  126. package/dist/workers/worker-entry.js.map +1 -0
  127. package/dist/workers/worker-system.d.ts +31 -0
  128. package/dist/workers/worker-system.d.ts.map +1 -0
  129. package/dist/workers/worker-system.js +20 -0
  130. package/dist/workers/worker-system.js.map +1 -0
  131. package/dist/workers/world-view.d.ts +39 -0
  132. package/dist/workers/world-view.d.ts.map +1 -0
  133. package/dist/workers/world-view.js +85 -0
  134. package/dist/workers/world-view.js.map +1 -0
  135. package/package.json +53 -0
@@ -0,0 +1,67 @@
1
+ // The THREADED frame loop (PHASE-2): the same wave/round walk as the
2
+ // single-thread executor (update.ts/run-wave.ts), but each round's worker-eligible batches are
3
+ // dispatched to the WorkerPool (pool.runRound) — which flips world.phase to 'wave' across the
4
+ // dispatch, awaits the Atomics wave fence, flips back to 'serial', and applies the per-worker command
5
+ // buffers in the deterministic ascending-worker-index merge order (flushAll).
6
+ //
7
+ // This is the missing integration the headline requires: a threaded world REPRODUCES the
8
+ // single-thread observable result through the SAME frame loop, not only
9
+ // through a hand-driven runRound. Worker batches run concurrently on workers; main-thread batches
10
+ // (workerIndex === -1, e.g. object-field systems) run serially on the main thread BEFORE the wave's
11
+ // dispatch, while world.phase is still 'serial' (so their direct-apply fast path is legal).
12
+ //
13
+ // Because batches in a round are conflict-free by construction (WAVE-CONFLICT ) and the command
14
+ // merge order is fixed, the threaded result equals the single-thread result for the same
15
+ // (state, plan, dt).
16
+ function runMainThreadSystem(env, batch, dt) {
17
+ const sb = env.systems[batch.systemId];
18
+ const ctx = {
19
+ world: env.world,
20
+ dt,
21
+ tick: env.world.currentTick(),
22
+ query: env.scopedQueries[sb.id],
23
+ };
24
+ sb.run(ctx);
25
+ }
26
+ async function runWaveThreaded(env, pool, wave, dt) {
27
+ for (const round of wave.rounds) {
28
+ // Main-thread batches first (phase stays 'serial', direct-apply legal — PHASE-2 ).
29
+ for (const batch of round) {
30
+ if (batch.workerIndex < 0)
31
+ runMainThreadSystem(env, batch, dt);
32
+ }
33
+ // Worker-eligible batches dispatched concurrently; runRound flips phase 'wave'→'serial', awaits the
34
+ // fence, and applies the per-worker command buffers (deterministic merge). It is a no-op if there
35
+ // are no worker batches in this round.
36
+ const workerBatches = round.filter((b) => b.workerIndex >= 0);
37
+ if (workerBatches.length > 0) {
38
+ await pool.runRound(workerBatches.map((b) => ({ systemId: b.systemId, workerIndex: b.workerIndex })), dt);
39
+ }
40
+ }
41
+ // ---- SERIAL SLOT (after the wave) ---- query maintenance + (per-system) observers.
42
+ env.world.maintainStructural();
43
+ if (env.observerCadence === 'per-system')
44
+ env.world.observerDrain();
45
+ }
46
+ /**
47
+ * Run one threaded tick: the whole schedule (worker waves) + reactivity flush. The frame order is
48
+ * byte-identical to the single-thread runUpdate (frameReset → waves → observerDrain → flushLogs);
49
+ * the only difference is that each round's worker batches run on workers.
50
+ */
51
+ export async function runUpdateThreaded(env, plan, pool, dt) {
52
+ const world = env.world;
53
+ if (world.phase !== 'serial') {
54
+ throw new Error(`scheduler.update entered with world.phase === '${world.phase}', expected 'serial'`);
55
+ }
56
+ world.frameReset();
57
+ for (const wave of plan.waves) {
58
+ await runWaveThreaded(env, pool, wave, dt);
59
+ }
60
+ if (env.observerCadence === 'frame-end')
61
+ world.observerDrain();
62
+ world.flushLogs();
63
+ if (world.phase !== 'serial') {
64
+ throw new Error(`scheduler.update exited with world.phase === '${world.phase}', expected 'serial'`);
65
+ }
66
+ }
67
+ //# sourceMappingURL=update-threaded.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-threaded.js","sourceRoot":"","sources":["../../src/executor/update-threaded.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,+FAA+F;AAC/F,8FAA8F;AAC9F,sGAAsG;AACtG,8EAA8E;AAC9E,EAAE;AACF,yFAAyF;AACzF,wEAAwE;AACxE,kGAAkG;AAClG,oGAAoG;AACpG,4FAA4F;AAC5F,EAAE;AACF,gGAAgG;AAChG,yFAAyF;AACzF,qBAAqB;AAcrB,SAAS,mBAAmB,CAAC,GAAgB,EAAE,KAAkB,EAAE,EAAU;IAC3E,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,QAA6B,CAAE,CAAA;IAC5D,MAAM,GAAG,GAAkB;QACzB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,EAAE;QACF,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,EAAqB;QAChD,KAAK,EAAE,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,EAAuB,CAAE;KACtD,CAAA;IACD,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AACb,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAgB,EAAE,IAAqB,EAAE,IAAkB,EAAE,EAAU;IACpG,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,mFAAmF;QACnF,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,WAAW,GAAG,CAAC;gBAAE,mBAAmB,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAA;QAChE,CAAC;QACD,oGAAoG;QACpG,kGAAkG;QAClG,uCAAuC;QACvC,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,CAAA;QAC7D,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,CAAC,QAAQ,CACjB,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,EAChF,EAAE,CACH,CAAA;QACH,CAAC;IACH,CAAC;IACD,qFAAqF;IACrF,GAAG,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAA;IAC9B,IAAI,GAAG,CAAC,eAAe,KAAK,YAAY;QAAE,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,CAAA;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAgB,EAAE,IAAkB,EAAE,IAAqB,EAAE,EAAU;IAC7G,MAAM,KAAK,GAAU,GAAG,CAAC,KAAK,CAAA;IAC9B,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,kDAAkD,KAAK,CAAC,KAAK,sBAAsB,CAAC,CAAA;IACtG,CAAC;IACD,KAAK,CAAC,UAAU,EAAE,CAAA;IAClB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IAC5C,CAAC;IACD,IAAI,GAAG,CAAC,eAAe,KAAK,WAAW;QAAE,KAAK,CAAC,aAAa,EAAE,CAAA;IAC9D,KAAK,CAAC,SAAS,EAAE,CAAA;IACjB,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,iDAAiD,KAAK,CAAC,KAAK,sBAAsB,CAAC,CAAA;IACrG,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { SchedulePlan } from '../graph/index.js';
2
+ import type { ExecutorEnv } from './run-wave.js';
3
+ export declare function runUpdate(env: ExecutorEnv, plan: SchedulePlan, dt: number): void;
4
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/executor/update.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,wBAAgB,SAAS,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAiBhF"}
@@ -0,0 +1,23 @@
1
+ // The world.update(dt) frame loop (byte-identical to ). Asserts
2
+ // world.phase === 'serial' on entry and exit: single-thread mode never leaves 'serial'.
3
+ import { runWave } from './run-wave.js';
4
+ export function runUpdate(env, plan, dt) {
5
+ const world = env.world;
6
+ if (world.phase !== 'serial') {
7
+ throw new Error(`scheduler.update entered with world.phase === '${world.phase}', expected 'serial'`);
8
+ }
9
+ // ---- 1. frame start ----
10
+ world.frameReset(); // advance currentTick; reset transient query lists
11
+ // ---- 2..4. run every wave, flushing structural + reactivity between waves ----
12
+ for (const wave of plan.waves) {
13
+ runWave(env, wave, dt);
14
+ }
15
+ // ---- 5. end-of-frame reactivity ----
16
+ if (env.observerCadence === 'frame-end')
17
+ world.observerDrain();
18
+ world.flushLogs(); // drain spill, schedule ring resize
19
+ if (world.phase !== 'serial') {
20
+ throw new Error(`scheduler.update exited with world.phase === '${world.phase}', expected 'serial'`);
21
+ }
22
+ }
23
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/executor/update.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,wFAAwF;AAIxF,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAGvC,MAAM,UAAU,SAAS,CAAC,GAAgB,EAAE,IAAkB,EAAE,EAAU;IACxE,MAAM,KAAK,GAAU,GAAG,CAAC,KAAK,CAAA;IAC9B,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,kDAAkD,KAAK,CAAC,KAAK,sBAAsB,CAAC,CAAA;IACtG,CAAC;IACD,2BAA2B;IAC3B,KAAK,CAAC,UAAU,EAAE,CAAA,CAAC,mDAAmD;IACtE,iFAAiF;IACjF,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IACxB,CAAC;IACD,uCAAuC;IACvC,IAAI,GAAG,CAAC,eAAe,KAAK,WAAW;QAAE,KAAK,CAAC,aAAa,EAAE,CAAA;IAC9D,KAAK,CAAC,SAAS,EAAE,CAAA,CAAC,oCAAoC;IACtD,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,iDAAiD,KAAK,CAAC,KAAK,sBAAsB,CAAC,CAAA;IACrG,CAAC;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { SystemId } from '@ecsia/schema';
2
+ import type { SystemBox } from '../planner/index.js';
3
+ import type { Edge } from './edges.js';
4
+ export declare class CycleError extends Error {
5
+ /** The cycle as a SystemId chain `[A, B, …, A]`. */
6
+ readonly chain: readonly SystemId[];
7
+ constructor(message: string, chain: readonly SystemId[]);
8
+ }
9
+ export interface DAG {
10
+ readonly n: number;
11
+ /** succ[from] = list of `to` (transitively reduced). */
12
+ readonly succ: readonly (readonly SystemId[])[];
13
+ }
14
+ /** Build the transitively-reduced DAG, failing fast on any cycle. */
15
+ export declare function buildDAG(systems: readonly SystemBox[], edges: readonly Edge[]): DAG;
16
+ //# sourceMappingURL=dag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dag.d.ts","sourceRoot":"","sources":["../../src/graph/dag.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,qBAAa,UAAW,SAAQ,KAAK;IACnC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAA;gBACvB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,QAAQ,EAAE;CAKxD;AAED,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,QAAQ,EAAE,CAAC,EAAE,CAAA;CAChD;AAwGD,qEAAqE;AACrE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,GAAG,CAMnF"}
@@ -0,0 +1,118 @@
1
+ // Conflict DAG construction: adjacency from the resolved max-weight edges, DFS
2
+ // three-color cycle detection with NAMED-CHAIN reporting + a suggested break edge, and
3
+ // DFS-based transitive reduction so the topological layering produces the widest possible waves.
4
+ export class CycleError extends Error {
5
+ /** The cycle as a SystemId chain `[A, B, …, A]`. */
6
+ chain;
7
+ constructor(message, chain) {
8
+ super(message);
9
+ this.name = 'CycleError';
10
+ this.chain = chain;
11
+ }
12
+ }
13
+ function adjacency(n, edges) {
14
+ const succ = Array.from({ length: n }, () => []);
15
+ for (const e of edges)
16
+ succ[e.from].push(e.to);
17
+ return succ;
18
+ }
19
+ function edgeCause(edges, from, to) {
20
+ const fn = from;
21
+ const tn = to;
22
+ for (const e of edges) {
23
+ if (e.from === fn && e.to === tn)
24
+ return e.cause;
25
+ }
26
+ return '';
27
+ }
28
+ /**
29
+ *: a single DFS finds ANY cycle; the gray-stack gives the full chain. Throws CycleError with the
30
+ * named chain and a suggested `inAnyOrderWith` break edge. Fail-fast at createWorld, never at
31
+ * frame time.
32
+ */
33
+ function detectCycle(systems, succ, edges) {
34
+ const n = succ.length;
35
+ const color = new Uint8Array(n);
36
+ const stack = [];
37
+ const dfs = (u) => {
38
+ const un = u;
39
+ color[un] = 1;
40
+ stack.push(u);
41
+ for (const v of succ[un]) {
42
+ const vn = v;
43
+ if (color[vn] === 1) {
44
+ // Back edge → cycle. Slice the gray stack from v to u, then close it.
45
+ const start = stack.findIndex((s) => s === vn);
46
+ const cycle = [...stack.slice(start), v];
47
+ throw new CycleError(reportChain(systems, edges, cycle), cycle);
48
+ }
49
+ if (color[vn] === 0)
50
+ dfs(v);
51
+ }
52
+ color[un] = 2;
53
+ stack.pop();
54
+ };
55
+ for (let i = 0; i < n; i++) {
56
+ if (color[i] === 0)
57
+ dfs(i);
58
+ }
59
+ }
60
+ function reportChain(systems, edges, cycle) {
61
+ const name = (id) => systems[id]?.name ?? `#${id}`;
62
+ const lines = ['System cycle detected:'];
63
+ for (let i = 0; i + 1 < cycle.length; i++) {
64
+ const from = cycle[i];
65
+ const to = cycle[i + 1];
66
+ const cause = edgeCause(edges, from, to);
67
+ lines.push(` ${name(from)} → ${name(to)}${cause ? ` (${cause})` : ''}`);
68
+ }
69
+ // Suggest breaking the first implicit-looking edge with inAnyOrderWith.
70
+ const a = name(cycle[0]);
71
+ const b = name(cycle[1] ?? cycle[0]);
72
+ lines.push(`Break it by declaring inAnyOrderWith(${a}, ${b}) if the order is irrelevant, or remove one of the conflicting declarations.`);
73
+ return lines.join('\n');
74
+ }
75
+ /**: remove edge A→C if a path A→B→C exists (B ≠ C). DFS reachability excluding the direct edge. */
76
+ function transitiveReduction(succ) {
77
+ const n = succ.length;
78
+ const reduced = Array.from({ length: n }, () => []);
79
+ // reachExcludingDirect(u, target): is `target` reachable from `u` WITHOUT using the direct u→target edge?
80
+ const reachableVia = (u, target) => {
81
+ const un = u;
82
+ const tn = target;
83
+ const seen = new Uint8Array(n);
84
+ const visit = (x) => {
85
+ for (const w of succ[x]) {
86
+ const wn = w;
87
+ if (x === un && wn === tn)
88
+ continue; // skip ONLY the direct u→target edge
89
+ if (wn === tn)
90
+ return true;
91
+ if (seen[wn] === 0) {
92
+ seen[wn] = 1;
93
+ if (visit(wn))
94
+ return true;
95
+ }
96
+ }
97
+ return false;
98
+ };
99
+ return visit(un);
100
+ };
101
+ for (let u = 0; u < n; u++) {
102
+ for (const v of succ[u]) {
103
+ // Keep u→v unless v is reachable from u through an intermediary (redundant transitive edge).
104
+ if (!reachableVia(u, v))
105
+ reduced[u].push(v);
106
+ }
107
+ }
108
+ return reduced;
109
+ }
110
+ /** Build the transitively-reduced DAG, failing fast on any cycle. */
111
+ export function buildDAG(systems, edges) {
112
+ const n = systems.length;
113
+ const succ = adjacency(n, edges);
114
+ detectCycle(systems, succ, edges);
115
+ const reduced = transitiveReduction(succ);
116
+ return { n, succ: reduced };
117
+ }
118
+ //# sourceMappingURL=dag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dag.js","sourceRoot":"","sources":["../../src/graph/dag.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { SystemId } from '@ecsia/schema';
2
+ import type { AccessMaps } from '../planner/index.js';
3
+ import type { SystemBox, SystemDef } from '../planner/index.js';
4
+ import { EdgeWeight } from './weights.js';
5
+ /** A resolved directed edge with the diagnostic cause(s) that produced its winning weight. */
6
+ export interface Edge {
7
+ readonly from: SystemId;
8
+ readonly to: SystemId;
9
+ readonly weight: EdgeWeight;
10
+ /** Human-readable cause used in cycle reporting. */
11
+ readonly cause: string;
12
+ }
13
+ /**
14
+ * Resolve before/after declarations to SystemIds, returning a new SystemBox set with `before`/`after`
15
+ * populated (the lowered boxes start with empty arrays — they need the full def→id map).
16
+ */
17
+ export declare function resolveOrdering(systems: readonly SystemBox[], defs: readonly SystemDef[]): SystemBox[];
18
+ /** Build the resolved max-weight edge set (DENY-suppressed IMPLICIT removed by construction). */
19
+ export declare function buildEdges(systems: readonly SystemBox[], defs: readonly SystemDef[], access: AccessMaps): Edge[];
20
+ //# sourceMappingURL=edges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edges.d.ts","sourceRoot":"","sources":["../../src/graph/edges.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,KAAK,EAAgB,SAAS,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC,8FAA8F;AAC9F,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AA8BD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,IAAI,EAAE,SAAS,SAAS,EAAE,GACzB,SAAS,EAAE,CAoBb;AA8HD,iGAAiG;AACjG,wBAAgB,UAAU,CACxB,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,IAAI,EAAE,SAAS,SAAS,EAAE,EAC1B,MAAM,EAAE,UAAU,GACjB,IAAI,EAAE,CAQR"}
@@ -0,0 +1,181 @@
1
+ // Conflict-edge derivation. Produces the resolved max-weight directed edge
2
+ // set `A → B` ("A must run before B") from four sources: EXPLICIT before/after (5), DENY suppression
3
+ // (4, removes IMPLICIT only), CLASS_HINT coarse helpers (3), and the auto IMPLICIT conflict edges (1).
4
+ import { EdgeWeight } from './weights.js';
5
+ function key(from, to) {
6
+ // Pack an ordered (from,to) pair into one number; system counts are well under 2^16.
7
+ return from * 0x10000 + to;
8
+ }
9
+ /** A DENY between A and B suppresses an IMPLICIT edge in BOTH directions. Keyed on the unordered pair. */
10
+ function denyKey(a, b) {
11
+ const lo = a < b ? a : b;
12
+ const hi = a < b ? b : a;
13
+ return lo * 0x10000 + hi;
14
+ }
15
+ function offer(b, edge) {
16
+ if (edge.from === edge.to)
17
+ return;
18
+ const k = key(edge.from, edge.to);
19
+ const prev = b.best.get(k);
20
+ if (prev === undefined || edge.weight > prev.weight) {
21
+ b.best.set(k, edge);
22
+ }
23
+ else if (edge.weight === prev.weight && edge.cause !== prev.cause) {
24
+ b.best.set(k, { ...prev, cause: `${prev.cause}; ${edge.cause}` });
25
+ }
26
+ }
27
+ /**
28
+ * Resolve before/after declarations to SystemIds, returning a new SystemBox set with `before`/`after`
29
+ * populated (the lowered boxes start with empty arrays — they need the full def→id map).
30
+ */
31
+ export function resolveOrdering(systems, defs) {
32
+ const idByDef = new Map();
33
+ defs.forEach((d, i) => idByDef.set(d, i));
34
+ const resolve = (list, owner) => {
35
+ if (list === undefined)
36
+ return [];
37
+ return list.map((d) => {
38
+ const id = idByDef.get(d);
39
+ if (id === undefined) {
40
+ throw new Error(`system '${owner}' references an unregistered system in before/after`);
41
+ }
42
+ return id;
43
+ });
44
+ };
45
+ return systems.map((sb) => Object.freeze({
46
+ ...sb,
47
+ before: resolve(sb.def.before, sb.name),
48
+ after: resolve(sb.def.after, sb.name),
49
+ }));
50
+ }
51
+ /**
52
+ * Collect the unordered pairs the user denied any implicit ordering between. Endpoints resolve
53
+ * by SystemDef identity first, then by `name` (unique per world) — name-matching tolerates the common
54
+ * spread-copy pattern `defineSystem({ ...a, order: [inAnyOrderWith(a, b)] })` where the registered def
55
+ * is a copy of the hint endpoint.
56
+ */
57
+ function collectDenials(systems, defs) {
58
+ const idByDef = new Map();
59
+ const idByName = new Map();
60
+ defs.forEach((d, i) => {
61
+ idByDef.set(d, i);
62
+ idByName.set(d.name, i);
63
+ });
64
+ const resolve = (d) => idByDef.get(d) ?? idByName.get(d.name);
65
+ const denied = new Set();
66
+ for (const sb of systems) {
67
+ for (const hint of sb.def.order ?? []) {
68
+ if (hint.kind === 'deny') {
69
+ const a = resolve(hint.a);
70
+ const b = resolve(hint.b);
71
+ if (a !== undefined && b !== undefined) {
72
+ denied.add(denyKey(a, b));
73
+ }
74
+ }
75
+ }
76
+ }
77
+ return denied;
78
+ }
79
+ function nameOf(systems, id) {
80
+ return systems[id]?.name ?? `#${id}`;
81
+ }
82
+ /** Does system `id` write component `c`? Tested against the packed write words. */
83
+ function writes(systems, id, c) {
84
+ const words = systems[id].writeWords;
85
+ return (words[c >>> 5] & (1 << (c & 31))) !== 0;
86
+ }
87
+ /**: IMPLICIT conflict edges. Reader–reader pairs never conflict; direction = registration order. */
88
+ function deriveImplicit(systems, access, denied, b) {
89
+ const allIds = new Set([...access.readers.keys(), ...access.writers.keys()]);
90
+ for (const c of allIds) {
91
+ const cn = c;
92
+ const W = access.writers.get(c) ?? new Set();
93
+ if (W.size === 0)
94
+ continue; // a pure reader-reader set on `c` never conflicts
95
+ const R = access.readers.get(c) ?? new Set();
96
+ const members = [...new Set([...W, ...R])];
97
+ for (let i = 0; i < members.length; i++) {
98
+ for (let j = i + 1; j < members.length; j++) {
99
+ const a = members[i];
100
+ const bb = members[j];
101
+ const an = a;
102
+ const bn = bb;
103
+ // At least one must WRITE `c` (a reader-reader pair drawn from R does not conflict).
104
+ if (!writes(systems, a, cn) && !writes(systems, bb, cn))
105
+ continue;
106
+ if (denied.has(denyKey(an, bn)))
107
+ continue;
108
+ const lo = an < bn ? a : bb; // earlier-registered runs first
109
+ const hi = an < bn ? bb : a;
110
+ const loVerb = writes(systems, lo, cn) ? 'writes' : 'reads';
111
+ const hiVerb = writes(systems, hi, cn) ? 'writes' : 'reads';
112
+ offer(b, {
113
+ from: lo,
114
+ to: hi,
115
+ weight: EdgeWeight.IMPLICIT,
116
+ cause: `both access component#${cn}: ${nameOf(systems, lo)} ${loVerb}, ${nameOf(systems, hi)} ${hiVerb}`,
117
+ });
118
+ }
119
+ }
120
+ }
121
+ }
122
+ /**: CLASS_HINT coarse helpers add weight-3 edges to all current writers/readers of `c`. */
123
+ function applyClassHints(systems, access, b) {
124
+ for (const sb of systems) {
125
+ for (const hint of sb.def.order ?? []) {
126
+ if (hint.kind === 'deny')
127
+ continue;
128
+ const cid = hint.component.id;
129
+ if (hint.kind === 'beforeWritersOf') {
130
+ const writers = access.writers.get(cid);
131
+ if (writers === undefined)
132
+ continue;
133
+ for (const w of writers) {
134
+ offer(b, {
135
+ from: sb.id,
136
+ to: w,
137
+ weight: EdgeWeight.CLASS_HINT,
138
+ cause: `${sb.name}.beforeWritersOf(${hint.component.name})`,
139
+ });
140
+ }
141
+ }
142
+ else {
143
+ const readers = access.readers.get(cid);
144
+ if (readers === undefined)
145
+ continue;
146
+ for (const r of readers) {
147
+ offer(b, {
148
+ from: r,
149
+ to: sb.id,
150
+ weight: EdgeWeight.CLASS_HINT,
151
+ cause: `${sb.name}.afterReadersOf(${hint.component.name})`,
152
+ });
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+ /**: EXPLICIT before/after edges (weight 5). */
159
+ function applyExplicit(systems, b) {
160
+ for (const sb of systems) {
161
+ for (const after of sb.after) {
162
+ // sb runs AFTER `after` ⇒ edge after → sb
163
+ offer(b, { from: after, to: sb.id, weight: EdgeWeight.EXPLICIT, cause: `${sb.name}.after = [${nameOf(systems, after)}]` });
164
+ }
165
+ for (const before of sb.before) {
166
+ // sb runs BEFORE `before` ⇒ edge sb → before
167
+ offer(b, { from: sb.id, to: before, weight: EdgeWeight.EXPLICIT, cause: `${sb.name}.before = [${nameOf(systems, before)}]` });
168
+ }
169
+ }
170
+ }
171
+ /** Build the resolved max-weight edge set (DENY-suppressed IMPLICIT removed by construction). */
172
+ export function buildEdges(systems, defs, access) {
173
+ const b = { best: new Map() };
174
+ const denied = collectDenials(systems, defs);
175
+ // Order matters only for cause-merge readability; max-weight resolution is order-independent.
176
+ applyExplicit(systems, b);
177
+ applyClassHints(systems, access, b);
178
+ deriveImplicit(systems, access, denied, b);
179
+ return [...b.best.values()];
180
+ }
181
+ //# sourceMappingURL=edges.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edges.js","sourceRoot":"","sources":["../../src/graph/edges.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { EdgeWeight } from './weights.js';
2
+ export { resolveOrdering, buildEdges } from './edges.js';
3
+ export type { Edge } from './edges.js';
4
+ export { buildDAG, CycleError } from './dag.js';
5
+ export type { DAG } from './dag.js';
6
+ export { buildPlan, concurrencyCompatible } from './waves.js';
7
+ export type { SchedulePlan, ScheduleWave, SystemBatch } from './waves.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACxD,YAAY,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC/C,YAAY,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AACnC,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAC7D,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,5 @@
1
+ export { EdgeWeight } from './weights.js';
2
+ export { resolveOrdering, buildEdges } from './edges.js';
3
+ export { buildDAG, CycleError } from './dag.js';
4
+ export { buildPlan, concurrencyCompatible } from './waves.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAExD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE/C,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,30 @@
1
+ import type { SystemId } from '@ecsia/schema';
2
+ import type { SystemBox } from '../planner/index.js';
3
+ import type { DAG } from './dag.js';
4
+ export interface SystemBatch {
5
+ readonly systemId: SystemId;
6
+ /** 0..workers-1 for worker batches; -1 = main-thread slot. */
7
+ readonly workerIndex: number;
8
+ }
9
+ export interface ScheduleWave {
10
+ /** Sequential rounds; rounds[r] runs after rounds[r-1] completes. Members of one round run concurrently. */
11
+ readonly rounds: readonly (readonly SystemBatch[])[];
12
+ /** Sum of maxSpawnsPerWave for systems dispatched to each worker this wave (reservation sizing). */
13
+ readonly perWorkerSpawnHint: Uint32Array;
14
+ }
15
+ export interface SchedulePlan {
16
+ readonly waves: readonly ScheduleWave[];
17
+ readonly systems: readonly SystemBox[];
18
+ readonly accessStrideWords: number;
19
+ readonly workers: number;
20
+ }
21
+ /**
22
+ * Rule WAVE-CONFLICT (v1, T5): A and B are concurrency-compatible iff write-sets
23
+ * are disjoint AND neither writes what the other reads. Pure read–read overlap is allowed.
24
+ * O(accessStrideWords) per pair — never per-entity, never per-archetype. This is the single seam a v2
25
+ * column-level build narrows.
26
+ */
27
+ export declare function concurrencyCompatible(a: SystemBox, b: SystemBox): boolean;
28
+ /** Build the immutable SchedulePlan from the reduced DAG. */
29
+ export declare function buildPlan(systems: readonly SystemBox[], dag: DAG, accessStrideWords: number, workers: number): SchedulePlan;
30
+ //# sourceMappingURL=waves.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waves.d.ts","sourceRoot":"","sources":["../../src/graph/waves.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,4GAA4G;IAC5G,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,WAAW,EAAE,CAAC,EAAE,CAAA;IACpD,oGAAoG;IACpG,QAAQ,CAAC,kBAAkB,EAAE,WAAW,CAAA;CACzC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAA;IACtC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAUzE;AAqGD,6DAA6D;AAC7D,wBAAgB,SAAS,CACvB,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,GAAG,EAAE,GAAG,EACR,iBAAiB,EAAE,MAAM,EACzB,OAAO,EAAE,MAAM,GACd,YAAY,CASd"}