@typeonce/ritmo 0.6.0 → 0.7.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 +2 -0
- package/dist/Brand.d.ts +9 -4
- package/dist/Brand.d.ts.map +1 -1
- package/dist/Brand.js +9 -4
- package/dist/Brand.js.map +1 -1
- package/dist/Clock.d.ts +186 -0
- package/dist/Clock.d.ts.map +1 -0
- package/dist/Clock.js +111 -0
- package/dist/Clock.js.map +1 -0
- package/dist/Command.d.ts +157 -33
- package/dist/Command.d.ts.map +1 -1
- package/dist/Command.js +49 -15
- package/dist/Command.js.map +1 -1
- package/dist/Debug.d.ts +104 -12
- package/dist/Debug.d.ts.map +1 -1
- package/dist/Debug.js.map +1 -1
- package/dist/Decode.d.ts +1 -1
- package/dist/Decode.js +1 -1
- package/dist/Descriptor.d.ts +31 -21
- package/dist/Descriptor.d.ts.map +1 -1
- package/dist/Descriptor.js +21 -12
- package/dist/Descriptor.js.map +1 -1
- package/dist/Entity.d.ts +14 -5
- package/dist/Entity.d.ts.map +1 -1
- package/dist/Entity.js +2 -2
- package/dist/Entity.js.map +1 -1
- package/dist/EntityScope.d.ts +6 -0
- package/dist/EntityScope.d.ts.map +1 -1
- package/dist/EntityScope.js +6 -0
- package/dist/EntityScope.js.map +1 -1
- package/dist/HostClock.d.ts +49 -0
- package/dist/HostClock.d.ts.map +1 -0
- package/dist/HostClock.js +46 -0
- package/dist/HostClock.js.map +1 -0
- package/dist/Inspector.d.ts +7 -1
- package/dist/Inspector.d.ts.map +1 -1
- package/dist/Inspector.js.map +1 -1
- package/dist/Machine.d.ts +12 -3
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +4 -1
- package/dist/Machine.js.map +1 -1
- package/dist/Query.d.ts +28 -24
- package/dist/Query.d.ts.map +1 -1
- package/dist/Query.js +0 -24
- package/dist/Query.js.map +1 -1
- package/dist/Runtime.d.ts +36 -16
- package/dist/Runtime.d.ts.map +1 -1
- package/dist/Runtime.js +230 -67
- package/dist/Runtime.js.map +1 -1
- package/dist/Schedule.d.ts +217 -10
- package/dist/Schedule.d.ts.map +1 -1
- package/dist/Schedule.js +5 -0
- package/dist/Schedule.js.map +1 -1
- package/dist/Schema.d.ts +61 -25
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +0 -8
- package/dist/Schema.js.map +1 -1
- package/dist/System.d.ts +149 -52
- package/dist/System.d.ts.map +1 -1
- package/dist/System.js +52 -6
- package/dist/System.js.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/internal/cells.d.ts +3 -3
- package/dist/internal/cells.d.ts.map +1 -1
- package/dist/internal/cells.js +9 -11
- package/dist/internal/cells.js.map +1 -1
- package/dist/internal/debug.d.ts +6 -0
- package/dist/internal/debug.d.ts.map +1 -1
- package/dist/internal/debug.js +95 -1
- package/dist/internal/debug.js.map +1 -1
- package/dist/internal/game.d.ts.map +1 -1
- package/dist/internal/game.js +9 -2
- package/dist/internal/game.js.map +1 -1
- package/dist/internal/order.d.ts +10 -0
- package/dist/internal/order.d.ts.map +1 -0
- package/dist/internal/order.js +120 -0
- package/dist/internal/order.js.map +1 -0
- package/dist/internal/queries.js +2 -2
- package/dist/internal/queries.js.map +1 -1
- package/package.json +1 -1
- package/src/Brand.ts +9 -4
- package/src/Clock.ts +311 -0
- package/src/Command.ts +271 -73
- package/src/Debug.ts +111 -10
- package/src/Decode.ts +1 -1
- package/src/Descriptor.ts +37 -27
- package/src/Entity.ts +15 -5
- package/src/EntityScope.ts +6 -0
- package/src/HostClock.ts +64 -0
- package/src/Inspector.ts +7 -2
- package/src/Machine.ts +13 -3
- package/src/Query.ts +41 -33
- package/src/Runtime.ts +273 -91
- package/src/Schedule.ts +380 -13
- package/src/Schema.ts +70 -41
- package/src/System.ts +174 -88
- package/src/index.ts +8 -7
- package/src/internal/cells.ts +10 -12
- package/src/internal/debug.ts +104 -4
- package/src/internal/game.ts +11 -3
- package/src/internal/order.ts +140 -0
- package/src/internal/queries.ts +3 -3
- package/dist/Definition.d.ts +0 -99
- package/dist/Definition.d.ts.map +0 -1
- package/dist/Definition.js +0 -68
- package/dist/Definition.js.map +0 -1
- package/dist/Feature.d.ts +0 -106
- package/dist/Feature.d.ts.map +0 -1
- package/dist/Feature.js +0 -87
- package/dist/Feature.js.map +0 -1
- package/dist/Fx.d.ts +0 -128
- package/dist/Fx.d.ts.map +0 -1
- package/dist/Fx.js +0 -112
- package/dist/Fx.js.map +0 -1
- package/dist/internal/definition.d.ts +0 -17
- package/dist/internal/definition.d.ts.map +0 -1
- package/dist/internal/definition.js +0 -25
- package/dist/internal/definition.js.map +0 -1
- package/src/Definition.ts +0 -106
- package/src/Feature.ts +0 -241
- package/src/Fx.ts +0 -176
- package/src/internal/definition.ts +0 -56
package/src/Runtime.ts
CHANGED
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
* @groupDescription Functions
|
|
56
56
|
* Public runtime constructors and helpers for services, machines, and bootstrap input.
|
|
57
57
|
*/
|
|
58
|
+
import * as HostClockModule from "./HostClock.ts"
|
|
58
59
|
import * as Command from "./Command.ts"
|
|
59
60
|
import type * as Debug from "./Debug.ts"
|
|
60
61
|
import * as DescriptorModule from "./Descriptor.ts"
|
|
@@ -74,9 +75,10 @@ import * as Relation from "./Relation.ts"
|
|
|
74
75
|
import type * as Requirement from "./Requirement.ts"
|
|
75
76
|
import * as Result from "./Result.ts"
|
|
76
77
|
import * as Schedule from "./Schedule.ts"
|
|
78
|
+
import * as OrderInternal from "./internal/order.ts"
|
|
77
79
|
import * as EntityScopeModule from "./EntityScope.ts"
|
|
78
80
|
import * as Snapshot from "./Snapshot.ts"
|
|
79
|
-
import type { ExecutableScheduleDefinition } from "./Schedule.ts"
|
|
81
|
+
import type { AnyScheduleOrder, ExecutableScheduleDefinition } from "./Schedule.ts"
|
|
80
82
|
import type { Registry, Schema } from "./Schema.ts"
|
|
81
83
|
import type {
|
|
82
84
|
DespawnedReadView,
|
|
@@ -179,10 +181,13 @@ export type RuntimeConstructionError<S extends Schema.Any, Provided> = Simplify<
|
|
|
179
181
|
}>
|
|
180
182
|
|
|
181
183
|
/**
|
|
182
|
-
* A runtime plus the `debug` handle when it was made with `debug:
|
|
184
|
+
* A runtime plus the `debug` handle when it was made with `debug: { hostClock }`
|
|
185
|
+
* (optional when the option may be `undefined`).
|
|
183
186
|
*/
|
|
184
187
|
export type WithDebug<R, Enabled, S extends Schema.Any, Root> =
|
|
185
|
-
[Enabled] extends [
|
|
188
|
+
[Enabled] extends [undefined] ? R
|
|
189
|
+
: [Enabled] extends [Debug.Option] ? R & Debug.Member<S, Root>
|
|
190
|
+
: R & Partial<Debug.Member<S, Root>>
|
|
186
191
|
|
|
187
192
|
/**
|
|
188
193
|
* The runtime returned by `make`, wrapped in a `Result` exactly when a
|
|
@@ -367,23 +372,23 @@ type RequirementErrorsFor<
|
|
|
367
372
|
: never
|
|
368
373
|
|
|
369
374
|
type RequirementErrorsOfSchedule<
|
|
370
|
-
Selected extends ExecutableScheduleDefinition<any, any, any, any, any>,
|
|
375
|
+
Selected extends ExecutableScheduleDefinition<any, any, any, any, any, AnyScheduleOrder>,
|
|
371
376
|
Services extends Record<string, unknown>,
|
|
372
377
|
Resources extends object,
|
|
373
378
|
Machines extends object
|
|
374
|
-
> = Selected extends ExecutableScheduleDefinition<infer S, any, any, any, any>
|
|
379
|
+
> = Selected extends ExecutableScheduleDefinition<infer S, any, any, any, any, AnyScheduleOrder>
|
|
375
380
|
? RequirementErrorsFor<Requirement.Of<Selected>, S, Services, Resources, Machines>
|
|
376
381
|
: never
|
|
377
382
|
|
|
378
383
|
export type ValidateSchedules<
|
|
379
|
-
Schedules extends ReadonlyArray<ExecutableScheduleDefinition<any, any, any, any, any>>,
|
|
384
|
+
Schedules extends ReadonlyArray<ExecutableScheduleDefinition<any, any, any, any, any, AnyScheduleOrder>>,
|
|
380
385
|
Services extends Record<string, unknown>,
|
|
381
386
|
Resources extends object,
|
|
382
387
|
Machines extends object
|
|
383
388
|
> = {
|
|
384
389
|
readonly [K in keyof Schedules]:
|
|
385
390
|
Schedules[K] & ValidateSchedule<
|
|
386
|
-
Extract<Schedules[K], ExecutableScheduleDefinition<any, any, any, any, any>>,
|
|
391
|
+
Extract<Schedules[K], ExecutableScheduleDefinition<any, any, any, any, any, AnyScheduleOrder>>,
|
|
387
392
|
Services,
|
|
388
393
|
Resources,
|
|
389
394
|
Machines
|
|
@@ -391,7 +396,7 @@ export type ValidateSchedules<
|
|
|
391
396
|
}
|
|
392
397
|
|
|
393
398
|
export type ValidateScheduleArray<
|
|
394
|
-
Schedules extends ReadonlyArray<ExecutableScheduleDefinition<any, any, any, any, any>>,
|
|
399
|
+
Schedules extends ReadonlyArray<ExecutableScheduleDefinition<any, any, any, any, any, AnyScheduleOrder>>,
|
|
395
400
|
Services extends Record<string, unknown>,
|
|
396
401
|
Resources extends object,
|
|
397
402
|
Machines extends object
|
|
@@ -402,18 +407,23 @@ export type ValidateScheduleArray<
|
|
|
402
407
|
}
|
|
403
408
|
|
|
404
409
|
type ValidateScheduleArgs<
|
|
405
|
-
Schedules extends ReadonlyArray<ExecutableScheduleDefinition<any, any, any, any, any>>,
|
|
410
|
+
Schedules extends ReadonlyArray<ExecutableScheduleDefinition<any, any, any, any, any, AnyScheduleOrder>>,
|
|
406
411
|
Services extends Record<string, unknown>,
|
|
407
412
|
Resources extends object,
|
|
408
413
|
Machines extends object
|
|
409
414
|
> = [RequirementErrorsOfSchedule<NoInfer<Schedules[number]>, Services, Resources, Machines>] extends [never]
|
|
410
|
-
? Schedules
|
|
415
|
+
? [Schedule.TickOrderErrors<NoInfer<Schedules>>] extends [never]
|
|
416
|
+
? Schedules
|
|
417
|
+
: Schedules & {
|
|
418
|
+
/** `Game.System.thisTick` reads whose writers do not all run earlier in this call. */
|
|
419
|
+
readonly __fixTickOrder__: Schedule.TickOrderErrors<NoInfer<Schedules>>
|
|
420
|
+
}
|
|
411
421
|
: Schedules & {
|
|
412
422
|
readonly __fixRuntimeRequirements__: RequirementErrorsOfSchedule<NoInfer<Schedules[number]>, Services, Resources, Machines>
|
|
413
423
|
}
|
|
414
424
|
|
|
415
425
|
type ValidateSchedule<
|
|
416
|
-
Schedule extends ExecutableScheduleDefinition<any, any, any, any, any>,
|
|
426
|
+
Schedule extends ExecutableScheduleDefinition<any, any, any, any, any, AnyScheduleOrder>,
|
|
417
427
|
Services extends Record<string, unknown>,
|
|
418
428
|
Resources extends object,
|
|
419
429
|
Machines extends object
|
|
@@ -432,7 +442,13 @@ type RequirementErrorsOfInspector<
|
|
|
432
442
|
? RequirementErrorsFor<Needs, Spec["schema"], Services, Resources, Machines>
|
|
433
443
|
: never
|
|
434
444
|
|
|
435
|
-
|
|
445
|
+
/**
|
|
446
|
+
* Resolves to `unknown` when the runtime provides every resource, service,
|
|
447
|
+
* and machine the inspector reads, otherwise to an object naming each
|
|
448
|
+
* missing or incompatible one. `runtime.inspect(...)` uses it; tools that
|
|
449
|
+
* run inspectors on a runtime they receive (devtools invariants) reuse it.
|
|
450
|
+
*/
|
|
451
|
+
export type ValidateInspector<
|
|
436
452
|
Selected extends Inspector.Inspector.Any,
|
|
437
453
|
Services extends Record<string, unknown>,
|
|
438
454
|
Resources extends object,
|
|
@@ -451,6 +467,15 @@ export interface MissingRuntimeRequirement {
|
|
|
451
467
|
readonly name: string
|
|
452
468
|
}
|
|
453
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Returned by `tryTick` when a `Game.System.thisTick` read has a writer that
|
|
472
|
+
* runs after it in the schedule. Nothing ran.
|
|
473
|
+
*/
|
|
474
|
+
export interface TickOrderViolation {
|
|
475
|
+
readonly kind: "TickOrderViolation"
|
|
476
|
+
readonly issues: ReadonlyArray<Schedule.OrderIssue>
|
|
477
|
+
}
|
|
478
|
+
|
|
454
479
|
/** Explicit failure returned by the dynamic schedule execution path. */
|
|
455
480
|
export interface MissingRuntimeRequirements {
|
|
456
481
|
readonly kind: "MissingRuntimeRequirements"
|
|
@@ -584,20 +609,22 @@ export interface Runtime<
|
|
|
584
609
|
* system's ECS writes are rolled back and earlier systems stay committed.
|
|
585
610
|
*/
|
|
586
611
|
readonly tick: {
|
|
587
|
-
<const Schedules extends ReadonlyArray<ExecutableScheduleDefinition<S, any, Root, any, any>>>(
|
|
612
|
+
<const Schedules extends ReadonlyArray<ExecutableScheduleDefinition<S, any, Root, any, any, AnyScheduleOrder>>>(
|
|
588
613
|
...schedules: ValidateScheduleArgs<Schedules, Services, Resources, Machines>
|
|
589
614
|
): Result.Result<void, Schedule.FailureOf<Schedules[number]>>
|
|
590
615
|
}
|
|
591
616
|
/**
|
|
592
|
-
* Runs a schedule whose exact requirements are not statically known
|
|
593
|
-
*
|
|
617
|
+
* Runs a schedule whose exact requirements are not statically known: for
|
|
618
|
+
* tools that hold schedules with erased types, such as devtools sessions.
|
|
619
|
+
* Games use `tick(...)`, which rejects missing requirements at compile time.
|
|
594
620
|
*
|
|
595
621
|
* Requirements are checked against current provisioning first and missing
|
|
596
|
-
* ones are returned as data.
|
|
622
|
+
* ones are returned as data, then `Game.System.thisTick` reads are checked
|
|
623
|
+
* against the schedule's order (`TickOrderViolation`).
|
|
597
624
|
*/
|
|
598
|
-
readonly tryTick: <const Selected extends ExecutableScheduleDefinition<S, any, any, any, any>>(
|
|
625
|
+
readonly tryTick: <const Selected extends ExecutableScheduleDefinition<S, any, any, any, any, AnyScheduleOrder>>(
|
|
599
626
|
schedule: Selected
|
|
600
|
-
) => Result.Result<void, MissingRuntimeRequirements | Schedule.FailureOf<Selected>>
|
|
627
|
+
) => Result.Result<void, MissingRuntimeRequirements | TickOrderViolation | Schedule.FailureOf<Selected>>
|
|
601
628
|
/**
|
|
602
629
|
* Captures the world as plain data: entities with their ids, components,
|
|
603
630
|
* relation edges, resources, and committed machine values.
|
|
@@ -678,7 +705,7 @@ const makeValidatedRuntime = <
|
|
|
678
705
|
readonly resources: Partial<Record<string, unknown>>
|
|
679
706
|
readonly machines: RuntimeMachines<any> | undefined
|
|
680
707
|
readonly machineDefinitions: ReadonlyArray<Machine.StateMachine.Any>
|
|
681
|
-
readonly debug:
|
|
708
|
+
readonly debug: Debug.Option | undefined
|
|
682
709
|
}): Runtime<S, Services, Resources, Root, Machines> => {
|
|
683
710
|
const world = makeWorld(options.schema, streamCapacity)
|
|
684
711
|
const queries = makeQueryEngine(world)
|
|
@@ -723,12 +750,12 @@ const makeValidatedRuntime = <
|
|
|
723
750
|
const pendingCommands: Array<Command.DeferredCommand<S>> = []
|
|
724
751
|
|
|
725
752
|
/**
|
|
726
|
-
* Debug state. With `debug
|
|
753
|
+
* Debug state. With `debug`, `pendingOrigins` names the system that
|
|
727
754
|
* queued each pending command, in step with `pendingCommands`. Tracing is
|
|
728
755
|
* on only while `observe` listeners are registered; untraced runs pay one
|
|
729
756
|
* boolean check at each system, schedule, and marker boundary.
|
|
730
757
|
*/
|
|
731
|
-
const debugEnabled = options.debug
|
|
758
|
+
const debugEnabled = options.debug !== undefined
|
|
732
759
|
const pendingOrigins: Array<string> = []
|
|
733
760
|
const traceListeners = new Set<(event: Debug.TraceEvent) => void>()
|
|
734
761
|
let tracing = false
|
|
@@ -740,7 +767,9 @@ const makeValidatedRuntime = <
|
|
|
740
767
|
const scheduleNames = new Map<object, string>()
|
|
741
768
|
const anonymousScheduleNames = new WeakMap<object, string>()
|
|
742
769
|
let anonymousScheduleCount = 0
|
|
743
|
-
|
|
770
|
+
// Only a debug runtime times anything; the clock is the one it was given.
|
|
771
|
+
const hostClock: HostClockModule.HostClock = options.debug?.hostClock ?? { now: () => 0 }
|
|
772
|
+
const now = (): number => hostClock.now()
|
|
744
773
|
const emit = (event: Debug.TraceEvent): void => {
|
|
745
774
|
for (const listener of traceListeners) listener(event)
|
|
746
775
|
}
|
|
@@ -797,29 +826,76 @@ const makeValidatedRuntime = <
|
|
|
797
826
|
])
|
|
798
827
|
}
|
|
799
828
|
|
|
829
|
+
/**
|
|
830
|
+
* With `debug`, the relations commands have tried to relate or
|
|
831
|
+
* reorder, with the systems that queued those commands, for the
|
|
832
|
+
* `relation-failure-never-read` lint. `commandOrigin` names the system whose
|
|
833
|
+
* command is being applied.
|
|
834
|
+
*/
|
|
835
|
+
const relationMutations = new Map<string, Set<string>>()
|
|
836
|
+
let commandOrigin = "(unknown)"
|
|
837
|
+
/**
|
|
838
|
+
* With `debug`, what each system's applied commands did, as
|
|
839
|
+
* `kind:name` (`spawns:Enemy`, `despawns:Enemy` for every component the
|
|
840
|
+
* despawned entity had), and the systems that ran: for the
|
|
841
|
+
* `command-permission-unused` lint.
|
|
842
|
+
*/
|
|
843
|
+
const commandUses = new Map<string, Set<string>>()
|
|
844
|
+
const ranSystems = new Set<string>()
|
|
845
|
+
const noteCommand = (kind: string, name: string): void => {
|
|
846
|
+
let uses = commandUses.get(commandOrigin)
|
|
847
|
+
if (uses === undefined) {
|
|
848
|
+
uses = new Set()
|
|
849
|
+
commandUses.set(commandOrigin, uses)
|
|
850
|
+
}
|
|
851
|
+
uses.add(`${kind}:${name}`)
|
|
852
|
+
}
|
|
853
|
+
const noteRelationMutation = (relation: Relation.Relation.Any): void => {
|
|
854
|
+
let systems = relationMutations.get(relation.name)
|
|
855
|
+
if (systems === undefined) {
|
|
856
|
+
systems = new Set()
|
|
857
|
+
relationMutations.set(relation.name, systems)
|
|
858
|
+
}
|
|
859
|
+
systems.add(commandOrigin)
|
|
860
|
+
}
|
|
861
|
+
|
|
800
862
|
/**
|
|
801
863
|
* World adapter used by deferred commands.
|
|
802
864
|
*/
|
|
803
865
|
const commandWorld: Command.InternalWorld<S> = {
|
|
804
866
|
spawnEntity(id, components) {
|
|
867
|
+
if (debugEnabled) for (const [descriptor] of components) noteCommand("spawns", descriptor.name)
|
|
805
868
|
world.spawnEntity(id, components)
|
|
806
869
|
},
|
|
807
870
|
destroyEntity(id) {
|
|
871
|
+
if (debugEnabled) {
|
|
872
|
+
const record = world.records.get(id)
|
|
873
|
+
record?.values.forEach((value, ordinal) => {
|
|
874
|
+
if (value !== ABSENT) noteCommand("despawns", world.descriptorAt(ordinal).name)
|
|
875
|
+
})
|
|
876
|
+
}
|
|
808
877
|
world.destroyEntity(id)
|
|
809
878
|
},
|
|
810
879
|
assignEntityScope(id, scope) {
|
|
811
880
|
world.assignEntityScope(id, scope.key)
|
|
812
881
|
},
|
|
813
882
|
destroyEntityScope(scope) {
|
|
883
|
+
if (debugEnabled) noteCommand("despawnsScope", scope.name)
|
|
814
884
|
world.destroyEntityScope(scope.key)
|
|
815
885
|
},
|
|
816
886
|
removeComponent(id, descriptor) {
|
|
887
|
+
if (debugEnabled) noteCommand("removes", descriptor.name)
|
|
817
888
|
world.removeComponent(id, descriptor)
|
|
818
889
|
},
|
|
819
890
|
writeComponent(id, descriptor, value) {
|
|
891
|
+
if (debugEnabled) noteCommand("inserts", descriptor.name)
|
|
820
892
|
world.writeComponent(id, descriptor, value)
|
|
821
893
|
},
|
|
822
894
|
tryRelate(id, relation, target) {
|
|
895
|
+
if (debugEnabled) {
|
|
896
|
+
noteRelationMutation(relation)
|
|
897
|
+
noteCommand("relates", relation.name)
|
|
898
|
+
}
|
|
823
899
|
const result = world.tryRelate(id, relation, target)
|
|
824
900
|
if (!result.ok) {
|
|
825
901
|
appendRelationFailure(relation, "relate", id, target, result.error)
|
|
@@ -827,9 +903,14 @@ const makeValidatedRuntime = <
|
|
|
827
903
|
return result
|
|
828
904
|
},
|
|
829
905
|
unrelate(id, relation) {
|
|
906
|
+
if (debugEnabled) noteCommand("relates", relation.name)
|
|
830
907
|
world.unrelate(id, relation)
|
|
831
908
|
},
|
|
832
909
|
reorderChildren(id, relation, children) {
|
|
910
|
+
if (debugEnabled) {
|
|
911
|
+
noteRelationMutation(relation)
|
|
912
|
+
noteCommand("relates", relation.name)
|
|
913
|
+
}
|
|
833
914
|
const result = world.reorderChildren(id, relation, children.map((child) => child))
|
|
834
915
|
if (!result.ok) {
|
|
835
916
|
const targetId = result.error._tag === "MissingChildEntity"
|
|
@@ -882,12 +963,6 @@ const makeValidatedRuntime = <
|
|
|
882
963
|
|
|
883
964
|
const makeLookup = (reader: ReaderState): LookupApi<S, Root> => {
|
|
884
965
|
const lookup: LookupApi<S, Root> = {
|
|
885
|
-
get(target, query) {
|
|
886
|
-
return resolve(target, query, reader)
|
|
887
|
-
},
|
|
888
|
-
getHandle(handle, query) {
|
|
889
|
-
return resolve(handle, query, reader)
|
|
890
|
-
},
|
|
891
966
|
related: relatedTarget,
|
|
892
967
|
relatedSources(target, relation) {
|
|
893
968
|
if (!world.records.has(target)) {
|
|
@@ -895,19 +970,6 @@ const makeValidatedRuntime = <
|
|
|
895
970
|
}
|
|
896
971
|
return Result.success(world.relatedSourceIds(relation, target).map(entityId))
|
|
897
972
|
},
|
|
898
|
-
childMatches(target, relation, query) {
|
|
899
|
-
if (!world.records.has(target)) {
|
|
900
|
-
return Result.failure(Relation.missingEntityError(target))
|
|
901
|
-
}
|
|
902
|
-
const matches: Array<QueryMatch<S, typeof query>> = []
|
|
903
|
-
for (const sourceId of world.relatedSourceIds(relation, target)) {
|
|
904
|
-
const resolved = resolve(sourceId, query, reader)
|
|
905
|
-
if (resolved.ok) {
|
|
906
|
-
matches.push(resolved.value)
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
return Result.success(matches)
|
|
910
|
-
},
|
|
911
973
|
parent: relatedTarget,
|
|
912
974
|
ancestors(target, relation) {
|
|
913
975
|
if (!world.records.has(target)) {
|
|
@@ -946,20 +1008,6 @@ const makeValidatedRuntime = <
|
|
|
946
1008
|
}
|
|
947
1009
|
return Result.success(descendants)
|
|
948
1010
|
},
|
|
949
|
-
descendantMatches(target, relation, query, options) {
|
|
950
|
-
const descendants = lookup.descendants(target, relation, options)
|
|
951
|
-
if (!descendants.ok) {
|
|
952
|
-
return descendants
|
|
953
|
-
}
|
|
954
|
-
const matches: Array<QueryMatch<S, typeof query>> = []
|
|
955
|
-
for (const descendantId of descendants.value) {
|
|
956
|
-
const resolved = resolve(descendantId, query, reader)
|
|
957
|
-
if (resolved.ok) {
|
|
958
|
-
matches.push(resolved.value)
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
return Result.success(matches)
|
|
962
|
-
},
|
|
963
1011
|
root(target, relation) {
|
|
964
1012
|
if (!world.records.has(target)) {
|
|
965
1013
|
return Result.failure(Relation.missingEntityError(target))
|
|
@@ -976,13 +1024,32 @@ const makeValidatedRuntime = <
|
|
|
976
1024
|
return lookup
|
|
977
1025
|
}
|
|
978
1026
|
|
|
979
|
-
const makeQueryHandle = <Q extends Query.Query.Any<Root>>(query: Q, reader: ReaderState): QueryHandle<S, Q> => {
|
|
1027
|
+
const makeQueryHandle = <Q extends Query.Query.Any<Root>>(query: Q, reader: ReaderState, lookup: LookupApi<S, Root>): QueryHandle<S, Q> => {
|
|
980
1028
|
const each = () => queries.each(query, reader.since) as ReadonlyArray<QueryMatch<S, Q>>
|
|
1029
|
+
const matching = (ids: ReadonlyArray<Entity.EntityId<S, Root>>): Array<QueryMatch<S, Q>> => {
|
|
1030
|
+
const matches: Array<QueryMatch<S, Q>> = []
|
|
1031
|
+
for (const id of ids) {
|
|
1032
|
+
const resolved = resolve(id, query, reader)
|
|
1033
|
+
if (resolved.ok) matches.push(resolved.value)
|
|
1034
|
+
}
|
|
1035
|
+
return matches
|
|
1036
|
+
}
|
|
981
1037
|
return {
|
|
982
1038
|
each,
|
|
983
1039
|
get(target) {
|
|
984
1040
|
return resolve(target, query, reader)
|
|
985
1041
|
},
|
|
1042
|
+
getHandle(handle) {
|
|
1043
|
+
return resolve(handle as Entity.Handle<Root, any>, query, reader)
|
|
1044
|
+
},
|
|
1045
|
+
children(target, relation) {
|
|
1046
|
+
if (!world.records.has(target)) return Result.failure(Relation.missingEntityError(target))
|
|
1047
|
+
return Result.success(matching(world.relatedSourceIds(relation, target).map(entityId)))
|
|
1048
|
+
},
|
|
1049
|
+
descendants(target, relation, options) {
|
|
1050
|
+
const ids = lookup.descendants(target, relation, options)
|
|
1051
|
+
return ids.ok ? Result.success(matching(ids.value)) : ids
|
|
1052
|
+
},
|
|
986
1053
|
single() {
|
|
987
1054
|
const matches = each()
|
|
988
1055
|
if (matches.length === 0) {
|
|
@@ -1115,6 +1182,9 @@ const makeValidatedRuntime = <
|
|
|
1115
1182
|
return {
|
|
1116
1183
|
all() {
|
|
1117
1184
|
return world.removedSince(ordinal, reader.since).map(entityId)
|
|
1185
|
+
},
|
|
1186
|
+
lagged() {
|
|
1187
|
+
return world.removedLagged(ordinal, reader.since, reader.registeredAt)
|
|
1118
1188
|
}
|
|
1119
1189
|
}
|
|
1120
1190
|
}
|
|
@@ -1122,6 +1192,9 @@ const makeValidatedRuntime = <
|
|
|
1122
1192
|
const makeDespawnedReadView = (reader: ReaderState): DespawnedReadView<S, Root> => ({
|
|
1123
1193
|
all() {
|
|
1124
1194
|
return world.despawnedSince(reader.since).map(entityId)
|
|
1195
|
+
},
|
|
1196
|
+
lagged() {
|
|
1197
|
+
return world.despawnedLagged(reader.since, reader.registeredAt)
|
|
1125
1198
|
}
|
|
1126
1199
|
})
|
|
1127
1200
|
|
|
@@ -1199,12 +1272,13 @@ const makeValidatedRuntime = <
|
|
|
1199
1272
|
* views the implementation is allowed to see. Views read live storage, so
|
|
1200
1273
|
* one context is built per system and reused for every run.
|
|
1201
1274
|
*/
|
|
1202
|
-
const makeContext = (system: SystemDefinition<any, any, any>, reader: ReaderState): SystemContext<any> => {
|
|
1275
|
+
const makeContext = (system: SystemDefinition<any, any, any>, reader: ReaderState, queue: Command.CommandQueue<S, Root>): SystemContext<any> => {
|
|
1203
1276
|
const spec = system.spec
|
|
1277
|
+
const lookup = makeLookup(reader)
|
|
1204
1278
|
return {
|
|
1205
1279
|
local: (spec.local as (() => unknown) | undefined)?.(),
|
|
1206
|
-
queries: mapRecord(spec.queries as Record<string, Query.Query.Any<Root>>, (query) => makeQueryHandle(query, reader)),
|
|
1207
|
-
lookup
|
|
1280
|
+
queries: mapRecord(spec.queries as Record<string, Query.Query.Any<Root>>, (query) => makeQueryHandle(query, reader, lookup)),
|
|
1281
|
+
lookup,
|
|
1208
1282
|
resources: mapRecord(spec.resources as Record<string, any>, (access) =>
|
|
1209
1283
|
access.mode === "read"
|
|
1210
1284
|
? Cells.storeRead(resources, access.descriptor.key)
|
|
@@ -1222,12 +1296,15 @@ const makeValidatedRuntime = <
|
|
|
1222
1296
|
relationFailures: mapRecord(spec.relationFailures as Record<string, any>, (access) => makeRelationFailureReadView(access.relation, reader)),
|
|
1223
1297
|
services: mapRecord(spec.services as Record<string, any>, (access) =>
|
|
1224
1298
|
providedServices[access.descriptor.name as keyof Services]),
|
|
1225
|
-
|
|
1299
|
+
// The queue serves every declared permission set; the spec's types narrow it.
|
|
1300
|
+
commands: queue as unknown
|
|
1226
1301
|
} as SystemContext<any>
|
|
1227
1302
|
}
|
|
1228
1303
|
|
|
1229
1304
|
interface SystemSlot {
|
|
1230
1305
|
readonly context: SystemContext<any>
|
|
1306
|
+
/** The system's command queue (its context's `commands`, unchecked). */
|
|
1307
|
+
readonly commands: Command.CommandQueue<S, Root>
|
|
1231
1308
|
readonly reader: ReaderState
|
|
1232
1309
|
}
|
|
1233
1310
|
|
|
@@ -1316,7 +1393,7 @@ const makeValidatedRuntime = <
|
|
|
1316
1393
|
system: system.name,
|
|
1317
1394
|
outcome,
|
|
1318
1395
|
...(outcome === "ok" ? {} : { error }),
|
|
1319
|
-
ms: now()
|
|
1396
|
+
ms: HostClockModule.elapsed(started, now()),
|
|
1320
1397
|
writes,
|
|
1321
1398
|
resources: resourceWrites,
|
|
1322
1399
|
events: emitted,
|
|
@@ -1383,7 +1460,8 @@ const makeValidatedRuntime = <
|
|
|
1383
1460
|
let slot = slots.get(stateKey(system))
|
|
1384
1461
|
if (!slot) {
|
|
1385
1462
|
const reader: ReaderState = { since: 0, lastRun: 0, streamSince: 0, streamLastRun: 0, registeredAt: world.currentTick() }
|
|
1386
|
-
|
|
1463
|
+
const commands = Command.makeCommands<S, Root>(() => world.allocateEntity() as Entity.EntityId<S, Root>)
|
|
1464
|
+
slot = { context: makeContext(system, reader, commands), commands, reader }
|
|
1387
1465
|
slots.set(stateKey(system), slot)
|
|
1388
1466
|
if (holdsStreams) registerStreamReader(system, slot.reader)
|
|
1389
1467
|
const make = system.spec.local as (() => unknown) | undefined
|
|
@@ -1422,7 +1500,8 @@ const makeValidatedRuntime = <
|
|
|
1422
1500
|
return succeeded
|
|
1423
1501
|
}
|
|
1424
1502
|
}
|
|
1425
|
-
const { context, reader } = slotOf(system, true)
|
|
1503
|
+
const { context, commands, reader } = slotOf(system, true)
|
|
1504
|
+
if (debugEnabled) ranSystems.add(system.name)
|
|
1426
1505
|
// Changes stamped after the previous completed run are visible to this run.
|
|
1427
1506
|
reader.since = reader.lastRun
|
|
1428
1507
|
reader.streamSince = reader.streamLastRun
|
|
@@ -1431,23 +1510,21 @@ const makeValidatedRuntime = <
|
|
|
1431
1510
|
beginSystemTransaction()
|
|
1432
1511
|
let outcome: Result.Result<unknown, unknown>
|
|
1433
1512
|
try {
|
|
1434
|
-
// A system that returns nothing cannot fail
|
|
1435
|
-
|
|
1436
|
-
const effect = system.run(context)
|
|
1437
|
-
outcome = effect === undefined ? succeeded : effect.run(context.services)
|
|
1513
|
+
// A system that returns nothing cannot fail; otherwise it returned a `Result`.
|
|
1514
|
+
outcome = system.run(context) ?? succeeded
|
|
1438
1515
|
} catch (defect) {
|
|
1439
|
-
if (tracing) emit(traceSystem(system, reader, thisRun, started, "defect", defect,
|
|
1516
|
+
if (tracing) emit(traceSystem(system, reader, thisRun, started, "defect", defect, commands.flush()))
|
|
1440
1517
|
rollbackSystemTransaction()
|
|
1441
|
-
|
|
1518
|
+
commands.flush()
|
|
1442
1519
|
throw defect
|
|
1443
1520
|
}
|
|
1444
1521
|
if (!outcome.ok) {
|
|
1445
|
-
if (tracing) emit(traceSystem(system, reader, thisRun, started, "failed", outcome.error,
|
|
1522
|
+
if (tracing) emit(traceSystem(system, reader, thisRun, started, "failed", outcome.error, commands.flush()))
|
|
1446
1523
|
rollbackSystemTransaction()
|
|
1447
|
-
|
|
1524
|
+
commands.flush()
|
|
1448
1525
|
return Result.failure({ kind: "SystemFailure", system: system.name, error: outcome.error })
|
|
1449
1526
|
}
|
|
1450
|
-
const queued =
|
|
1527
|
+
const queued = commands.flush()
|
|
1451
1528
|
const traced = tracing ? traceSystem(system, reader, thisRun, started, "ok", undefined, queued) : undefined
|
|
1452
1529
|
commitSystemTransaction()
|
|
1453
1530
|
// A failed run leaves `lastRun` unchanged, so the next run sees the same changes again.
|
|
@@ -1469,8 +1546,15 @@ const makeValidatedRuntime = <
|
|
|
1469
1546
|
const origins = pendingOrigins.splice(0, pendingOrigins.length)
|
|
1470
1547
|
world.advanceTick()
|
|
1471
1548
|
if (!tracing) {
|
|
1472
|
-
|
|
1473
|
-
command
|
|
1549
|
+
if (!debugEnabled) {
|
|
1550
|
+
for (const command of commands) {
|
|
1551
|
+
command.apply(commandWorld)
|
|
1552
|
+
}
|
|
1553
|
+
return
|
|
1554
|
+
}
|
|
1555
|
+
for (let index = 0; index < commands.length; index++) {
|
|
1556
|
+
commandOrigin = origins[index] ?? "(unknown)"
|
|
1557
|
+
commands[index]!.apply(commandWorld)
|
|
1474
1558
|
}
|
|
1475
1559
|
return
|
|
1476
1560
|
}
|
|
@@ -1479,6 +1563,7 @@ const makeValidatedRuntime = <
|
|
|
1479
1563
|
const command = commands[index]!
|
|
1480
1564
|
const effects: Array<Debug.Effect> = []
|
|
1481
1565
|
commandEffects = effects
|
|
1566
|
+
commandOrigin = origins[index] ?? "(unknown)"
|
|
1482
1567
|
try {
|
|
1483
1568
|
command.apply(commandWorld)
|
|
1484
1569
|
} finally {
|
|
@@ -1503,7 +1588,7 @@ const makeValidatedRuntime = <
|
|
|
1503
1588
|
} finally {
|
|
1504
1589
|
schedulePath.pop()
|
|
1505
1590
|
}
|
|
1506
|
-
emit({ type: "schedule.end", frame: frameCount, schedule: scheduleLabel, ok: result.ok, ms: now()
|
|
1591
|
+
emit({ type: "schedule.end", frame: frameCount, schedule: scheduleLabel, ok: result.ok, ms: HostClockModule.elapsed(started, now()) })
|
|
1507
1592
|
return result
|
|
1508
1593
|
}
|
|
1509
1594
|
|
|
@@ -1661,7 +1746,7 @@ const makeValidatedRuntime = <
|
|
|
1661
1746
|
* Runs one schedule. A failing system stops the schedule; systems that
|
|
1662
1747
|
* already succeeded stay committed, and their pending work stays pending.
|
|
1663
1748
|
*/
|
|
1664
|
-
const runScheduleUnsafe = (schedule: ExecutableScheduleDefinition<S, any, any, any, any>): Result.Result<void, SystemFailure> => {
|
|
1749
|
+
const runScheduleUnsafe = (schedule: ExecutableScheduleDefinition<S, any, any, any, any, AnyScheduleOrder>): Result.Result<void, SystemFailure> => {
|
|
1665
1750
|
changedMachines = new Set()
|
|
1666
1751
|
return tracing
|
|
1667
1752
|
? traceSchedule(scheduleName(schedule), () => runSteps(schedule.steps))
|
|
@@ -1670,6 +1755,13 @@ const makeValidatedRuntime = <
|
|
|
1670
1755
|
|
|
1671
1756
|
const eventsByKey = new Set((Object.values(options.schema.events) as ReadonlyArray<Descriptor.Any>).map((descriptor) => descriptor.key))
|
|
1672
1757
|
const externallyWrittenEvents = new Set<string>()
|
|
1758
|
+
/** `debug.observeHost` listeners: ticks and sends, without tracing. Only a debug runtime registers any. */
|
|
1759
|
+
const hostListeners = new Set<(event: Debug.HostEvent<S, Root>) => void>()
|
|
1760
|
+
const emitHost = (event: Debug.HostEvent<S, Root>): void => {
|
|
1761
|
+
for (const listener of hostListeners) listener(event)
|
|
1762
|
+
}
|
|
1763
|
+
/** Events declared as sent by the host (`debug.declareHostEvents`). */
|
|
1764
|
+
let hostEvents = new Set<string>()
|
|
1673
1765
|
|
|
1674
1766
|
const send = (descriptor: Descriptor<"event", string, any>, value: unknown): void => {
|
|
1675
1767
|
if (ticking) throw new Error(`runtime.send(${descriptor.name}) was called while a schedule is running: emit from a system instead`)
|
|
@@ -1679,6 +1771,7 @@ const makeValidatedRuntime = <
|
|
|
1679
1771
|
// Bound host traffic even when it sends repeatedly without ticking.
|
|
1680
1772
|
events.trim(0)
|
|
1681
1773
|
externallyWrittenEvents.add(descriptor.name)
|
|
1774
|
+
if (hostListeners.size > 0) emitHost({ type: "send", event: descriptor.name, value })
|
|
1682
1775
|
if (tracing) emit({ type: "runtime.sent", frame: frameCount, tick: published, event: descriptor.name, value })
|
|
1683
1776
|
}
|
|
1684
1777
|
|
|
@@ -1691,21 +1784,91 @@ const makeValidatedRuntime = <
|
|
|
1691
1784
|
relationFailures.trim(boundary)
|
|
1692
1785
|
}
|
|
1693
1786
|
|
|
1694
|
-
|
|
1787
|
+
/**
|
|
1788
|
+
* With debug: the last failed tick result, until its `ok` is read. The
|
|
1789
|
+
* result handed back is a copy whose `ok` getter marks it read.
|
|
1790
|
+
*/
|
|
1791
|
+
let unread: { read: boolean; readonly event: Omit<Debug.FailureIgnoredEvent, "type" | "when"> } | undefined
|
|
1792
|
+
const reportUnread = (when: Debug.FailureIgnoredEvent["when"]): void => {
|
|
1793
|
+
const pending = unread
|
|
1794
|
+
unread = undefined
|
|
1795
|
+
if (pending === undefined || pending.read) return
|
|
1796
|
+
const event: Debug.FailureIgnoredEvent = { type: "failure.ignored", ...pending.event, when }
|
|
1797
|
+
emit(event)
|
|
1798
|
+
emitHost(event)
|
|
1799
|
+
}
|
|
1800
|
+
const watchResult = <R extends Result.Result<void, unknown>>(
|
|
1801
|
+
result: R,
|
|
1802
|
+
schedules: ReadonlyArray<ExecutableScheduleDefinition<S, any, any, any, any, AnyScheduleOrder>>
|
|
1803
|
+
): R => {
|
|
1804
|
+
if (!debugEnabled || result.ok) return result
|
|
1805
|
+
const error = result.error
|
|
1806
|
+
const system = typeof error === "object" && error !== null && (error as { readonly kind?: unknown }).kind === "SystemFailure"
|
|
1807
|
+
? (error as SystemFailure).system
|
|
1808
|
+
: undefined
|
|
1809
|
+
const names = schedules.flatMap((schedule) => schedule.steps.filter(Schedule.isSystemStep).map((step) => (step as SystemDefinition<any, any, any>).name))
|
|
1810
|
+
const at = system === undefined ? -1 : names.indexOf(system)
|
|
1811
|
+
const holder = system === undefined
|
|
1812
|
+
? schedules[0]
|
|
1813
|
+
: schedules.find((schedule) => schedule.steps.some((step) => Schedule.isSystemStep(step) && (step as SystemDefinition<any, any, any>).name === system)) ?? schedules[0]
|
|
1814
|
+
const entry = {
|
|
1815
|
+
read: false,
|
|
1816
|
+
event: {
|
|
1817
|
+
frame: frameCount,
|
|
1818
|
+
schedule: holder === undefined ? "(none)" : scheduleName(holder),
|
|
1819
|
+
system,
|
|
1820
|
+
error,
|
|
1821
|
+
skipped: [...new Set(names.slice(at + 1))]
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
unread = entry
|
|
1825
|
+
return {
|
|
1826
|
+
get ok() {
|
|
1827
|
+
entry.read = true
|
|
1828
|
+
return false
|
|
1829
|
+
},
|
|
1830
|
+
error
|
|
1831
|
+
} as unknown as R
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
const tickUnsafe = (schedules: ReadonlyArray<ExecutableScheduleDefinition<S, any, any, any, any, AnyScheduleOrder>>): Result.Result<void, SystemFailure> => {
|
|
1695
1835
|
if (ticking) throw new Error("runtime.tick cannot run inside another tick")
|
|
1836
|
+
if (debugEnabled) reportUnread("next-tick")
|
|
1696
1837
|
ticking = true
|
|
1697
1838
|
try {
|
|
1698
1839
|
advanceFrame()
|
|
1699
|
-
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1840
|
+
return watchResult(observedTick(schedules, () => {
|
|
1841
|
+
for (const schedule of schedules) {
|
|
1842
|
+
const result = runScheduleUnsafe(schedule)
|
|
1843
|
+
if (!result.ok) return result
|
|
1844
|
+
}
|
|
1845
|
+
return succeeded
|
|
1846
|
+
}), schedules)
|
|
1704
1847
|
} finally {
|
|
1705
1848
|
ticking = false
|
|
1706
1849
|
}
|
|
1707
1850
|
}
|
|
1708
1851
|
|
|
1852
|
+
/** Runs one tick's schedules, telling `observeHost` listeners when it starts and how it ends. */
|
|
1853
|
+
const observedTick = (
|
|
1854
|
+
schedules: ReadonlyArray<ExecutableScheduleDefinition<S, any, any, any, any, AnyScheduleOrder>>,
|
|
1855
|
+
run: () => Result.Result<void, SystemFailure>
|
|
1856
|
+
): Result.Result<void, SystemFailure> => {
|
|
1857
|
+
if (hostListeners.size === 0) return run()
|
|
1858
|
+
emitHost({ type: "tick.start", schedules, frame: frameCount })
|
|
1859
|
+
let result: Result.Result<void, SystemFailure>
|
|
1860
|
+
try {
|
|
1861
|
+
result = run()
|
|
1862
|
+
} catch (defect) {
|
|
1863
|
+
emitHost({ type: "tick.end", schedules, frame: frameCount, outcome: "defect", error: defect })
|
|
1864
|
+
throw defect
|
|
1865
|
+
}
|
|
1866
|
+
emitHost(result.ok
|
|
1867
|
+
? { type: "tick.end", schedules, frame: frameCount, outcome: "ok" }
|
|
1868
|
+
: { type: "tick.end", schedules, frame: frameCount, outcome: "failed", error: result.error })
|
|
1869
|
+
return result
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1709
1872
|
const hasRequirement = (requirement: Requirement.RequirementValue): boolean => {
|
|
1710
1873
|
switch (requirement.kind) {
|
|
1711
1874
|
case "service":
|
|
@@ -1717,22 +1880,28 @@ const makeValidatedRuntime = <
|
|
|
1717
1880
|
}
|
|
1718
1881
|
}
|
|
1719
1882
|
|
|
1720
|
-
const tryRunSchedule = <const Selected extends ExecutableScheduleDefinition<S, any, any, any, any>>(
|
|
1883
|
+
const tryRunSchedule = <const Selected extends ExecutableScheduleDefinition<S, any, any, any, any, AnyScheduleOrder>>(
|
|
1721
1884
|
schedule: Selected
|
|
1722
|
-
): Result.Result<void, MissingRuntimeRequirements | Schedule.FailureOf<Selected>> => {
|
|
1885
|
+
): Result.Result<void, MissingRuntimeRequirements | TickOrderViolation | Schedule.FailureOf<Selected>> => {
|
|
1886
|
+
if (debugEnabled) reportUnread("next-tick")
|
|
1723
1887
|
const missing = schedule.requirements
|
|
1724
1888
|
.filter((requirement) => !hasRequirement(requirement))
|
|
1725
1889
|
.map(({ kind, name }) => ({ kind, name }))
|
|
1726
1890
|
|
|
1727
1891
|
if (missing.length > 0) {
|
|
1728
|
-
return Result.failure({
|
|
1729
|
-
kind: "MissingRuntimeRequirements",
|
|
1892
|
+
return watchResult(Result.failure({
|
|
1893
|
+
kind: "MissingRuntimeRequirements" as const,
|
|
1730
1894
|
requirements: missing
|
|
1731
|
-
})
|
|
1895
|
+
}), [schedule])
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
const issues = OrderInternal.tickOrderIssues(schedule.steps)
|
|
1899
|
+
if (issues.length > 0) {
|
|
1900
|
+
return watchResult(Result.failure({ kind: "TickOrderViolation" as const, issues }), [schedule])
|
|
1732
1901
|
}
|
|
1733
1902
|
|
|
1734
1903
|
advanceFrame()
|
|
1735
|
-
return runScheduleUnsafe(schedule) as Result.Result<void, Schedule.FailureOf<Selected>>
|
|
1904
|
+
return watchResult(observedTick([schedule], () => runScheduleUnsafe(schedule)), [schedule]) as Result.Result<void, Schedule.FailureOf<Selected>>
|
|
1736
1905
|
}
|
|
1737
1906
|
|
|
1738
1907
|
/**
|
|
@@ -2041,12 +2210,18 @@ const makeValidatedRuntime = <
|
|
|
2041
2210
|
scheduleNames.clear()
|
|
2042
2211
|
for (const [name, schedule] of Object.entries(named)) scheduleNames.set(schedule, name)
|
|
2043
2212
|
},
|
|
2213
|
+
declareHostEvents(declared) {
|
|
2214
|
+
hostEvents = new Set(declared.map((descriptor) => descriptor.name))
|
|
2215
|
+
},
|
|
2044
2216
|
describe: () => DebugInternal.describe({
|
|
2045
2217
|
schema: options.schema,
|
|
2046
2218
|
machines: [...new Set(machinesByName.values())],
|
|
2047
2219
|
currentMachine: (machine) => currentMachines.get(machine.key) as Machine.StateValue | undefined,
|
|
2048
2220
|
providedServices: Object.keys(providedServices),
|
|
2049
|
-
externallyWrittenEvents,
|
|
2221
|
+
externallyWrittenEvents: new Set([...externallyWrittenEvents, ...hostEvents]),
|
|
2222
|
+
relationMutations,
|
|
2223
|
+
commandUses,
|
|
2224
|
+
ranSystems,
|
|
2050
2225
|
hasResource: (descriptor) => resources.has(descriptor.key),
|
|
2051
2226
|
schedules: [...scheduleNames].map(([schedule, name]) => [name, schedule as ExecutableScheduleDefinition<S>] as const)
|
|
2052
2227
|
}),
|
|
@@ -2063,6 +2238,13 @@ const makeValidatedRuntime = <
|
|
|
2063
2238
|
}
|
|
2064
2239
|
}
|
|
2065
2240
|
},
|
|
2241
|
+
observeHost(listener) {
|
|
2242
|
+
hostListeners.add(listener)
|
|
2243
|
+
return () => {
|
|
2244
|
+
hostListeners.delete(listener)
|
|
2245
|
+
}
|
|
2246
|
+
},
|
|
2247
|
+
checkIgnoredFailures: () => reportUnread("session-end"),
|
|
2066
2248
|
streams,
|
|
2067
2249
|
population: () => {
|
|
2068
2250
|
const components: Record<string, number> = {}
|
|
@@ -2110,7 +2292,7 @@ export const make = <
|
|
|
2110
2292
|
readonly resources?: Resources
|
|
2111
2293
|
readonly machines?: ProvidedMachines
|
|
2112
2294
|
readonly machineDefinitions?: ReadonlyArray<Machine.StateMachine.Any>
|
|
2113
|
-
/** Attaches a `debug` handle to the runtime
|
|
2295
|
+
/** Attaches a `debug` handle to the runtime, timed by `hostClock`; see the `Debug` module. */
|
|
2114
2296
|
readonly debug?: Enabled
|
|
2115
2297
|
}): MakeRuntimeResult<S, Simplify<RuntimeServicesOf<ProvidedServices>>, Resources, Root, RuntimeMachinesOf<ProvidedMachines>, Enabled> => {
|
|
2116
2298
|
const fallible = Object.entries(options.schema.resources).some(([key, descriptor]) =>
|
|
@@ -2128,7 +2310,7 @@ export const make = <
|
|
|
2128
2310
|
resources: validated.value,
|
|
2129
2311
|
machines: options.machines,
|
|
2130
2312
|
machineDefinitions: options.machineDefinitions ?? [],
|
|
2131
|
-
debug: options.debug
|
|
2313
|
+
debug: options.debug
|
|
2132
2314
|
})
|
|
2133
2315
|
return (fallible ? Result.success(runtime) : runtime) as never
|
|
2134
2316
|
}
|