@forgeax/engine-ecs 0.0.0-dev.8d955ade1c79
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/LICENSE +202 -0
- package/README.md +278 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__fixtures__/field-shape-fixture.d.ts +16 -0
- package/dist/__fixtures__/field-shape-fixture.d.ts.map +1 -0
- package/dist/__tests__/__fixtures__/inspect-scripts.snapshot.d.ts +3 -0
- package/dist/__tests__/__fixtures__/inspect-scripts.snapshot.d.ts.map +1 -0
- package/dist/__tests__/archetype.unit.test.d.ts +2 -0
- package/dist/__tests__/archetype.unit.test.d.ts.map +1 -0
- package/dist/__tests__/array-handle-element.test-d.d.ts +2 -0
- package/dist/__tests__/array-handle-element.test-d.d.ts.map +1 -0
- package/dist/__tests__/buffer-array-vocab.test-d.d.ts +2 -0
- package/dist/__tests__/buffer-array-vocab.test-d.d.ts.map +1 -0
- package/dist/__tests__/buffer-field-input-widen.unit.test.d.ts +2 -0
- package/dist/__tests__/buffer-field-input-widen.unit.test.d.ts.map +1 -0
- package/dist/__tests__/buffer-pool.test-d.d.ts +2 -0
- package/dist/__tests__/buffer-pool.test-d.d.ts.map +1 -0
- package/dist/__tests__/change-detection.unit.test.d.ts +2 -0
- package/dist/__tests__/change-detection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/command-buffer.test.d.ts +2 -0
- package/dist/__tests__/command-buffer.test.d.ts.map +1 -0
- package/dist/__tests__/commands-deferred.unit.test.d.ts +2 -0
- package/dist/__tests__/commands-deferred.unit.test.d.ts.map +1 -0
- package/dist/__tests__/component-catalog.test.d.ts +2 -0
- package/dist/__tests__/component-catalog.test.d.ts.map +1 -0
- package/dist/__tests__/component-definition-identity.unit.test.d.ts +2 -0
- package/dist/__tests__/component-definition-identity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/component-nan-preflight.unit.test.d.ts +2 -0
- package/dist/__tests__/component-nan-preflight.unit.test.d.ts.map +1 -0
- package/dist/__tests__/component-schema.test-d.d.ts +2 -0
- package/dist/__tests__/component-schema.test-d.d.ts.map +1 -0
- package/dist/__tests__/component-schema.test.d.ts +2 -0
- package/dist/__tests__/component-schema.test.d.ts.map +1 -0
- package/dist/__tests__/define-component-options.test-d.d.ts +2 -0
- package/dist/__tests__/define-component-options.test-d.d.ts.map +1 -0
- package/dist/__tests__/deprecated-vocab-rejection.unit.test.d.ts +2 -0
- package/dist/__tests__/deprecated-vocab-rejection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/despawn-all.unit.test.d.ts +2 -0
- package/dist/__tests__/despawn-all.unit.test.d.ts.map +1 -0
- package/dist/__tests__/ecs-core-reduction.characterization.test.d.ts +2 -0
- package/dist/__tests__/ecs-core-reduction.characterization.test.d.ts.map +1 -0
- package/dist/__tests__/ecs-core-reduction.perf.test.d.ts +2 -0
- package/dist/__tests__/ecs-core-reduction.perf.test.d.ts.map +1 -0
- package/dist/__tests__/ecs-error-code-31.test-d.d.ts +2 -0
- package/dist/__tests__/ecs-error-code-31.test-d.d.ts.map +1 -0
- package/dist/__tests__/entity-disabling.unit.test.d.ts +2 -0
- package/dist/__tests__/entity-disabling.unit.test.d.ts.map +1 -0
- package/dist/__tests__/entity-handle-codec-parity.unit.test.d.ts +2 -0
- package/dist/__tests__/entity-handle-codec-parity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/errors.unit.test.d.ts +2 -0
- package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-conflict-boundary.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-conflict-boundary.unit.test.d.ts.map +1 -0
- package/dist/__tests__/execution-shared-storage.unit.test.d.ts +2 -0
- package/dist/__tests__/execution-shared-storage.unit.test.d.ts.map +1 -0
- package/dist/__tests__/externalization-portable-value.unit.test.d.ts +2 -0
- package/dist/__tests__/externalization-portable-value.unit.test.d.ts.map +1 -0
- package/dist/__tests__/externalization-projection.unit.test.d.ts +2 -0
- package/dist/__tests__/externalization-projection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/externalization-render-read-lease.test-d.d.ts +2 -0
- package/dist/__tests__/externalization-render-read-lease.test-d.d.ts.map +1 -0
- package/dist/__tests__/externalization-render-read-lease.unit.test.d.ts +2 -0
- package/dist/__tests__/externalization-render-read-lease.unit.test.d.ts.map +1 -0
- package/dist/__tests__/externalization-root-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/externalization-root-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/field-enum-labels-reflection.test.d.ts +2 -0
- package/dist/__tests__/field-enum-labels-reflection.test.d.ts.map +1 -0
- package/dist/__tests__/field-shape-fixture.test.d.ts +2 -0
- package/dist/__tests__/field-shape-fixture.test.d.ts.map +1 -0
- package/dist/__tests__/field-transient-reflection.test.d.ts +2 -0
- package/dist/__tests__/field-transient-reflection.test.d.ts.map +1 -0
- package/dist/__tests__/field-value-type.test-d.d.ts +2 -0
- package/dist/__tests__/field-value-type.test-d.d.ts.map +1 -0
- package/dist/__tests__/fixed-update.unit.test.d.ts +2 -0
- package/dist/__tests__/fixed-update.unit.test.d.ts.map +1 -0
- package/dist/__tests__/handle.test-d.d.ts +2 -0
- package/dist/__tests__/handle.test-d.d.ts.map +1 -0
- package/dist/__tests__/hierarchy.unit.test.d.ts +2 -0
- package/dist/__tests__/hierarchy.unit.test.d.ts.map +1 -0
- package/dist/__tests__/managed-array-vocab.test-d.d.ts +2 -0
- package/dist/__tests__/managed-array-vocab.test-d.d.ts.map +1 -0
- package/dist/__tests__/minimal-example.test-d.d.ts +2 -0
- package/dist/__tests__/minimal-example.test-d.d.ts.map +1 -0
- package/dist/__tests__/name-read-types.test-d.d.ts +2 -0
- package/dist/__tests__/name-read-types.test-d.d.ts.map +1 -0
- package/dist/__tests__/plugin-service.test.d.ts +2 -0
- package/dist/__tests__/plugin-service.test.d.ts.map +1 -0
- package/dist/__tests__/query-combinations.unit.test.d.ts +2 -0
- package/dist/__tests__/query-combinations.unit.test.d.ts.map +1 -0
- package/dist/__tests__/query-light-extract-flat-column-ratio.perf.test.d.ts +2 -0
- package/dist/__tests__/query-light-extract-flat-column-ratio.perf.test.d.ts.map +1 -0
- package/dist/__tests__/query-storage-trends.perf.test.d.ts +2 -0
- package/dist/__tests__/query-storage-trends.perf.test.d.ts.map +1 -0
- package/dist/__tests__/query-trs-flat-column-ratio.perf.test.d.ts +2 -0
- package/dist/__tests__/query-trs-flat-column-ratio.perf.test.d.ts.map +1 -0
- package/dist/__tests__/query.unit.test.d.ts +2 -0
- package/dist/__tests__/query.unit.test.d.ts.map +1 -0
- package/dist/__tests__/relationship-index.perf.test.d.ts +2 -0
- package/dist/__tests__/relationship-index.perf.test.d.ts.map +1 -0
- package/dist/__tests__/relationship-index.test.d.ts +2 -0
- package/dist/__tests__/relationship-index.test.d.ts.map +1 -0
- package/dist/__tests__/scalar-field-type-owner.test-d.d.ts +2 -0
- package/dist/__tests__/scalar-field-type-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/schedule-determinism.unit.test.d.ts +2 -0
- package/dist/__tests__/schedule-determinism.unit.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-inspection.unit.test.d.ts +2 -0
- package/dist/__tests__/schedule-inspection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-scope-error.unit.test.d.ts +2 -0
- package/dist/__tests__/schedule-scope-error.unit.test.d.ts.map +1 -0
- package/dist/__tests__/schedule-token.unit.test.d.ts +2 -0
- package/dist/__tests__/schedule-token.unit.test.d.ts.map +1 -0
- package/dist/__tests__/schema-vocab.test-d.d.ts +2 -0
- package/dist/__tests__/schema-vocab.test-d.d.ts.map +1 -0
- package/dist/__tests__/serialization.unit.test.d.ts +2 -0
- package/dist/__tests__/serialization.unit.test.d.ts.map +1 -0
- package/dist/__tests__/shared-kernel-eligibility.test-d.d.ts +2 -0
- package/dist/__tests__/shared-kernel-eligibility.test-d.d.ts.map +1 -0
- package/dist/__tests__/shared-kernel-eligibility.unit.test.d.ts +2 -0
- package/dist/__tests__/shared-kernel-eligibility.unit.test.d.ts.map +1 -0
- package/dist/__tests__/shared-ref-store.unit.test.d.ts +2 -0
- package/dist/__tests__/shared-ref-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sparse-tag.unit.test.d.ts +2 -0
- package/dist/__tests__/sparse-tag.unit.test.d.ts.map +1 -0
- package/dist/__tests__/spawn-field-error.test-d.d.ts +2 -0
- package/dist/__tests__/spawn-field-error.test-d.d.ts.map +1 -0
- package/dist/__tests__/stale-error.test-d.d.ts +2 -0
- package/dist/__tests__/stale-error.test-d.d.ts.map +1 -0
- package/dist/__tests__/string-view-symmetry.test-d.d.ts +2 -0
- package/dist/__tests__/string-view-symmetry.test-d.d.ts.map +1 -0
- package/dist/__tests__/string-vocab.test-d.d.ts +2 -0
- package/dist/__tests__/string-vocab.test-d.d.ts.map +1 -0
- package/dist/__tests__/structure-epoch-boundaries.unit.test.d.ts +2 -0
- package/dist/__tests__/structure-epoch-boundaries.unit.test.d.ts.map +1 -0
- package/dist/__tests__/structure-epoch.unit.test.d.ts +2 -0
- package/dist/__tests__/structure-epoch.unit.test.d.ts.map +1 -0
- package/dist/__tests__/table-archetype-mapping.unit.test.d.ts +2 -0
- package/dist/__tests__/table-archetype-mapping.unit.test.d.ts.map +1 -0
- package/dist/__tests__/time.unit.test.d.ts +2 -0
- package/dist/__tests__/time.unit.test.d.ts.map +1 -0
- package/dist/__tests__/types.test-d.d.ts +2 -0
- package/dist/__tests__/types.test-d.d.ts.map +1 -0
- package/dist/__tests__/unique-ref-store.unit.test.d.ts +2 -0
- package/dist/__tests__/unique-ref-store.unit.test.d.ts.map +1 -0
- package/dist/__tests__/utils/handle-numeric.d.ts +14 -0
- package/dist/__tests__/utils/handle-numeric.d.ts.map +1 -0
- package/dist/__tests__/validation-root-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/validation-root-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-atomic-write.unit.test.d.ts +2 -0
- package/dist/__tests__/visibility-atomic-write.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-change-journal.unit.test.d.ts +2 -0
- package/dist/__tests__/world-change-journal.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-characterization.unit.test.d.ts +2 -0
- package/dist/__tests__/world-characterization.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-consumer-inference.test-d.d.ts +2 -0
- package/dist/__tests__/world-consumer-inference.test-d.d.ts.map +1 -0
- package/dist/__tests__/world-health.contract.test.d.ts +2 -0
- package/dist/__tests__/world-health.contract.test.d.ts.map +1 -0
- package/dist/__tests__/world-inspection.test.d.ts +2 -0
- package/dist/__tests__/world-inspection.test.d.ts.map +1 -0
- package/dist/__tests__/world-internal-identity.unit.test.d.ts +2 -0
- package/dist/__tests__/world-internal-identity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-managed-roundtrip.unit.test.d.ts +2 -0
- package/dist/__tests__/world-managed-roundtrip.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world-scheduling.m4.test.d.ts +2 -0
- package/dist/__tests__/world-scheduling.m4.test.d.ts.map +1 -0
- package/dist/__tests__/world-unique-roundtrip.unit.test.d.ts +2 -0
- package/dist/__tests__/world-unique-roundtrip.unit.test.d.ts.map +1 -0
- package/dist/__tests__/world.test-d.d.ts +2 -0
- package/dist/__tests__/world.test-d.d.ts.map +1 -0
- package/dist/buffer-pool.d.ts +109 -0
- package/dist/buffer-pool.d.ts.map +1 -0
- package/dist/commands.d.ts +93 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/component-default-fallback.d.ts +88 -0
- package/dist/component-default-fallback.d.ts.map +1 -0
- package/dist/component-schema.d.ts +34 -0
- package/dist/component-schema.d.ts.map +1 -0
- package/dist/component-value-validate.d.ts +29 -0
- package/dist/component-value-validate.d.ts.map +1 -0
- package/dist/component.d.ts +581 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/entity-handle.d.ts +68 -0
- package/dist/entity-handle.d.ts.map +1 -0
- package/dist/entity.d.ts +86 -0
- package/dist/entity.d.ts.map +1 -0
- package/dist/errors/query-and-component-errors.d.ts +132 -0
- package/dist/errors/query-and-component-errors.d.ts.map +1 -0
- package/dist/errors/relationship-errors.d.ts +112 -0
- package/dist/errors/relationship-errors.d.ts.map +1 -0
- package/dist/errors/sprite-and-shared-errors.d.ts +107 -0
- package/dist/errors/sprite-and-shared-errors.d.ts.map +1 -0
- package/dist/errors/validation-errors.d.ts +229 -0
- package/dist/errors/validation-errors.d.ts.map +1 -0
- package/dist/errors.d.ts +763 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/execution/shared-kernel.d.ts +106 -0
- package/dist/execution/shared-kernel.d.ts.map +1 -0
- package/dist/externalization/index.d.ts +31 -0
- package/dist/externalization/index.d.ts.map +1 -0
- package/dist/externalization/index.mjs +298 -0
- package/dist/externalization/index.mjs.map +1 -0
- package/dist/index.d.ts +261 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +7422 -0
- package/dist/index.mjs.map +1 -0
- package/dist/internal.mjs +479 -0
- package/dist/internal.mjs.map +1 -0
- package/dist/plugin-service.d.ts +11 -0
- package/dist/plugin-service.d.ts.map +1 -0
- package/dist/projection/index.d.ts +103 -0
- package/dist/projection/index.d.ts.map +1 -0
- package/dist/projection/index.mjs +615 -0
- package/dist/projection/index.mjs.map +1 -0
- package/dist/query/query.d.ts +58 -0
- package/dist/query/query.d.ts.map +1 -0
- package/dist/relationship-index.d.ts +81 -0
- package/dist/relationship-index.d.ts.map +1 -0
- package/dist/resource.d.ts +70 -0
- package/dist/resource.d.ts.map +1 -0
- package/dist/schedule-token.d.ts +16 -0
- package/dist/schedule-token.d.ts.map +1 -0
- package/dist/schedule.d.ts +271 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/shared-ref-store.d.ts +144 -0
- package/dist/shared-ref-store.d.ts.map +1 -0
- package/dist/shared.d.ts +3 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.mjs +364 -0
- package/dist/shared.mjs.map +1 -0
- package/dist/storage/archetype-graph.d.ts +54 -0
- package/dist/storage/archetype-graph.d.ts.map +1 -0
- package/dist/storage/archetype.d.ts +23 -0
- package/dist/storage/archetype.d.ts.map +1 -0
- package/dist/storage/change-detection.d.ts +40 -0
- package/dist/storage/change-detection.d.ts.map +1 -0
- package/dist/storage/column.d.ts +111 -0
- package/dist/storage/column.d.ts.map +1 -0
- package/dist/storage/table.d.ts +29 -0
- package/dist/storage/table.d.ts.map +1 -0
- package/dist/time.d.ts +57 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/unique-ref-store.d.ts +105 -0
- package/dist/unique-ref-store.d.ts.map +1 -0
- package/dist/world-change-journal.d.ts +34 -0
- package/dist/world-change-journal.d.ts.map +1 -0
- package/dist/world-component-access.d.ts +265 -0
- package/dist/world-component-access.d.ts.map +1 -0
- package/dist/world-component-storage.d.ts +282 -0
- package/dist/world-component-storage.d.ts.map +1 -0
- package/dist/world-core.d.ts +39 -0
- package/dist/world-core.d.ts.map +1 -0
- package/dist/world-entity-lifecycle.d.ts +30 -0
- package/dist/world-entity-lifecycle.d.ts.map +1 -0
- package/dist/world-internal.d.ts +14 -0
- package/dist/world-internal.d.ts.map +1 -0
- package/dist/world-scheduling.d.ts +30 -0
- package/dist/world-scheduling.d.ts.map +1 -0
- package/dist/world.d.ts +620 -0
- package/dist/world.d.ts.map +1 -0
- package/package.json +77 -0
- package/src/__fixtures__/field-shape-fixture.ts +30 -0
- package/src/__tests__/__fixtures__/inspect-scripts.snapshot.ts +2 -0
- package/src/__tests__/archetype.unit.test.ts +1837 -0
- package/src/__tests__/array-handle-element.test-d.ts +34 -0
- package/src/__tests__/buffer-array-vocab.test-d.ts +53 -0
- package/src/__tests__/buffer-field-input-widen.unit.test.ts +99 -0
- package/src/__tests__/buffer-pool.test-d.ts +32 -0
- package/src/__tests__/change-detection.unit.test.ts +86 -0
- package/src/__tests__/command-buffer.test.ts +179 -0
- package/src/__tests__/commands-deferred.unit.test.ts +68 -0
- package/src/__tests__/component-catalog.test.ts +59 -0
- package/src/__tests__/component-definition-identity.unit.test.ts +24 -0
- package/src/__tests__/component-nan-preflight.unit.test.ts +186 -0
- package/src/__tests__/component-schema.test-d.ts +119 -0
- package/src/__tests__/component-schema.test.ts +55 -0
- package/src/__tests__/define-component-options.test-d.ts +35 -0
- package/src/__tests__/deprecated-vocab-rejection.unit.test.ts +59 -0
- package/src/__tests__/despawn-all.unit.test.ts +17 -0
- package/src/__tests__/ecs-core-reduction.characterization.test.ts +130 -0
- package/src/__tests__/ecs-core-reduction.perf.test.ts +260 -0
- package/src/__tests__/ecs-error-code-31.test-d.ts +42 -0
- package/src/__tests__/entity-disabling.unit.test.ts +37 -0
- package/src/__tests__/entity-handle-codec-parity.unit.test.ts +126 -0
- package/src/__tests__/errors.unit.test.ts +1456 -0
- package/src/__tests__/execution-conflict-boundary.unit.test.ts +123 -0
- package/src/__tests__/execution-shared-storage.unit.test.ts +40 -0
- package/src/__tests__/externalization-portable-value.unit.test.ts +275 -0
- package/src/__tests__/externalization-projection.unit.test.ts +325 -0
- package/src/__tests__/externalization-render-read-lease.test-d.ts +30 -0
- package/src/__tests__/externalization-render-read-lease.unit.test.ts +79 -0
- package/src/__tests__/externalization-root-surface.unit.test.ts +33 -0
- package/src/__tests__/field-enum-labels-reflection.test.ts +66 -0
- package/src/__tests__/field-shape-fixture.test.ts +61 -0
- package/src/__tests__/field-transient-reflection.test.ts +59 -0
- package/src/__tests__/field-value-type.test-d.ts +57 -0
- package/src/__tests__/fixed-update.unit.test.ts +78 -0
- package/src/__tests__/handle.test-d.ts +86 -0
- package/src/__tests__/hierarchy.unit.test.ts +2534 -0
- package/src/__tests__/managed-array-vocab.test-d.ts +137 -0
- package/src/__tests__/minimal-example.test-d.ts +42 -0
- package/src/__tests__/name-read-types.test-d.ts +72 -0
- package/src/__tests__/plugin-service.test.ts +13 -0
- package/src/__tests__/query-combinations.unit.test.ts +42 -0
- package/src/__tests__/query-light-extract-flat-column-ratio.perf.test.ts +252 -0
- package/src/__tests__/query-storage-trends.perf.test.ts +104 -0
- package/src/__tests__/query-trs-flat-column-ratio.perf.test.ts +194 -0
- package/src/__tests__/query.unit.test.ts +196 -0
- package/src/__tests__/relationship-index.perf.test.ts +126 -0
- package/src/__tests__/relationship-index.test.ts +70 -0
- package/src/__tests__/scalar-field-type-owner.test-d.ts +71 -0
- package/src/__tests__/schedule-determinism.unit.test.ts +36 -0
- package/src/__tests__/schedule-inspection.unit.test.ts +81 -0
- package/src/__tests__/schedule-scope-error.unit.test.ts +63 -0
- package/src/__tests__/schedule-token.unit.test.ts +54 -0
- package/src/__tests__/schema-vocab.test-d.ts +103 -0
- package/src/__tests__/serialization.unit.test.ts +2740 -0
- package/src/__tests__/shared-kernel-eligibility.test-d.ts +18 -0
- package/src/__tests__/shared-kernel-eligibility.unit.test.ts +78 -0
- package/src/__tests__/shared-ref-store.unit.test.ts +646 -0
- package/src/__tests__/sparse-tag.unit.test.ts +201 -0
- package/src/__tests__/spawn-field-error.test-d.ts +41 -0
- package/src/__tests__/stale-error.test-d.ts +208 -0
- package/src/__tests__/string-view-symmetry.test-d.ts +26 -0
- package/src/__tests__/string-vocab.test-d.ts +49 -0
- package/src/__tests__/structure-epoch-boundaries.unit.test.ts +39 -0
- package/src/__tests__/structure-epoch.unit.test.ts +48 -0
- package/src/__tests__/table-archetype-mapping.unit.test.ts +78 -0
- package/src/__tests__/time.unit.test.ts +79 -0
- package/src/__tests__/types.test-d.ts +38 -0
- package/src/__tests__/unique-ref-store.unit.test.ts +348 -0
- package/src/__tests__/utils/handle-numeric.ts +30 -0
- package/src/__tests__/validation-root-surface.unit.test.ts +15 -0
- package/src/__tests__/visibility-atomic-write.unit.test.ts +82 -0
- package/src/__tests__/world-change-journal.unit.test.ts +97 -0
- package/src/__tests__/world-characterization.unit.test.ts +408 -0
- package/src/__tests__/world-consumer-inference.test-d.ts +16 -0
- package/src/__tests__/world-health.contract.test.ts +87 -0
- package/src/__tests__/world-inspection.test.ts +37 -0
- package/src/__tests__/world-internal-identity.unit.test.ts +17 -0
- package/src/__tests__/world-managed-roundtrip.unit.test.ts +98 -0
- package/src/__tests__/world-scheduling.m4.test.ts +52 -0
- package/src/__tests__/world-unique-roundtrip.unit.test.ts +357 -0
- package/src/__tests__/world.test-d.ts +156 -0
- package/src/buffer-pool.ts +357 -0
- package/src/commands.ts +644 -0
- package/src/component-default-fallback.ts +236 -0
- package/src/component-schema.ts +92 -0
- package/src/component-value-validate.ts +135 -0
- package/src/component.ts +1268 -0
- package/src/entity-handle.ts +104 -0
- package/src/entity.ts +140 -0
- package/src/errors/query-and-component-errors.ts +200 -0
- package/src/errors/relationship-errors.ts +198 -0
- package/src/errors/sprite-and-shared-errors.ts +231 -0
- package/src/errors/validation-errors.ts +506 -0
- package/src/errors.ts +1464 -0
- package/src/execution/shared-kernel.ts +392 -0
- package/src/externalization/index.ts +170 -0
- package/src/index.ts +414 -0
- package/src/plugin-service.ts +35 -0
- package/src/projection/index.ts +318 -0
- package/src/query/query.ts +606 -0
- package/src/relationship-index.ts +175 -0
- package/src/resource.ts +106 -0
- package/src/schedule-token.ts +40 -0
- package/src/schedule.ts +846 -0
- package/src/shared-ref-store.ts +418 -0
- package/src/shared.ts +23 -0
- package/src/storage/archetype-graph.ts +166 -0
- package/src/storage/archetype.ts +78 -0
- package/src/storage/change-detection.ts +231 -0
- package/src/storage/column.ts +247 -0
- package/src/storage/table.ts +153 -0
- package/src/time.ts +122 -0
- package/src/unique-ref-store.ts +278 -0
- package/src/world-change-journal.ts +84 -0
- package/src/world-component-access.ts +1421 -0
- package/src/world-component-storage.ts +1178 -0
- package/src/world-core.ts +74 -0
- package/src/world-entity-lifecycle.ts +539 -0
- package/src/world-internal.ts +75 -0
- package/src/world-scheduling.ts +499 -0
- package/src/world.ts +1399 -0
package/src/errors.ts
ADDED
|
@@ -0,0 +1,1464 @@
|
|
|
1
|
+
// @forgeax/engine-ecs — typed error class collection.
|
|
2
|
+
//
|
|
3
|
+
// Typed error classes covering all boundary conditions. Each follows progressive
|
|
4
|
+
// disclosure format: one-line summary → context fields → hint fix suggestion.
|
|
5
|
+
// Each exposes a `.hint` readonly property for programmatic extraction.
|
|
6
|
+
|
|
7
|
+
import type { QuerySpanUnavailableReason } from './errors/query-and-component-errors';
|
|
8
|
+
|
|
9
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
// Re-exports from split error sub-files (w3-b — package cohesion split)
|
|
11
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
ComponentNotDefinedError,
|
|
15
|
+
QueryDataRequiresFieldsError,
|
|
16
|
+
QueryDescriptorConflictError,
|
|
17
|
+
QueryIterationActiveError,
|
|
18
|
+
QueryIterationInvalidatedError,
|
|
19
|
+
QuerySpanUnavailableError,
|
|
20
|
+
type QuerySpanUnavailableReason,
|
|
21
|
+
RemoveEssentialComponentError,
|
|
22
|
+
SpawnDataUnknownFieldError,
|
|
23
|
+
} from './errors/query-and-component-errors';
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
RelationshipDetachMismatchError,
|
|
27
|
+
RelationshipMirrorComponentNotRegisteredError,
|
|
28
|
+
RelationshipMirrorFieldTypeMismatchError,
|
|
29
|
+
RelationshipSelfCycleError,
|
|
30
|
+
RelationshipTargetReadonlyError,
|
|
31
|
+
} from './errors/relationship-errors';
|
|
32
|
+
export {
|
|
33
|
+
SharedFieldInvalidValueError,
|
|
34
|
+
SpriteInstancesCountMismatchError,
|
|
35
|
+
SpriteInstancesMutuallyExclusiveWithInstancesError,
|
|
36
|
+
SpriteInstancesRequiresSpriteShaderError,
|
|
37
|
+
} from './errors/sprite-and-shared-errors';
|
|
38
|
+
export {
|
|
39
|
+
ComponentFieldInvalidValueError,
|
|
40
|
+
ComponentNumericValueInvalidError,
|
|
41
|
+
ManagedArrayInvalidValueError,
|
|
42
|
+
ResourceInvalidValueError,
|
|
43
|
+
ScheduleScopeMismatchError,
|
|
44
|
+
SpawnLightInvalidBoundsError,
|
|
45
|
+
SpriteAnimationInvalidError,
|
|
46
|
+
TimeConfigInvalidError,
|
|
47
|
+
TimeDeltaInvalidError,
|
|
48
|
+
validateEnumFieldValues,
|
|
49
|
+
validateNumericFieldValues,
|
|
50
|
+
} from './errors/validation-errors';
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
SharedKernelEligibilityError,
|
|
54
|
+
SharedKernelFailureError,
|
|
55
|
+
WorldPoisonedError,
|
|
56
|
+
} from './execution/shared-kernel';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Thrown when an attempt is made to encode an entity index that does not fit
|
|
60
|
+
* in 24 bits (i.e. >= 2^24 = 16_777_216).
|
|
61
|
+
*
|
|
62
|
+
* `.code = 'entity-index-overflow'`
|
|
63
|
+
* `.hint` — suggests reducing entity count or investigating leaks.
|
|
64
|
+
*/
|
|
65
|
+
export class EntityIndexOverflowError extends RangeError {
|
|
66
|
+
override readonly name = 'EntityIndexOverflowError';
|
|
67
|
+
readonly code = 'entity-index-overflow' as const;
|
|
68
|
+
readonly hint: string;
|
|
69
|
+
|
|
70
|
+
constructor(index: number) {
|
|
71
|
+
const hint =
|
|
72
|
+
'Entity index exceeds 24-bit max (16777215). Reduce simultaneous entity count or investigate entity leaks.';
|
|
73
|
+
super(
|
|
74
|
+
`Entity index ${index} exceeds 24-bit max (16777215).\n` +
|
|
75
|
+
` index: ${index}\n` +
|
|
76
|
+
` hint: ${hint}`,
|
|
77
|
+
);
|
|
78
|
+
this.hint = hint;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Thrown when `defineComponent` is given a field type that is not in the
|
|
84
|
+
* supported scalar field type set.
|
|
85
|
+
*
|
|
86
|
+
* `.code = 'schema-unsupported-field'`
|
|
87
|
+
* `.hint` — lists all supported scalar field types.
|
|
88
|
+
*/
|
|
89
|
+
export class SchemaUnsupportedFieldError extends Error {
|
|
90
|
+
override readonly name = 'SchemaUnsupportedFieldError';
|
|
91
|
+
readonly code = 'schema-unsupported-field' as const;
|
|
92
|
+
readonly hint: string;
|
|
93
|
+
|
|
94
|
+
constructor(fieldName: string, fieldType: string) {
|
|
95
|
+
let hint = 'Supported types: f32 / f64 / i32 / u32 / i16 / u16 / i8 / u8 / bool / enum / ref.';
|
|
96
|
+
// feat-20260614 M1 / M5: explicit migration hints for the two
|
|
97
|
+
// retired schema-vocab keyword families. Both renames preserve brand
|
|
98
|
+
// and storage layout (u32 column); only the keyword + dispatch arm
|
|
99
|
+
// changed. AI users hitting either literal land directly on the new
|
|
100
|
+
// keyword instead of grepping for the rename note (charter F1
|
|
101
|
+
// single-entry indexability).
|
|
102
|
+
if (fieldType.startsWith('handle<') && fieldType.endsWith('>')) {
|
|
103
|
+
const tag = fieldType.slice(7, -1);
|
|
104
|
+
hint = `'handle<${tag}>' was removed in feat-20260614 M5; use 'shared<${tag}>' instead. The brand and storage layout are unchanged; only the keyword + write-barrier dispatch (SharedRefStore retain/release) is new.`;
|
|
105
|
+
} else if (fieldType.startsWith('ref<') && fieldType.endsWith('>')) {
|
|
106
|
+
const tag = fieldType.slice(4, -1);
|
|
107
|
+
hint = `'ref<${tag}>' was renamed in feat-20260614 M1; use 'unique<${tag}>' instead. The brand and storage layout are unchanged; the dispatch still routes through UniqueRefStore (single-holder direct release).`;
|
|
108
|
+
}
|
|
109
|
+
super(
|
|
110
|
+
`Schema field "${fieldName}" has unsupported type "${fieldType}".\n` +
|
|
111
|
+
` field: ${fieldName}\n` +
|
|
112
|
+
` type: ${fieldType}\n` +
|
|
113
|
+
` hint: ${hint}`,
|
|
114
|
+
);
|
|
115
|
+
this.hint = hint;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class SparseStorageRequiresTagError extends Error {
|
|
120
|
+
override readonly name = 'SparseStorageRequiresTagError';
|
|
121
|
+
readonly code = 'sparse-storage-requires-tag' as const;
|
|
122
|
+
readonly expected = 'sparse components have an empty schema and no relationship metadata';
|
|
123
|
+
readonly hint = 'remove all fields and relationship metadata, or use storage: table';
|
|
124
|
+
readonly detail: { readonly componentName: string };
|
|
125
|
+
|
|
126
|
+
constructor(componentName: string) {
|
|
127
|
+
super(
|
|
128
|
+
`Sparse component "${componentName}" must be a zero-field, non-relationship tag.\n` +
|
|
129
|
+
` component: ${componentName}\n` +
|
|
130
|
+
` hint: remove all fields and relationship metadata, or use storage: table`,
|
|
131
|
+
);
|
|
132
|
+
this.detail = { componentName };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Thrown (or returned via the `Result` err branch — `r.ok === false`, `r.error`)
|
|
138
|
+
* when get/set/addComponent/removeComponent is called on an entity that has
|
|
139
|
+
* been despawned (stale handle).
|
|
140
|
+
*
|
|
141
|
+
* `.code = 'stale-entity'`
|
|
142
|
+
* `.hint` — includes operation name, expected/actual generation, and component name.
|
|
143
|
+
* Enhanced fields: `.component`, `.operation`, `.expectedGeneration`, `.actualGeneration`.
|
|
144
|
+
*/
|
|
145
|
+
export class StaleEntityError extends Error {
|
|
146
|
+
override readonly name = 'StaleEntityError';
|
|
147
|
+
readonly code = 'stale-entity' as const;
|
|
148
|
+
readonly hint: string;
|
|
149
|
+
|
|
150
|
+
/** Component name involved in the operation (undefined when the operation does not target a specific component). */
|
|
151
|
+
readonly component: string | undefined;
|
|
152
|
+
/** The component-level operation that triggered this error (e.g. 'get' / 'set' / 'add' / 'remove'). Entity-level operations like `despawn` are not surfaced here — see `EntityHandle` lifecycle errors. */
|
|
153
|
+
readonly operation: string | undefined;
|
|
154
|
+
/** The generation the caller expected (from the entity handle). */
|
|
155
|
+
readonly expectedGeneration: number | undefined;
|
|
156
|
+
/**
|
|
157
|
+
* The actual generation found in the entity pool. `-1` is the sentinel
|
|
158
|
+
* value for entities never allocated (slot was never occupied), as opposed
|
|
159
|
+
* to allocated-then-despawned entities which carry a real (incremented)
|
|
160
|
+
* generation number.
|
|
161
|
+
*/
|
|
162
|
+
readonly actualGeneration: number | undefined;
|
|
163
|
+
|
|
164
|
+
constructor(
|
|
165
|
+
entityId: number,
|
|
166
|
+
index: number,
|
|
167
|
+
generation: number,
|
|
168
|
+
enhanced?: {
|
|
169
|
+
component?: string;
|
|
170
|
+
operation: string;
|
|
171
|
+
expectedGeneration: number;
|
|
172
|
+
actualGeneration: number;
|
|
173
|
+
},
|
|
174
|
+
) {
|
|
175
|
+
const hint = enhanced
|
|
176
|
+
? `Entity was despawned. Operation "${enhanced.operation}" on entity ${entityId}` +
|
|
177
|
+
(enhanced.component ? ` (component: ${enhanced.component})` : '') +
|
|
178
|
+
` expected generation ${enhanced.expectedGeneration}, found ${enhanced.actualGeneration}.` +
|
|
179
|
+
' Check entity lifecycle before access.'
|
|
180
|
+
: 'Entity was despawned. Check entity lifecycle before access.';
|
|
181
|
+
super(
|
|
182
|
+
`Operation on stale entity handle.\n` +
|
|
183
|
+
` entity: ${entityId} (index=${index}, generation=${generation})\n` +
|
|
184
|
+
(enhanced ? ` operation: ${enhanced.operation}\n` : '') +
|
|
185
|
+
(enhanced?.component ? ` component: ${enhanced.component}\n` : '') +
|
|
186
|
+
` hint: ${hint}`,
|
|
187
|
+
);
|
|
188
|
+
this.hint = hint;
|
|
189
|
+
this.component = enhanced?.component;
|
|
190
|
+
this.operation = enhanced?.operation;
|
|
191
|
+
this.expectedGeneration = enhanced?.expectedGeneration;
|
|
192
|
+
this.actualGeneration = enhanced?.actualGeneration;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Returned via the `Result` err branch (`r.ok === false`, `r.error`) when
|
|
198
|
+
* addComponent tries to add a component that the entity already possesses.
|
|
199
|
+
*
|
|
200
|
+
* `.code = 'component-already-present'`
|
|
201
|
+
* `.hint` — suggests using `set()` to update values instead.
|
|
202
|
+
*/
|
|
203
|
+
export class ComponentAlreadyPresentError extends Error {
|
|
204
|
+
override readonly name = 'ComponentAlreadyPresentError';
|
|
205
|
+
readonly code = 'component-already-present' as const;
|
|
206
|
+
readonly hint: string;
|
|
207
|
+
|
|
208
|
+
constructor(entityId: number, componentName: string) {
|
|
209
|
+
const hint = 'Entity already has this component. Use set() to update values.';
|
|
210
|
+
super(
|
|
211
|
+
`Entity ${entityId} already has component "${componentName}".\n` +
|
|
212
|
+
` entity: ${entityId}\n` +
|
|
213
|
+
` component: ${componentName}\n` +
|
|
214
|
+
` hint: ${hint}`,
|
|
215
|
+
);
|
|
216
|
+
this.hint = hint;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Returned via the `Result` err branch (`r.ok === false`, `r.error`) when
|
|
222
|
+
* removeComponent / set is called for a component the entity does not possess.
|
|
223
|
+
*
|
|
224
|
+
* `.code = 'component-not-present'`
|
|
225
|
+
* `.hint` — suggests checking with query or inspect().
|
|
226
|
+
*/
|
|
227
|
+
export class ComponentNotPresentError extends Error {
|
|
228
|
+
override readonly name = 'ComponentNotPresentError';
|
|
229
|
+
readonly code = 'component-not-present' as const;
|
|
230
|
+
readonly hint: string;
|
|
231
|
+
|
|
232
|
+
constructor(entityId: number, componentName: string) {
|
|
233
|
+
const hint = 'Entity does not have this component. Check with query or inspect().';
|
|
234
|
+
super(
|
|
235
|
+
`Entity ${entityId} does not have component "${componentName}".\n` +
|
|
236
|
+
` entity: ${entityId}\n` +
|
|
237
|
+
` component: ${componentName}\n` +
|
|
238
|
+
` hint: ${hint}`,
|
|
239
|
+
);
|
|
240
|
+
this.hint = hint;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Thrown when DAG Schedule detects a cyclic dependency among systems.
|
|
246
|
+
*
|
|
247
|
+
* `.code = 'cyclic-dependency'`
|
|
248
|
+
* `.hint` — includes the cycle path and suggests removing one constraint.
|
|
249
|
+
* `.detail` — `{ code: 'cyclic-dependency'; cycle: readonly string[] }` contains the
|
|
250
|
+
* structured cycle path for programmatic consumption.
|
|
251
|
+
*/
|
|
252
|
+
export class CyclicDependencyError extends Error {
|
|
253
|
+
override readonly name = 'CyclicDependencyError';
|
|
254
|
+
readonly code = 'cyclic-dependency' as const;
|
|
255
|
+
readonly expected = 'the schedule dependency graph is acyclic';
|
|
256
|
+
readonly hint: string;
|
|
257
|
+
/** Structured cycle path — programmatic consumers read this, not the message. */
|
|
258
|
+
readonly detail: { readonly code: 'cyclic-dependency'; readonly cycle: readonly string[] };
|
|
259
|
+
|
|
260
|
+
constructor(cycle: readonly string[]) {
|
|
261
|
+
const cycleStr = cycle.join(' -> ');
|
|
262
|
+
const hint = `Cycle path: ${cycleStr}. Remove one ordering constraint to break the cycle.`;
|
|
263
|
+
super(`DAG Schedule has a cyclic dependency.\n cycle: ${cycleStr}\n hint: ${hint}`);
|
|
264
|
+
this.hint = hint;
|
|
265
|
+
this.detail = { code: 'cyclic-dependency' as const, cycle };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Returned via `Result.err` from `world.addSystems`
|
|
271
|
+
* when a SystemSet token fails local structural validation.
|
|
272
|
+
*
|
|
273
|
+
* The sole public invalid-token error type (D-2a). Covers all rejection
|
|
274
|
+
* scenarios: plain-object cast, unregistered name, stale token after
|
|
275
|
+
* overwrite, and cross-realm copies.
|
|
276
|
+
*
|
|
277
|
+
* `.code = 'system-set-not-registered'`
|
|
278
|
+
* `.expected` — the name of the unregistered token.
|
|
279
|
+
* `.hint` — suggests passing the current token to the owning World schedule.
|
|
280
|
+
* `.detail` — `{ code, name, registered }` where `registered` is a deterministic snapshot
|
|
281
|
+
* of the current World-local schedule keys.
|
|
282
|
+
*/
|
|
283
|
+
export class SystemSetNotRegisteredError extends Error {
|
|
284
|
+
override readonly name = 'SystemSetNotRegisteredError';
|
|
285
|
+
readonly code = 'system-set-not-registered' as const;
|
|
286
|
+
/** The name carried by the rejected token. */
|
|
287
|
+
readonly expected: string;
|
|
288
|
+
readonly hint: string;
|
|
289
|
+
/** Deterministic snapshot of the current World-local schedule for repair. */
|
|
290
|
+
readonly detail: {
|
|
291
|
+
readonly code: 'system-set-not-registered';
|
|
292
|
+
readonly name: string;
|
|
293
|
+
readonly registered: readonly string[];
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
constructor(name: string, registered: readonly string[]) {
|
|
297
|
+
const hint =
|
|
298
|
+
`SystemSet "${name}" is not valid for the current World schedule. ` +
|
|
299
|
+
`Pass a non-empty SystemSet token owned by this World schedule.`;
|
|
300
|
+
const message =
|
|
301
|
+
`SystemSet "${name}" is not registered.\n` +
|
|
302
|
+
` expected: ${name}\n` +
|
|
303
|
+
` registered: [${registered.join(', ')}]\n` +
|
|
304
|
+
` hint: ${hint}`;
|
|
305
|
+
super(message);
|
|
306
|
+
this.expected = name;
|
|
307
|
+
this.hint = hint;
|
|
308
|
+
this.detail = { code: 'system-set-not-registered' as const, name, registered };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Factory for {@link SystemSetNotRegisteredError}. Consumed by
|
|
314
|
+
* {@link validateSystemSetTokens} (w4) and the two mutation entry points.
|
|
315
|
+
*/
|
|
316
|
+
export function systemSetNotRegistered(
|
|
317
|
+
name: string,
|
|
318
|
+
registered: readonly string[],
|
|
319
|
+
): SystemSetNotRegisteredError {
|
|
320
|
+
return new SystemSetNotRegisteredError(name, registered);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Closed-set ScheduleMutationError code union (M2 — plan-strategy D-3).
|
|
325
|
+
*
|
|
326
|
+
* Schedule add-only API (`removeSystem` / `replaceSystem`) returns
|
|
327
|
+
* `Result<void, ScheduleMutationError>` carrying one of these codes. The
|
|
328
|
+
* `@forgeax/engine-remote` layer (M3) bridges these strings to JSON-RPC
|
|
329
|
+
* `RemoteErrorCode` 1:1 — keeping ECS free of console / wire dependencies.
|
|
330
|
+
*
|
|
331
|
+
* - `system-before-unknown` — name argument does not match any registered system.
|
|
332
|
+
* - `system-name-conflict` — reserved for the M3 inject path; surfaced from
|
|
333
|
+
* schedule when callers inject a name that already exists.
|
|
334
|
+
* - `cyclic-injection` — schedule build detected a cycle introduced by
|
|
335
|
+
* the mutation; carries the cycle path in `.detail.cycle`.
|
|
336
|
+
*/
|
|
337
|
+
export type ScheduleMutationErrorCode = 'system-before-unknown';
|
|
338
|
+
|
|
339
|
+
export interface ScheduleMutationErrorDetail {
|
|
340
|
+
readonly cycle?: readonly string[];
|
|
341
|
+
readonly candidates?: readonly string[];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Returned via `Result.err` from `world.removeSystem` / `world.replaceSystem`.
|
|
346
|
+
*
|
|
347
|
+
* `.code` is the closed-set string SSOT consumed by the M3 console bridge;
|
|
348
|
+
* `.hint` carries an AI-friendly self-repair suggestion; `.detail` is the
|
|
349
|
+
* discriminated payload (cycle path for `cyclic-injection`, candidate list
|
|
350
|
+
* for `system-before-unknown`).
|
|
351
|
+
*/
|
|
352
|
+
export class ScheduleMutationError extends Error {
|
|
353
|
+
override readonly name = 'ScheduleMutationError';
|
|
354
|
+
readonly code: ScheduleMutationErrorCode;
|
|
355
|
+
readonly hint: string;
|
|
356
|
+
readonly detail: ScheduleMutationErrorDetail;
|
|
357
|
+
|
|
358
|
+
constructor(
|
|
359
|
+
code: ScheduleMutationErrorCode,
|
|
360
|
+
message: string,
|
|
361
|
+
hint: string,
|
|
362
|
+
detail: ScheduleMutationErrorDetail = {},
|
|
363
|
+
) {
|
|
364
|
+
super(`${message}\n code: ${code}\n hint: ${hint}`);
|
|
365
|
+
this.code = code;
|
|
366
|
+
this.hint = hint;
|
|
367
|
+
this.detail = detail;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Thrown when getResource is called with a key that does not exist.
|
|
373
|
+
*
|
|
374
|
+
* `.code = 'resource-not-found'`
|
|
375
|
+
* `.hint` — suggests using `world.insertResource()` first.
|
|
376
|
+
*/
|
|
377
|
+
export class ResourceNotFoundError extends Error {
|
|
378
|
+
override readonly name = 'ResourceNotFoundError';
|
|
379
|
+
readonly code = 'resource-not-found' as const;
|
|
380
|
+
readonly hint: string;
|
|
381
|
+
|
|
382
|
+
constructor(key: string) {
|
|
383
|
+
const hint = `Resource "${key}" not found. Insert with world.insertResource() first.`;
|
|
384
|
+
super(`Resource "${key}" not found.\n` + ` key: ${key}\n` + ` hint: ${hint}`);
|
|
385
|
+
this.hint = hint;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export class ChangeEpochExhaustedError extends Error {
|
|
390
|
+
override readonly name = 'ChangeEpochExhaustedError';
|
|
391
|
+
readonly code = 'change-epoch-exhausted' as const;
|
|
392
|
+
readonly expected = 'mutationEpoch < Number.MAX_SAFE_INTEGER';
|
|
393
|
+
readonly hint = 'Rebuild the World before performing another mutation.';
|
|
394
|
+
readonly detail: { readonly epoch: number };
|
|
395
|
+
|
|
396
|
+
constructor(epoch: number) {
|
|
397
|
+
super(`World mutation epoch is exhausted at ${epoch}.\n hint: Rebuild the World.`);
|
|
398
|
+
this.detail = { epoch };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Thrown when insertResource/removeResource is called on a World-owned
|
|
404
|
+
* protected resource (Time or FixedTime).
|
|
405
|
+
*
|
|
406
|
+
* `.code = 'resource-protected'`
|
|
407
|
+
* `.hint` — suggests using `world.update(delta)` or reading the resource.
|
|
408
|
+
* `.expected` — the resource name that was rejected.
|
|
409
|
+
*/
|
|
410
|
+
export class ProtectedResourceError extends Error {
|
|
411
|
+
override readonly name = 'ProtectedResourceError';
|
|
412
|
+
readonly code = 'resource-protected' as const;
|
|
413
|
+
readonly hint: string;
|
|
414
|
+
readonly expected: string;
|
|
415
|
+
|
|
416
|
+
constructor(resourceName: string, operation: 'insert' | 'remove') {
|
|
417
|
+
const hint =
|
|
418
|
+
operation === 'insert'
|
|
419
|
+
? `"${resourceName}" is a World-owned protected resource. It is advanced by world.update(delta); read it via world.getResource(${resourceName}).`
|
|
420
|
+
: `"${resourceName}" is a World-owned protected resource. It is owned by the World scheduler and cannot be removed.`;
|
|
421
|
+
const expected = `a user-owned resource key (not ${resourceName})`;
|
|
422
|
+
super(
|
|
423
|
+
`Protected resource "${resourceName}" cannot be ${operation}ed.\n` +
|
|
424
|
+
` code: resource-protected\n` +
|
|
425
|
+
` resource: ${resourceName}\n` +
|
|
426
|
+
` expected: ${expected}\n` +
|
|
427
|
+
` hint: ${hint}`,
|
|
428
|
+
);
|
|
429
|
+
this.hint = hint;
|
|
430
|
+
this.expected = expected;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
435
|
+
// w5 — managed-* closed-union extension (M0).
|
|
436
|
+
//
|
|
437
|
+
// Four error classes covering the managed-* family:
|
|
438
|
+
//
|
|
439
|
+
// managed-* : UniqueRefStore + BufferPool runtime fail-fast.
|
|
440
|
+
// Returned via Result.err from M1 / M2 storage paths.
|
|
441
|
+
//
|
|
442
|
+
// `.code` uses lowercase-kebab literals consistent with `ScheduleMutationErrorCode`
|
|
443
|
+
// (the prior closed-set convention). The 9 legacy errors keep their
|
|
444
|
+
// SCREAMING_SNAKE_CASE codes — codes are append-only per the evolution contract.
|
|
445
|
+
// `EcsErrorCode` (declared at the foot of this file) merges all literal codes
|
|
446
|
+
// into one closed union; downstream `switch (err.code)` is exhaustive.
|
|
447
|
+
//
|
|
448
|
+
// Every detail object is a discriminated payload — narrowed per `.code` via
|
|
449
|
+
// `EcsErrorDetail` (also at foot of file).
|
|
450
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Thrown / returned via `Result.err` when UniqueRefStore.alloc encounters a
|
|
454
|
+
* slot whose refcount has already dropped to zero (sentinel for a released
|
|
455
|
+
* slot reused without re-init).
|
|
456
|
+
*
|
|
457
|
+
* `.code = 'unique-ref-released'`
|
|
458
|
+
* `.detail = { handle, target }`
|
|
459
|
+
* `.hint` — recommends checking handle lifetime against owner despawn.
|
|
460
|
+
*/
|
|
461
|
+
export class UniqueRefReleasedError extends Error {
|
|
462
|
+
override readonly name = 'UniqueRefReleasedError';
|
|
463
|
+
readonly code = 'unique-ref-released' as const;
|
|
464
|
+
readonly hint: string;
|
|
465
|
+
readonly expected: string;
|
|
466
|
+
readonly detail: { readonly handle: number; readonly target: string };
|
|
467
|
+
|
|
468
|
+
constructor(handle: number, target: string) {
|
|
469
|
+
const hint = `Handle ${handle} (target ${target}) was released before this access. Re-acquire via the producing system or re-spawn the asset before reading.`;
|
|
470
|
+
const expected = 'live (refcount >= 1) managed handle';
|
|
471
|
+
super(
|
|
472
|
+
`UniqueRefStore: handle is already released.\n` +
|
|
473
|
+
` code: unique-ref-released\n` +
|
|
474
|
+
` handle: ${handle}\n` +
|
|
475
|
+
` target: ${target}\n` +
|
|
476
|
+
` expected: ${expected}\n` +
|
|
477
|
+
` hint: ${hint}`,
|
|
478
|
+
);
|
|
479
|
+
this.hint = hint;
|
|
480
|
+
this.expected = expected;
|
|
481
|
+
this.detail = { handle, target };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Thrown / returned via `Result.err` when UniqueRefStore.release is called on
|
|
487
|
+
* a handle whose refcount is already zero (double-free).
|
|
488
|
+
*
|
|
489
|
+
* `.code = 'unique-ref-double-release'`
|
|
490
|
+
* `.detail = { handle, target }`
|
|
491
|
+
* `.hint` — recommends auditing the release-loop entry points (set / removeComponent / despawn).
|
|
492
|
+
*/
|
|
493
|
+
export class UniqueRefDoubleReleaseError extends Error {
|
|
494
|
+
override readonly name = 'UniqueRefDoubleReleaseError';
|
|
495
|
+
readonly code = 'unique-ref-double-release' as const;
|
|
496
|
+
readonly hint: string;
|
|
497
|
+
readonly expected: string;
|
|
498
|
+
readonly detail: { readonly handle: number; readonly target: string };
|
|
499
|
+
|
|
500
|
+
constructor(handle: number, target: string) {
|
|
501
|
+
const hint = `Handle ${handle} (target ${target}) was released twice. Only one of {despawn / removeComponent / set} should release a managed handle per lifecycle.`;
|
|
502
|
+
const expected = 'first release of a managed handle (refcount transition 1 -> 0)';
|
|
503
|
+
super(
|
|
504
|
+
`UniqueRefStore: double release on handle.\n` +
|
|
505
|
+
` code: unique-ref-double-release\n` +
|
|
506
|
+
` handle: ${handle}\n` +
|
|
507
|
+
` target: ${target}\n` +
|
|
508
|
+
` expected: ${expected}\n` +
|
|
509
|
+
` hint: ${hint}`,
|
|
510
|
+
);
|
|
511
|
+
this.hint = hint;
|
|
512
|
+
this.expected = expected;
|
|
513
|
+
this.detail = { handle, target };
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
518
|
+
// feat-20260614-ecs-shared-component-and-unique-rename M3 — SharedRefStore
|
|
519
|
+
// closed-union extension (+2). Mirrors the UniqueRef* pair — `'shared-ref-released'`
|
|
520
|
+
// covers resolve-after-release / retain-after-release; `'shared-ref-double-release'`
|
|
521
|
+
// covers release-when-rc-already-zero. Both are Result.err returns (not throws);
|
|
522
|
+
// AI users branch on `.code` and read `.detail.handle` for the offending slot.
|
|
523
|
+
//
|
|
524
|
+
// Detail field shape mirrors UniqueRef* with one addition (`rc`) so AI users
|
|
525
|
+
// debugging a double-release see the exact rc transition that surfaced the
|
|
526
|
+
// failure (charter P3 progressive disclosure). Empty `target` handled the
|
|
527
|
+
// same way as UniqueRef* — runtime-erased phantom, surfaced as '<unknown>'.
|
|
528
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Thrown / returned via `Result.err` when SharedRefStore.resolve / .retain is
|
|
532
|
+
* called on a handle whose refcount has already dropped to zero (slot released).
|
|
533
|
+
*
|
|
534
|
+
* `.code = 'shared-ref-released'`
|
|
535
|
+
* `.detail = { handle, target }`
|
|
536
|
+
* `.hint` — recommends checking handle lifetime against owner / consumer release.
|
|
537
|
+
*/
|
|
538
|
+
export class SharedRefReleasedError extends Error {
|
|
539
|
+
override readonly name = 'SharedRefReleasedError';
|
|
540
|
+
readonly code = 'shared-ref-released' as const;
|
|
541
|
+
readonly hint: string;
|
|
542
|
+
readonly expected: string;
|
|
543
|
+
readonly detail: { readonly handle: number; readonly target: string };
|
|
544
|
+
|
|
545
|
+
constructor(handle: number, target: string) {
|
|
546
|
+
const hint = `Handle ${handle} (target ${target}) was released (refcount reached 0). Re-acquire via the producing system or re-spawn the asset before reading.`;
|
|
547
|
+
const expected = 'live (refcount >= 1) shared handle';
|
|
548
|
+
super(
|
|
549
|
+
`SharedRefStore: handle is already released.\n` +
|
|
550
|
+
` code: shared-ref-released\n` +
|
|
551
|
+
` handle: ${handle}\n` +
|
|
552
|
+
` target: ${target}\n` +
|
|
553
|
+
` expected: ${expected}\n` +
|
|
554
|
+
` hint: ${hint}`,
|
|
555
|
+
);
|
|
556
|
+
this.hint = hint;
|
|
557
|
+
this.expected = expected;
|
|
558
|
+
this.detail = { handle, target };
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Thrown / returned via `Result.err` when SharedRefStore.release is called on
|
|
564
|
+
* a handle whose refcount is already zero (double-release). Distinct from the
|
|
565
|
+
* UniqueRef family because shared release is rc--, not direct slot drop —
|
|
566
|
+
* AI users debug this by reading `.detail.rc` (always 0 here) alongside the
|
|
567
|
+
* payload-presence signal.
|
|
568
|
+
*
|
|
569
|
+
* `.code = 'shared-ref-double-release'`
|
|
570
|
+
* `.detail = { handle, target, rc }`
|
|
571
|
+
* `.hint` — recommends auditing the producer / consumer release pairs.
|
|
572
|
+
*/
|
|
573
|
+
export class SharedRefDoubleReleaseError extends Error {
|
|
574
|
+
override readonly name = 'SharedRefDoubleReleaseError';
|
|
575
|
+
readonly code = 'shared-ref-double-release' as const;
|
|
576
|
+
readonly hint: string;
|
|
577
|
+
readonly expected: string;
|
|
578
|
+
readonly detail: { readonly handle: number; readonly target: string; readonly rc: number };
|
|
579
|
+
|
|
580
|
+
constructor(handle: number, target: string, rc: number) {
|
|
581
|
+
const hint = `Handle ${handle} (target ${target}) released with rc=${rc}. Each shared handle must have a matching alloc/retain for every release; audit the producer / consumer release pairs.`;
|
|
582
|
+
const expected = 'rc >= 1 before release';
|
|
583
|
+
super(
|
|
584
|
+
`SharedRefStore: double release on handle.\n` +
|
|
585
|
+
` code: shared-ref-double-release\n` +
|
|
586
|
+
` handle: ${handle}\n` +
|
|
587
|
+
` target: ${target}\n` +
|
|
588
|
+
` rc: ${rc}\n` +
|
|
589
|
+
` expected: ${expected}\n` +
|
|
590
|
+
` hint: ${hint}`,
|
|
591
|
+
);
|
|
592
|
+
this.hint = hint;
|
|
593
|
+
this.expected = expected;
|
|
594
|
+
this.detail = { handle, target, rc };
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/** Raised before SharedRefStore mutates slot state for a nullish payload. */
|
|
599
|
+
export class SharedRefPayloadInvalidError extends Error {
|
|
600
|
+
override readonly name = 'SharedRefPayloadInvalidError';
|
|
601
|
+
readonly code = 'shared-ref-payload-invalid' as const;
|
|
602
|
+
readonly expected = 'a non-null, non-undefined shared payload';
|
|
603
|
+
readonly hint = 'Allocate a concrete payload and let its owning effect dispose it.';
|
|
604
|
+
readonly detail: { readonly target: string; readonly actual: 'null' | 'undefined' };
|
|
605
|
+
|
|
606
|
+
constructor(target: string, actual: 'null' | 'undefined') {
|
|
607
|
+
super(`SharedRefStore: ${actual} payload is not a valid shared reference for ${target}.`);
|
|
608
|
+
this.detail = { target, actual };
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Returned via `Result.err` when a builtin-tier slot (`slot < BUILTIN_BASE`)
|
|
614
|
+
* is passed to SharedRefStore.alloc / retain / release / resolve
|
|
615
|
+
* (feat-20260614 M6 D-15). The SharedRefStore manages ONLY user-tier slots
|
|
616
|
+
* (`>= BUILTIN_BASE`); builtin asset payloads are process-static and owned by
|
|
617
|
+
* the package that authored their builtin handle, never reference-counted by
|
|
618
|
+
* this World store.
|
|
619
|
+
*
|
|
620
|
+
* `.code = 'builtin-slot-not-owned'`
|
|
621
|
+
* `.detail = { slot }`
|
|
622
|
+
* `.hint` — points the caller back to the builtin handle owner.
|
|
623
|
+
*/
|
|
624
|
+
export class BuiltinSlotNotOwnedError extends Error {
|
|
625
|
+
override readonly name = 'BuiltinSlotNotOwnedError';
|
|
626
|
+
readonly code = 'builtin-slot-not-owned' as const;
|
|
627
|
+
readonly hint: string;
|
|
628
|
+
readonly expected: string;
|
|
629
|
+
readonly detail: { readonly slot: number };
|
|
630
|
+
|
|
631
|
+
constructor(slot: number) {
|
|
632
|
+
const hint = `Slot ${slot} is a builtin-tier handle (< BUILTIN_BASE). World.sharedRefs manages only user-tier handles (>= BUILTIN_BASE). Obtain the builtin payload from the package that authored the handle; builtin payloads are process-static and never reference-counted by this World.`;
|
|
633
|
+
const expected = 'user-tier slot (>= BUILTIN_BASE)';
|
|
634
|
+
super(
|
|
635
|
+
`SharedRefStore: builtin slot is not owned by this store.\n` +
|
|
636
|
+
` code: builtin-slot-not-owned\n` +
|
|
637
|
+
` slot: ${slot}\n` +
|
|
638
|
+
` expected: ${expected}\n` +
|
|
639
|
+
` hint: ${hint}`,
|
|
640
|
+
);
|
|
641
|
+
this.hint = hint;
|
|
642
|
+
this.expected = expected;
|
|
643
|
+
this.detail = { slot };
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
648
|
+
// feat-20260623-asset-handle-generation M4 — stale error classes (+2).
|
|
649
|
+
//
|
|
650
|
+
// Two error classes covering gen-based staleness detection in SharedRefStore
|
|
651
|
+
// and UniqueRefStore. Distinguish from the existing `*-ref-released` codes
|
|
652
|
+
// (slot empty / never allocated) — `*-ref-stale` means the slot has been
|
|
653
|
+
// released AND re-allocated, so the caller's handle generation no longer
|
|
654
|
+
// matches the store's current generation. AI users pick different recovery
|
|
655
|
+
// strategies: released -> re-load the asset; stale -> re-acquire the handle
|
|
656
|
+
// from AssetRegistry (charter P3 explicit failure, two semantics two
|
|
657
|
+
// recovery paths).
|
|
658
|
+
//
|
|
659
|
+
// `.detail` carries { slot, expectedGeneration, actualGeneration } aligned
|
|
660
|
+
// with StaleEntityError field names (AC-11). Codes are add-only minor
|
|
661
|
+
// members of EcsErrorCode (AC-10) and detail shapes extend EcsErrorDetail
|
|
662
|
+
// discriminator.
|
|
663
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Returned via `Result.err` when SharedRefStore.resolve / .retain / .release
|
|
667
|
+
* is called with a handle whose generation no longer matches the store's
|
|
668
|
+
* current generation for that slot — the slot was released and re-allocated
|
|
669
|
+
* to a different payload. Distinct from `'shared-ref-released'` (slot empty,
|
|
670
|
+
* never re-allocated): stale means the slot IS live but belongs to a newer
|
|
671
|
+
* allocation.
|
|
672
|
+
*
|
|
673
|
+
* `.code = 'shared-ref-stale'`
|
|
674
|
+
* `.detail = { slot, expectedGeneration, actualGeneration }`
|
|
675
|
+
* `.hint` — recommends re-acquiring the handle from AssetRegistry.
|
|
676
|
+
*/
|
|
677
|
+
export class SharedRefStaleError extends Error {
|
|
678
|
+
override readonly name = 'SharedRefStaleError';
|
|
679
|
+
readonly code = 'shared-ref-stale' as const;
|
|
680
|
+
readonly hint: string;
|
|
681
|
+
readonly expected: string;
|
|
682
|
+
readonly detail: {
|
|
683
|
+
readonly slot: number;
|
|
684
|
+
readonly expectedGeneration: number;
|
|
685
|
+
readonly actualGeneration: number;
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
constructor(slot: number, expectedGeneration: number, actualGeneration: number) {
|
|
689
|
+
const hint = `Handle for slot ${slot} is stale: expected generation ${expectedGeneration}, but the store has generation ${actualGeneration} (slot was released and re-allocated). Re-acquire the handle from AssetRegistry.`;
|
|
690
|
+
const expected = `generation === ${actualGeneration} (current store generation)`;
|
|
691
|
+
super(
|
|
692
|
+
`SharedRefStore: stale handle.\n` +
|
|
693
|
+
` code: shared-ref-stale\n` +
|
|
694
|
+
` slot: ${slot}\n` +
|
|
695
|
+
` expectedGeneration: ${expectedGeneration}\n` +
|
|
696
|
+
` actualGeneration: ${actualGeneration}\n` +
|
|
697
|
+
` expected: ${expected}\n` +
|
|
698
|
+
` hint: ${hint}`,
|
|
699
|
+
);
|
|
700
|
+
this.hint = hint;
|
|
701
|
+
this.expected = expected;
|
|
702
|
+
this.detail = { slot, expectedGeneration, actualGeneration };
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Returned via `Result.err` when UniqueRefStore.resolve / .release is
|
|
708
|
+
* called with a handle whose generation no longer matches the store's
|
|
709
|
+
* current generation for that slot — the slot was released and re-allocated.
|
|
710
|
+
* Distinct from `'unique-ref-released'` (slot empty). UniqueRefStore has
|
|
711
|
+
* no retain method; the stale surface is resolve + release only.
|
|
712
|
+
*
|
|
713
|
+
* `.code = 'unique-ref-stale'`
|
|
714
|
+
* `.detail = { slot, expectedGeneration, actualGeneration }`
|
|
715
|
+
* `.hint` — recommends re-acquiring the handle from the producing system.
|
|
716
|
+
*/
|
|
717
|
+
export class UniqueRefStaleError extends Error {
|
|
718
|
+
override readonly name = 'UniqueRefStaleError';
|
|
719
|
+
readonly code = 'unique-ref-stale' as const;
|
|
720
|
+
readonly hint: string;
|
|
721
|
+
readonly expected: string;
|
|
722
|
+
readonly detail: {
|
|
723
|
+
readonly slot: number;
|
|
724
|
+
readonly expectedGeneration: number;
|
|
725
|
+
readonly actualGeneration: number;
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
constructor(slot: number, expectedGeneration: number, actualGeneration: number) {
|
|
729
|
+
const hint = `Handle for slot ${slot} is stale: expected generation ${expectedGeneration}, but the store has generation ${actualGeneration} (slot was released and re-allocated). Re-acquire the handle via the producing system or re-spawn the asset.`;
|
|
730
|
+
const expected = `generation === ${actualGeneration} (current store generation)`;
|
|
731
|
+
super(
|
|
732
|
+
`UniqueRefStore: stale handle.\n` +
|
|
733
|
+
` code: unique-ref-stale\n` +
|
|
734
|
+
` slot: ${slot}\n` +
|
|
735
|
+
` expectedGeneration: ${expectedGeneration}\n` +
|
|
736
|
+
` actualGeneration: ${actualGeneration}\n` +
|
|
737
|
+
` expected: ${expected}\n` +
|
|
738
|
+
` hint: ${hint}`,
|
|
739
|
+
);
|
|
740
|
+
this.hint = hint;
|
|
741
|
+
this.expected = expected;
|
|
742
|
+
this.detail = { slot, expectedGeneration, actualGeneration };
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Thrown / returned via `Result.err` when BufferPool indexing reads or writes
|
|
748
|
+
* an offset outside the slot's `[0, size)` byte range. Triggers are limited
|
|
749
|
+
* to the `buffer:<N>` and managed-array-element-buffer paths; the
|
|
750
|
+
* `'string'` schema vocab no longer routes through this code (collapsed onto
|
|
751
|
+
* the managed-ref dispatch by feat-20260515-string-managed-collapse — JS
|
|
752
|
+
* string capacity is bounded by the host runtime, not by BufferPool buckets).
|
|
753
|
+
*
|
|
754
|
+
* `.code = 'managed-buffer-out-of-bounds'`
|
|
755
|
+
* `.detail = { index, size }`
|
|
756
|
+
* `.hint` — points at the field's `'buffer'` / `buffer<N>` schema declaration.
|
|
757
|
+
*/
|
|
758
|
+
export class ManagedBufferOutOfBoundsError extends RangeError {
|
|
759
|
+
override readonly name = 'ManagedBufferOutOfBoundsError';
|
|
760
|
+
readonly code = 'managed-buffer-out-of-bounds' as const;
|
|
761
|
+
readonly hint: string;
|
|
762
|
+
readonly expected: string;
|
|
763
|
+
readonly detail: { readonly index: number; readonly size: number };
|
|
764
|
+
|
|
765
|
+
constructor(index: number, size: number) {
|
|
766
|
+
const hint = `Index ${index} is outside [0, ${size}). Check the field's 'buffer' / 'buffer<N>' declaration matches the access pattern.`;
|
|
767
|
+
const expected = `index in [0, ${size})`;
|
|
768
|
+
super(
|
|
769
|
+
`BufferPool: index out of bounds.\n` +
|
|
770
|
+
` code: managed-buffer-out-of-bounds\n` +
|
|
771
|
+
` index: ${index}\n` +
|
|
772
|
+
` size: ${size}\n` +
|
|
773
|
+
` expected: ${expected}\n` +
|
|
774
|
+
` hint: ${hint}`,
|
|
775
|
+
);
|
|
776
|
+
this.hint = hint;
|
|
777
|
+
this.expected = expected;
|
|
778
|
+
this.detail = { index, size };
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Thrown / returned via `Result.err` when BufferPool resize is asked to shrink
|
|
784
|
+
* a slot below its current allocated size — the pool only grows.
|
|
785
|
+
*
|
|
786
|
+
* `.code = 'managed-buffer-shrink-not-supported'`
|
|
787
|
+
* `.detail = { requested, current }`
|
|
788
|
+
* `.hint` — directs callers to allocate a fresh slot if a smaller buffer is needed.
|
|
789
|
+
*/
|
|
790
|
+
export class ManagedBufferShrinkNotSupportedError extends Error {
|
|
791
|
+
override readonly name = 'ManagedBufferShrinkNotSupportedError';
|
|
792
|
+
readonly code = 'managed-buffer-shrink-not-supported' as const;
|
|
793
|
+
readonly hint: string;
|
|
794
|
+
readonly expected: string;
|
|
795
|
+
readonly detail: { readonly requested: number; readonly current: number };
|
|
796
|
+
|
|
797
|
+
constructor(requested: number, current: number) {
|
|
798
|
+
const hint = `BufferPool only grows. Requested ${requested} bytes < current ${current}; allocate a fresh slot if a smaller buffer is required.`;
|
|
799
|
+
const expected = `requested >= ${current}`;
|
|
800
|
+
super(
|
|
801
|
+
`BufferPool: shrink not supported.\n` +
|
|
802
|
+
` code: managed-buffer-shrink-not-supported\n` +
|
|
803
|
+
` requested: ${requested}\n` +
|
|
804
|
+
` current: ${current}\n` +
|
|
805
|
+
` expected: ${expected}\n` +
|
|
806
|
+
` hint: ${hint}`,
|
|
807
|
+
);
|
|
808
|
+
this.hint = hint;
|
|
809
|
+
this.expected = expected;
|
|
810
|
+
this.detail = { requested, current };
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
815
|
+
// feat-20260515-buffer-array-vocab-collapse w11 — closed-union evolution.
|
|
816
|
+
//
|
|
817
|
+
// 4 managed-array-* error classes deleted (replaced by the 4 collapsed-vocab
|
|
818
|
+
// codes below); ManagedArrayElementTypeNotAllowedError preserved (still
|
|
819
|
+
// surfaced from defineComponent's schema parser).
|
|
820
|
+
//
|
|
821
|
+
// 2 surviving error classes:
|
|
822
|
+
// - FixedSizeMismatchError ('fixed-size-mismatch')
|
|
823
|
+
// - InstanceTransformsStrideMismatchError ('instance-transforms-stride-mismatch')
|
|
824
|
+
//
|
|
825
|
+
// Naming-prefix orthogonality (plan-strategy §2.5):
|
|
826
|
+
// fixed- element-type or capacity contract violations on fixed shape
|
|
827
|
+
// array- operation failures (pop on empty) on the array vocab keyword
|
|
828
|
+
// instance- GPU-render component-specific stride contract (Instances.transforms)
|
|
829
|
+
//
|
|
830
|
+
// The array element-wise facade was removed; only write-shape and render
|
|
831
|
+
// stride errors remain on this boundary. The
|
|
832
|
+
// `instance-transforms-stride-mismatch` member is the plan-strategy §2.4
|
|
833
|
+
// evolution surfaced from `packages/runtime/src/render-system-extract.ts`
|
|
834
|
+
// defensive entry (consumed by w15 in M3, but the error class lives here so
|
|
835
|
+
// the EcsErrorCode union closure is owned by ECS — RhiError is not extended
|
|
836
|
+
// per plan-strategy §2.4 decision).
|
|
837
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Returned via `Result.err` from `world.set` when a `buffer<N>` field is
|
|
841
|
+
* written with a `Uint8Array` whose `byteLength` does not equal the
|
|
842
|
+
* schema-declared fixed size `N`. AI users resize their payload to exactly
|
|
843
|
+
* `N` bytes (zero-pad or truncate at the producer) before calling `world.set`.
|
|
844
|
+
*
|
|
845
|
+
* `.code = 'fixed-size-mismatch'`
|
|
846
|
+
* `.detail = { expected, actual }`
|
|
847
|
+
* `.hint` — points at the producer's payload sizing.
|
|
848
|
+
*/
|
|
849
|
+
export class FixedSizeMismatchError extends Error {
|
|
850
|
+
override readonly name = 'FixedSizeMismatchError';
|
|
851
|
+
readonly code = 'fixed-size-mismatch' as const;
|
|
852
|
+
readonly hint: string;
|
|
853
|
+
readonly expected: string;
|
|
854
|
+
readonly detail: { readonly expected: number; readonly actual: number };
|
|
855
|
+
|
|
856
|
+
constructor(fieldName: string, expected: number, actual: number) {
|
|
857
|
+
const hint = `buffer<${expected}> set with byteLength ${actual} (expected ${expected}); resize your Uint8Array to exactly ${expected} bytes before world.set`;
|
|
858
|
+
const expectedStr = `byteLength === ${expected}`;
|
|
859
|
+
super(
|
|
860
|
+
`buffer<N>: fixed-size mismatch.\n` +
|
|
861
|
+
` code: fixed-size-mismatch\n` +
|
|
862
|
+
` field: ${fieldName}\n` +
|
|
863
|
+
` expected: ${expected}\n` +
|
|
864
|
+
` actual: ${actual}\n` +
|
|
865
|
+
` hint: ${hint}`,
|
|
866
|
+
);
|
|
867
|
+
this.hint = hint;
|
|
868
|
+
this.expected = expectedStr;
|
|
869
|
+
this.detail = { expected, actual };
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Surfaced via the Layer-3 ErrorHandler from
|
|
875
|
+
* `packages/runtime/src/render-system-extract.ts` defensive entry when an
|
|
876
|
+
* `Instances.transforms` array<f32> length violates the column-major mat4
|
|
877
|
+
* stride contract (`length % 16 === 0`). Locates the failure adjacent to the
|
|
878
|
+
* extract pipeline rather than at GPU upload (plan-strategy §2.4 D-P2 +
|
|
879
|
+
* §8.3 hint SSOT).
|
|
880
|
+
*
|
|
881
|
+
* `.code = 'instance-transforms-stride-mismatch'`
|
|
882
|
+
* `.detail = { actualLength, expectedStride: 16 }`
|
|
883
|
+
* `.hint` — names the stride invariant + the call sites to audit.
|
|
884
|
+
*/
|
|
885
|
+
export class InstanceTransformsStrideMismatchError extends Error {
|
|
886
|
+
override readonly name = 'InstanceTransformsStrideMismatchError';
|
|
887
|
+
readonly code = 'instance-transforms-stride-mismatch' as const;
|
|
888
|
+
readonly hint: string;
|
|
889
|
+
readonly expected: string;
|
|
890
|
+
readonly detail: { readonly actualLength: number; readonly expectedStride: 16 };
|
|
891
|
+
|
|
892
|
+
constructor(actualLength: number) {
|
|
893
|
+
const hint = `Instances.transforms length ${actualLength} violates stride 16 (mat4); ensure transforms.length % 16 === 0 before render frame; verify world.set / world.push call sites`;
|
|
894
|
+
const expectedStr = 'actualLength % 16 === 0';
|
|
895
|
+
super(
|
|
896
|
+
`Instances.transforms: stride mismatch.\n` +
|
|
897
|
+
` code: instance-transforms-stride-mismatch\n` +
|
|
898
|
+
` actualLength: ${actualLength}\n` +
|
|
899
|
+
` expectedStride: 16\n` +
|
|
900
|
+
` hint: ${hint}`,
|
|
901
|
+
);
|
|
902
|
+
this.hint = hint;
|
|
903
|
+
this.expected = expectedStr;
|
|
904
|
+
this.detail = { actualLength, expectedStride: 16 };
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Thrown by `defineComponent` when an `array<T>` / `array<T,N>` schema field
|
|
910
|
+
* carries an element type outside the legal whitelist (scalars + entity).
|
|
911
|
+
* Forms like `array<ref<X>>` / `array<handle<X>>` / `array<buffer:N>` /
|
|
912
|
+
* `array<array<...>>` are rejected (AC-03 runtime fail-safe). The TS layer
|
|
913
|
+
* blocks these forms at compile time; this error is the runtime backstop for
|
|
914
|
+
* `as unknown as SchemaFieldType` casts.
|
|
915
|
+
*
|
|
916
|
+
* `.code = 'managed-array-element-type-not-allowed'`
|
|
917
|
+
* `.detail = { fieldName, elementType, hint }`
|
|
918
|
+
* `.hint` — lists the whitelist of legal element types.
|
|
919
|
+
*/
|
|
920
|
+
export class ManagedArrayElementTypeNotAllowedError extends Error {
|
|
921
|
+
override readonly name = 'ManagedArrayElementTypeNotAllowedError';
|
|
922
|
+
readonly code = 'managed-array-element-type-not-allowed' as const;
|
|
923
|
+
readonly hint: string;
|
|
924
|
+
readonly expected: string;
|
|
925
|
+
readonly detail: {
|
|
926
|
+
readonly fieldName: string;
|
|
927
|
+
readonly elementType: string;
|
|
928
|
+
readonly hint: string;
|
|
929
|
+
};
|
|
930
|
+
|
|
931
|
+
constructor(fieldName: string, elementType: string) {
|
|
932
|
+
const hint = `array<T> element type must be a scalar (f32/f64/i32/u32/i16/u16/i8/u8/bool/enum/ref) or entity. ref<X> / handle<X> / buffer:N / nested array<...> are forbidden on field "${fieldName}".`;
|
|
933
|
+
const expected = 'element type in {scalar | entity}';
|
|
934
|
+
super(
|
|
935
|
+
`managed-array: element type not allowed.\n` +
|
|
936
|
+
` code: managed-array-element-type-not-allowed\n` +
|
|
937
|
+
` field: ${fieldName}\n` +
|
|
938
|
+
` elementType: ${elementType}\n` +
|
|
939
|
+
` expected: ${expected}\n` +
|
|
940
|
+
` hint: ${hint}`,
|
|
941
|
+
);
|
|
942
|
+
this.hint = hint;
|
|
943
|
+
this.expected = expected;
|
|
944
|
+
this.detail = { fieldName, elementType, hint };
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
949
|
+
// EcsErrorCode closed union (w5)
|
|
950
|
+
//
|
|
951
|
+
// Merges every `.code` literal across the EcsError family. Downstream
|
|
952
|
+
// `switch (err.code)` blocks become exhaustive; `assertNever(code)` catches
|
|
953
|
+
// any future code addition without a matching case at compile time.
|
|
954
|
+
//
|
|
955
|
+
// Order is grouped (legacy SCREAMING_SNAKE first, then closed-set kebab) but
|
|
956
|
+
// not load-bearing — TS unions are unordered.
|
|
957
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
958
|
+
|
|
959
|
+
/** Closed union of every `.code` literal carried by EcsError instances. */
|
|
960
|
+
export type EcsErrorCode =
|
|
961
|
+
// Legacy SCREAMING_SNAKE codes (7, carried unchanged; the two
|
|
962
|
+
// registration codes COMPONENT_ALREADY_REGISTERED / COMPONENT_NOT_REGISTERED
|
|
963
|
+
// were dropped by feat-20260602 along with the per-World register concept).
|
|
964
|
+
| 'stale-entity'
|
|
965
|
+
| 'component-already-present'
|
|
966
|
+
| 'component-not-present'
|
|
967
|
+
| 'cyclic-dependency'
|
|
968
|
+
| 'resource-not-found'
|
|
969
|
+
// ECS time and schedule-scope errors (M2 w16, approved 43 -> 46 baseline; verify hotfix +1 → 47).
|
|
970
|
+
| 'time-delta-invalid'
|
|
971
|
+
| 'time-config-invalid'
|
|
972
|
+
| 'schedule-scope-mismatch'
|
|
973
|
+
// ScheduleMutationError closed-set kebab code.
|
|
974
|
+
| ScheduleMutationErrorCode
|
|
975
|
+
// w5 managed-* kebab codes (4).
|
|
976
|
+
| 'unique-ref-released'
|
|
977
|
+
| 'unique-ref-double-release'
|
|
978
|
+
// feat-20260614-ecs-shared-component-and-unique-rename M3 — SharedRefStore
|
|
979
|
+
// closed-union extension (+2). `'shared-ref-released'` covers resolve / retain
|
|
980
|
+
// on rc=0; `'shared-ref-double-release'` covers release on rc=0.
|
|
981
|
+
| 'shared-ref-released'
|
|
982
|
+
| 'shared-ref-double-release'
|
|
983
|
+
| 'shared-ref-payload-invalid'
|
|
984
|
+
// feat-20260614-ecs-shared-component-and-unique-rename M6 D-15 (+1).
|
|
985
|
+
// SharedRefStore manages ONLY user-tier slots (>= BUILTIN_BASE); a builtin
|
|
986
|
+
// slot (< BUILTIN_BASE) passed to alloc/retain/release/resolve is a caller
|
|
987
|
+
// error -> `'builtin-slot-not-owned'` (the authoring package owns the payload).
|
|
988
|
+
| 'builtin-slot-not-owned'
|
|
989
|
+
// feat-20260623-asset-handle-generation M4 — stale error codes (+2).
|
|
990
|
+
// `'shared-ref-stale'` / `'unique-ref-stale'` cover gen mismatch on resolve /
|
|
991
|
+
// retain / release after slot re-allocation. Add-only minor per AGENTS.md
|
|
992
|
+
// Error model evolution contract; distinct from the existing `*-ref-released`
|
|
993
|
+
// codes (slot empty vs slot re-allocated).
|
|
994
|
+
| 'shared-ref-stale'
|
|
995
|
+
| 'unique-ref-stale'
|
|
996
|
+
| 'managed-buffer-out-of-bounds'
|
|
997
|
+
| 'managed-buffer-shrink-not-supported'
|
|
998
|
+
// managed-array-* kebab codes — surviving member from feat-20260514;
|
|
999
|
+
// the other 4 (`managed-array-{index-out-of-bounds, pop-empty,
|
|
1000
|
+
// shrink-not-supported, stride-mismatch}`) were dropped by
|
|
1001
|
+
// feat-20260515-buffer-array-vocab-collapse w11 in favour of the 4 new
|
|
1002
|
+
// collapsed-vocab codes below. Kept here because `defineComponent`'s schema
|
|
1003
|
+
// parser still surfaces it for illegal `array<...>` element types.
|
|
1004
|
+
// feat-20260515-buffer-array-vocab-collapse w11 collapsed-vocab codes (4,
|
|
1005
|
+
// plan-strategy §2.4 + §2.5 four-prefix taxonomy).
|
|
1006
|
+
| 'fixed-size-mismatch'
|
|
1007
|
+
// feat-20260519-light-casters-point-spot-pbr w2 — PointLight / SpotLight
|
|
1008
|
+
// spawn-time payload bound violation (plan-strategy D-S3 a). 23 -> 24
|
|
1009
|
+
// minor evolution per AGENTS.md Error model evolution contract.
|
|
1010
|
+
// feat-20260520-2d-sprite-layer-mvp M-2 w13 — resource-setter bound
|
|
1011
|
+
// validation (plan-strategy D-4). 25 -> 26 minor evolution; first
|
|
1012
|
+
// consumer is `setTransparentSortConfig` (mode ∈ {0, 1, 2}).
|
|
1013
|
+
// feat-20260521-sprite-atlas-animation M1 T-05 — spriteAnimationTickSystem
|
|
1014
|
+
// runtime invariant violation (plan-strategy D-1). 26 -> 27 minor evolution
|
|
1015
|
+
// per AGENTS.md §Error model evolution contract; same-shape mirror of
|
|
1016
|
+
// 'spawn-light-invalid-bounds' (feat-20260519 w2) and 'resource-invalid-
|
|
1017
|
+
// value' (feat-20260520 w13) — the `<noun>-invalid-...` kebab series keeps
|
|
1018
|
+
// switch (err.code) narrows visually consistent for AI users (charter P4).
|
|
1019
|
+
// feat-20260531-ecs-relationship-abstraction-bidirectional-sync M2 —
|
|
1020
|
+
// relationship bidirectional sync + defineComponent relationship validation +
|
|
1021
|
+
// addChild/reparent cycle detection + removeChild detach guard
|
|
1022
|
+
// (plan-strategy D-5). 27 -> 31 minor evolution per AGENTS.md Error model
|
|
1023
|
+
// evolution contract. `relationship-exclusive-violation` is NOT a member:
|
|
1024
|
+
// exclusive re-add is an automatic reparent (success path), not an error.
|
|
1025
|
+
| 'relationship-self-cycle'
|
|
1026
|
+
| 'relationship-detach-mismatch'
|
|
1027
|
+
// feat-20260602-drop-component-registration w16-a — scene instantiate
|
|
1028
|
+
// fail-fast when a SceneAsset entity names a component that was never defined
|
|
1029
|
+
// via defineComponent (the per-World register concept was dropped; a
|
|
1030
|
+
// component becomes globally usable the moment defineComponent runs). 30 ->
|
|
1031
|
+
// 31 minor evolution per AGENTS.md Error model evolution contract. Replaces
|
|
1032
|
+
// the deleted COMPONENT_NOT_REGISTERED code at the scene-instance producer
|
|
1033
|
+
// sites (research Finding 5 missed these 3 producers; human escalation-
|
|
1034
|
+
// response authorized this scope-amendment).
|
|
1035
|
+
// feat-20260602-archetype-stores-full-packed-entity M1 / w3 — removeComponent
|
|
1036
|
+
// rejection when the target is an essential (undeletable) component. The only
|
|
1037
|
+
// essential component is the id=0 `Entity` (plan-strategy D-3). Net +1 minor
|
|
1038
|
+
// evolution per AGENTS.md §Error model evolution contract.
|
|
1039
|
+
| 'remove-essential-component'
|
|
1040
|
+
// feat-20260608-scene-nesting-ecs-fication M1 / w9 — setSceneOverride
|
|
1041
|
+
// type-mismatch fail-fast (plan-strategy D-9). 30 -> 31 minor evolution per
|
|
1042
|
+
// AGENTS.md §Error model evolution contract. Surfaced from
|
|
1043
|
+
// `world.setSceneOverride(root, member, comp, field, value)` when `value`'s
|
|
1044
|
+
// runtime type does not match the per-component schema field type (the
|
|
1045
|
+
// override apply path never silently coerces — value writes are typed at the
|
|
1046
|
+
// ECS layer; requirements §Edge cases table last row, reviewer Issue 1).
|
|
1047
|
+
// bug-20260615-spawn-data-unknown-field-fail-fast — spawn / addComponent /
|
|
1048
|
+
// SceneAsset.instantiate / Commands.spawn fail-fast when the caller-supplied
|
|
1049
|
+
// payload carries a key that is not declared in the component schema. Pre-
|
|
1050
|
+
// fix the unknown key was silently dropped by `fillComponentDefaults`
|
|
1051
|
+
// (which iterated only over schema keys), routing typos like
|
|
1052
|
+
// `MeshRenderer { material }` (singular legacy name) into the empty-default
|
|
1053
|
+
// path and producing invisible / mid-grey entities downstream. AI users
|
|
1054
|
+
// narrow on `.code` then read `.detail.field` for the offending key and
|
|
1055
|
+
// `.detail.knownFields` for the valid field whitelist.
|
|
1056
|
+
| 'spawn-data-unknown-field'
|
|
1057
|
+
// feat-20260625-sprite-instances-and-tilemap-terrain-static-batch M1 / w2 —
|
|
1058
|
+
// SpriteInstances primitive + tilemap terrain static-batch path. Three
|
|
1059
|
+
// codes declared together; fire path lands in M3 w13 at the
|
|
1060
|
+
// render-system-extract QueryRow loop. Minor evolution +3 per
|
|
1061
|
+
// AGENTS.md §Error model evolution contract; plan-strategy D-6 keeps the
|
|
1062
|
+
// detection in the render domain (not the ECS spawn path) to avoid an
|
|
1063
|
+
// ECS -> AssetRegistry reverse dep for the shader-id lookup.
|
|
1064
|
+
// feat-20260713-mount-override-component-add-and-shared-ref-round M2 / w9 —
|
|
1065
|
+
// P3 shared-field value gate. A `shared<T>` scalar or `array<shared<T>>`
|
|
1066
|
+
// element must be a resolved numeric Handle; a raw GUID string / `{ guid }` /
|
|
1067
|
+
// `{ kind }` object (the pre-resolution shape an AI user gets from a sidecar)
|
|
1068
|
+
// was silently coerced to the all-zero sentinel by the column packer
|
|
1069
|
+
// (`typed[i] = typeof val === 'number' ? val : 0`) / scalar write, so a
|
|
1070
|
+
// mis-bound reference read back as `0` / `[0,0,0,0]` and rendered blank with
|
|
1071
|
+
// no error. `validateComponentDataKeys` only checks key names, not value
|
|
1072
|
+
// types — this code closes the value-type gap at all three write entries
|
|
1073
|
+
// (spawn / addComponent / set). AI users resolve a GUID via
|
|
1074
|
+
// `AssetRegistry.load(guid, kind) + allocSharedRef` first; passing the raw GUID now fails fast.
|
|
1075
|
+
// Minor evolution +1 per AGENTS.md §Error model evolution contract.
|
|
1076
|
+
| 'shared-field-invalid-value'
|
|
1077
|
+
// feat-20260714-bevy-style-system-sets M1 / w3 — sole invalid-SystemSet
|
|
1078
|
+
// error code. Surfaced from world.addSystems when a
|
|
1079
|
+
// token fails identity validation (brand bypass + registry identity check).
|
|
1080
|
+
// Minor evolution +1 per AGENTS.md §Error model evolution contract.
|
|
1081
|
+
| 'system-set-not-registered'
|
|
1082
|
+
// Closed enum field writes fail before archetype or column mutation.
|
|
1083
|
+
| 'component-field-invalid-value'
|
|
1084
|
+
| 'component-numeric-value-invalid'
|
|
1085
|
+
| 'managed-array-invalid-value'
|
|
1086
|
+
| 'shared-kernel-ineligible'
|
|
1087
|
+
| 'shared-kernel-failed'
|
|
1088
|
+
| 'world-poisoned'
|
|
1089
|
+
// World.update terminal failures. These remain in the same closed union as
|
|
1090
|
+
// structural errors so consumers never need a second error discriminator.
|
|
1091
|
+
| 'command-failed'
|
|
1092
|
+
| 'system-failed';
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* Discriminated `.detail` payload per `.code`.
|
|
1096
|
+
*
|
|
1097
|
+
* Narrowed via `switch (err.code)` against `EcsErrorCode`. Empty-detail entries
|
|
1098
|
+
* (legacy errors without `.detail`) are intentionally omitted from this map —
|
|
1099
|
+
* only the w5 family + `cyclic-injection` carry structured payloads today.
|
|
1100
|
+
*/
|
|
1101
|
+
export type EcsErrorDetail =
|
|
1102
|
+
| {
|
|
1103
|
+
readonly code: 'shared-kernel-ineligible';
|
|
1104
|
+
readonly kernelName: string;
|
|
1105
|
+
readonly reason: string;
|
|
1106
|
+
}
|
|
1107
|
+
| {
|
|
1108
|
+
readonly code: 'shared-kernel-failed';
|
|
1109
|
+
readonly kernelName: string;
|
|
1110
|
+
readonly worldIdentity: string;
|
|
1111
|
+
readonly cause: unknown;
|
|
1112
|
+
readonly partialWrite: boolean;
|
|
1113
|
+
readonly retryable: false;
|
|
1114
|
+
}
|
|
1115
|
+
| { readonly code: 'world-poisoned'; readonly worldIdentity: string; readonly fault: unknown }
|
|
1116
|
+
| { readonly code: 'sparse-storage-requires-tag'; readonly componentName: string }
|
|
1117
|
+
| {
|
|
1118
|
+
readonly code: 'query-descriptor-conflict';
|
|
1119
|
+
readonly componentName: string;
|
|
1120
|
+
readonly roles: readonly string[];
|
|
1121
|
+
}
|
|
1122
|
+
| { readonly code: 'query-data-requires-fields'; readonly componentName: string }
|
|
1123
|
+
| { readonly code: 'query-span-unavailable'; readonly reason: QuerySpanUnavailableReason }
|
|
1124
|
+
| {
|
|
1125
|
+
readonly code: 'query-iteration-invalidated';
|
|
1126
|
+
readonly expectedStructureEpoch: number;
|
|
1127
|
+
readonly actualStructureEpoch: number;
|
|
1128
|
+
}
|
|
1129
|
+
| { readonly code: 'query-iteration-active' }
|
|
1130
|
+
| { readonly code: 'change-epoch-exhausted'; readonly epoch: number }
|
|
1131
|
+
| { readonly code: 'unique-ref-released'; readonly handle: number; readonly target: string }
|
|
1132
|
+
| {
|
|
1133
|
+
readonly code: 'unique-ref-double-release';
|
|
1134
|
+
readonly handle: number;
|
|
1135
|
+
readonly target: string;
|
|
1136
|
+
}
|
|
1137
|
+
// feat-20260614 M3 — SharedRefStore detail variants (+2).
|
|
1138
|
+
| { readonly code: 'shared-ref-released'; readonly handle: number; readonly target: string }
|
|
1139
|
+
| {
|
|
1140
|
+
readonly code: 'shared-ref-double-release';
|
|
1141
|
+
readonly handle: number;
|
|
1142
|
+
readonly target: string;
|
|
1143
|
+
readonly rc: number;
|
|
1144
|
+
}
|
|
1145
|
+
| {
|
|
1146
|
+
readonly code: 'shared-ref-payload-invalid';
|
|
1147
|
+
readonly target: string;
|
|
1148
|
+
readonly actual: 'null' | 'undefined';
|
|
1149
|
+
}
|
|
1150
|
+
// feat-20260614 M6 D-15 — builtin-slot fail-fast detail variant (+1).
|
|
1151
|
+
| { readonly code: 'builtin-slot-not-owned'; readonly slot: number }
|
|
1152
|
+
// feat-20260623-asset-handle-generation M4 — stale error detail variants (+2).
|
|
1153
|
+
| {
|
|
1154
|
+
readonly code: 'shared-ref-stale';
|
|
1155
|
+
readonly slot: number;
|
|
1156
|
+
readonly expectedGeneration: number;
|
|
1157
|
+
readonly actualGeneration: number;
|
|
1158
|
+
}
|
|
1159
|
+
| {
|
|
1160
|
+
readonly code: 'unique-ref-stale';
|
|
1161
|
+
readonly slot: number;
|
|
1162
|
+
readonly expectedGeneration: number;
|
|
1163
|
+
readonly actualGeneration: number;
|
|
1164
|
+
}
|
|
1165
|
+
| { readonly code: 'managed-buffer-out-of-bounds'; readonly index: number; readonly size: number }
|
|
1166
|
+
| {
|
|
1167
|
+
readonly code: 'managed-buffer-shrink-not-supported';
|
|
1168
|
+
readonly requested: number;
|
|
1169
|
+
readonly current: number;
|
|
1170
|
+
}
|
|
1171
|
+
// feat-20260514 surviving managed-array-* discriminated detail variant (1).
|
|
1172
|
+
| {
|
|
1173
|
+
readonly code: 'managed-array-element-type-not-allowed';
|
|
1174
|
+
readonly fieldName: string;
|
|
1175
|
+
readonly elementType: string;
|
|
1176
|
+
readonly hint: string;
|
|
1177
|
+
}
|
|
1178
|
+
// feat-20260515-buffer-array-vocab-collapse w11 collapsed-vocab detail
|
|
1179
|
+
// variants (4). Per-code field names are SSOT-anchored at AC-07 + plan-
|
|
1180
|
+
// strategy §2.4 §detail-list (NOT renamed for "consistency" — name follows
|
|
1181
|
+
// semantics).
|
|
1182
|
+
| {
|
|
1183
|
+
readonly code: 'fixed-size-mismatch';
|
|
1184
|
+
readonly expected: number;
|
|
1185
|
+
readonly actual: number;
|
|
1186
|
+
}
|
|
1187
|
+
| {
|
|
1188
|
+
readonly code: 'instance-transforms-stride-mismatch';
|
|
1189
|
+
readonly actualLength: number;
|
|
1190
|
+
readonly expectedStride: 16;
|
|
1191
|
+
}
|
|
1192
|
+
// feat-20260519-light-casters-point-spot-pbr w2 — PointLight / SpotLight
|
|
1193
|
+
// spawn-time payload bound violation (plan-strategy D-S3 a). detail.field
|
|
1194
|
+
// three-branch ('range' | 'innerOuter' | 'outerNinety') keeps four bound
|
|
1195
|
+
// violations under one code; AI users narrow on `.detail.field` after the
|
|
1196
|
+
// outer `switch (err.code)` to pick the specific recovery hint.
|
|
1197
|
+
| {
|
|
1198
|
+
readonly code: 'spawn-light-invalid-bounds';
|
|
1199
|
+
readonly field: 'range' | 'innerOuter' | 'outerNinety';
|
|
1200
|
+
readonly got: number;
|
|
1201
|
+
}
|
|
1202
|
+
// feat-20260520-2d-sprite-layer-mvp M-2 w13 — resource-setter bound
|
|
1203
|
+
// violation (plan-strategy D-4). receivedMode carries the rejected
|
|
1204
|
+
// payload number; receivedKey is optional so future resource
|
|
1205
|
+
// validators can share the same code while disambiguating which
|
|
1206
|
+
// resource produced the failure.
|
|
1207
|
+
| {
|
|
1208
|
+
readonly code: 'resource-invalid-value';
|
|
1209
|
+
readonly receivedMode: number;
|
|
1210
|
+
readonly receivedKey?: string;
|
|
1211
|
+
}
|
|
1212
|
+
// feat-20260521-sprite-atlas-animation M1 T-05 — sprite-animation tick
|
|
1213
|
+
// runtime invariant violation (plan-strategy D-1 + section 5 AC-09).
|
|
1214
|
+
// detail.field two-branch keeps the regions-length / frame-duration
|
|
1215
|
+
// invariants under one code; AI users narrow on `.detail.field` after
|
|
1216
|
+
// the outer `switch (err.code)` to pick the specific recovery hint
|
|
1217
|
+
// (charter P3 + P4). Two top-level variants give each `.field` branch
|
|
1218
|
+
// its own required sub-field shape so AI users get strong narrowing
|
|
1219
|
+
// inside `switch (err.detail.field)` without optional sub-fields
|
|
1220
|
+
// bleeding across branches.
|
|
1221
|
+
| {
|
|
1222
|
+
readonly code: 'sprite-animation-invalid';
|
|
1223
|
+
readonly field: 'regions-length';
|
|
1224
|
+
readonly regionsLength: number;
|
|
1225
|
+
readonly frameCount: number;
|
|
1226
|
+
}
|
|
1227
|
+
| {
|
|
1228
|
+
readonly code: 'sprite-animation-invalid';
|
|
1229
|
+
readonly field: 'frame-duration';
|
|
1230
|
+
readonly frameDuration: number;
|
|
1231
|
+
}
|
|
1232
|
+
// feat-20260531-ecs-relationship-abstraction-bidirectional-sync M2 — the 4
|
|
1233
|
+
// relationship-* discriminated detail variants (plan-strategy D-5). Each
|
|
1234
|
+
// carries the component name + the entities involved so AI users narrow on
|
|
1235
|
+
// `.code` then read `.detail` to locate the offending relationship surface.
|
|
1236
|
+
| {
|
|
1237
|
+
readonly code: 'relationship-self-cycle';
|
|
1238
|
+
readonly component: string;
|
|
1239
|
+
readonly entity: number;
|
|
1240
|
+
readonly ancestor: number;
|
|
1241
|
+
}
|
|
1242
|
+
| {
|
|
1243
|
+
readonly code: 'relationship-mirror-component-not-registered';
|
|
1244
|
+
readonly component: string;
|
|
1245
|
+
readonly mirror: string;
|
|
1246
|
+
}
|
|
1247
|
+
| {
|
|
1248
|
+
readonly code: 'relationship-mirror-field-type-mismatch';
|
|
1249
|
+
readonly component: string;
|
|
1250
|
+
readonly mirror: string;
|
|
1251
|
+
readonly field: string;
|
|
1252
|
+
readonly actualType: string;
|
|
1253
|
+
}
|
|
1254
|
+
| {
|
|
1255
|
+
readonly code: 'relationship-detach-mismatch';
|
|
1256
|
+
readonly component: string;
|
|
1257
|
+
readonly child: number;
|
|
1258
|
+
readonly expectedParent: number;
|
|
1259
|
+
readonly actualParent: number;
|
|
1260
|
+
}
|
|
1261
|
+
// feat-20260602-drop-component-registration w16-a — scene instantiate
|
|
1262
|
+
// unknown-component fail-fast (30 -> 31). `.detail.name` carries the
|
|
1263
|
+
// component name that was never defined via defineComponent.
|
|
1264
|
+
| {
|
|
1265
|
+
readonly code: 'component-not-defined';
|
|
1266
|
+
readonly name: string;
|
|
1267
|
+
}
|
|
1268
|
+
// feat-20260602-archetype-stores-full-packed-entity M1 / w3 — removeComponent
|
|
1269
|
+
// essential-component rejection. `.detail.componentName` carries the essential
|
|
1270
|
+
// component name (the id=0 `Entity`).
|
|
1271
|
+
| {
|
|
1272
|
+
readonly code: 'remove-essential-component';
|
|
1273
|
+
readonly componentName: string;
|
|
1274
|
+
}
|
|
1275
|
+
// feat-20260608-scene-nesting-ecs-fication M1 / w9 — setSceneOverride
|
|
1276
|
+
// value-type rejection (plan-strategy D-9; requirements §Edge cases last
|
|
1277
|
+
// row + reviewer Issue 1). `.detail.comp` / `.detail.field` locate the
|
|
1278
|
+
// override target; `.detail.expectedType` carries the schema-declared
|
|
1279
|
+
// type literal (e.g. 'f32', 'bool', 'string'); `.detail.actualType`
|
|
1280
|
+
// carries the runtime `typeof value` (typed `unknown` because the
|
|
1281
|
+
// override write is not coerced — fail-fast surfaces the mismatch).
|
|
1282
|
+
| {
|
|
1283
|
+
readonly code: 'scene-override-type-mismatch';
|
|
1284
|
+
readonly comp: string;
|
|
1285
|
+
readonly field: string;
|
|
1286
|
+
readonly expectedType: string;
|
|
1287
|
+
readonly actualType: unknown;
|
|
1288
|
+
}
|
|
1289
|
+
// bug-20260615-spawn-data-unknown-field-fail-fast — spawn-data unknown-key
|
|
1290
|
+
// fail-fast. `.detail.component` names the schema's component, `.detail.field`
|
|
1291
|
+
// is the offending raw key, `.detail.knownFields` is the schema's full field
|
|
1292
|
+
// whitelist (sorted, used by AI users / hint formatters to surface "did you
|
|
1293
|
+
// mean" suggestions without round-tripping to the schema).
|
|
1294
|
+
| {
|
|
1295
|
+
readonly code: 'spawn-data-unknown-field';
|
|
1296
|
+
readonly component: string;
|
|
1297
|
+
readonly field: string;
|
|
1298
|
+
readonly knownFields: readonly string[];
|
|
1299
|
+
}
|
|
1300
|
+
// feat-20260625-sprite-instances-and-tilemap-terrain-static-batch M1 / w2 —
|
|
1301
|
+
// 3 discriminated detail variants for the SpriteInstances primitive
|
|
1302
|
+
// (declared in M1, fired at render-system-extract entry in M3).
|
|
1303
|
+
| {
|
|
1304
|
+
readonly code: 'sprite-instances-count-mismatch';
|
|
1305
|
+
readonly transformsLength: number;
|
|
1306
|
+
readonly regionsLength: number;
|
|
1307
|
+
readonly expectedStride: { readonly transforms: 16; readonly regions: 4 };
|
|
1308
|
+
}
|
|
1309
|
+
| {
|
|
1310
|
+
readonly code: 'sprite-instances-requires-sprite-shader';
|
|
1311
|
+
readonly entityId: number;
|
|
1312
|
+
readonly observedMaterialShaderId: string;
|
|
1313
|
+
}
|
|
1314
|
+
| {
|
|
1315
|
+
readonly code: 'sprite-instances-mutually-exclusive-with-instances';
|
|
1316
|
+
readonly entityId: number;
|
|
1317
|
+
}
|
|
1318
|
+
// feat-20260713-mount-override-component-add-and-shared-ref-round M2 / w9 —
|
|
1319
|
+
// shared-field value gate. `.detail.component` / `.detail.field` locate the
|
|
1320
|
+
// shared reference field; `.detail.fieldType` is the schema-declared type
|
|
1321
|
+
// literal (`shared<T>` scalar or `array<shared<T>>`); `.detail.actualValue`
|
|
1322
|
+
// is the offending non-handle value (typed `unknown` — a raw GUID string /
|
|
1323
|
+
// `{ guid }` / `{ kind }` object is not coerced, the fail-fast surfaces it);
|
|
1324
|
+
// `.detail.index` is the array element index for the array form (undefined for
|
|
1325
|
+
// the scalar form). AI users read `.detail.field` + `.detail.fieldType` to see
|
|
1326
|
+
// which reference needs `AssetRegistry.load(guid, kind) + allocSharedRef` before binding.
|
|
1327
|
+
| {
|
|
1328
|
+
readonly code: 'shared-field-invalid-value';
|
|
1329
|
+
readonly component: string;
|
|
1330
|
+
readonly field: string;
|
|
1331
|
+
readonly fieldType: string;
|
|
1332
|
+
readonly actualValue: unknown;
|
|
1333
|
+
readonly index?: number;
|
|
1334
|
+
}
|
|
1335
|
+
// feat-20260714-bevy-style-system-sets M1 / w3 — invalid-SystemSet detail.
|
|
1336
|
+
// `.detail.name` is the rejected token name; `.detail.registered` is a
|
|
1337
|
+
// deterministic snapshot of the current registry keys.
|
|
1338
|
+
| {
|
|
1339
|
+
readonly code: 'system-set-not-registered';
|
|
1340
|
+
readonly name: string;
|
|
1341
|
+
readonly registered: readonly string[];
|
|
1342
|
+
}
|
|
1343
|
+
| {
|
|
1344
|
+
readonly code: 'component-field-invalid-value';
|
|
1345
|
+
readonly entity: number | undefined;
|
|
1346
|
+
readonly component: string;
|
|
1347
|
+
readonly field: string;
|
|
1348
|
+
readonly received: unknown;
|
|
1349
|
+
readonly allowedValues: Readonly<Record<string, number>>;
|
|
1350
|
+
}
|
|
1351
|
+
| {
|
|
1352
|
+
readonly code: 'component-numeric-value-invalid';
|
|
1353
|
+
readonly entity: number | undefined;
|
|
1354
|
+
readonly component: string;
|
|
1355
|
+
readonly field: string;
|
|
1356
|
+
readonly received: number;
|
|
1357
|
+
readonly index?: number;
|
|
1358
|
+
}
|
|
1359
|
+
| {
|
|
1360
|
+
readonly code: 'managed-array-invalid-value';
|
|
1361
|
+
readonly component: string;
|
|
1362
|
+
readonly field: string;
|
|
1363
|
+
readonly fieldType: string;
|
|
1364
|
+
readonly actualValue: unknown;
|
|
1365
|
+
}
|
|
1366
|
+
// feat-20260714-bevy-style-system-sets M2 / w12 — structured cyclic-dependency
|
|
1367
|
+
// detail. `.detail.cycle` is the ordered cycle path array; consumers read
|
|
1368
|
+
// this instead of parsing the message string.
|
|
1369
|
+
| {
|
|
1370
|
+
readonly code: 'cyclic-dependency';
|
|
1371
|
+
readonly cycle: readonly string[];
|
|
1372
|
+
}
|
|
1373
|
+
| {
|
|
1374
|
+
readonly code: 'command-failed';
|
|
1375
|
+
readonly systemName: string;
|
|
1376
|
+
readonly schedule: string;
|
|
1377
|
+
readonly commandIndex: number;
|
|
1378
|
+
readonly commandKind: CommandKind;
|
|
1379
|
+
readonly cause: unknown;
|
|
1380
|
+
}
|
|
1381
|
+
| {
|
|
1382
|
+
readonly code: 'system-failed';
|
|
1383
|
+
readonly systemName: string;
|
|
1384
|
+
readonly schedule: string;
|
|
1385
|
+
readonly cause: unknown;
|
|
1386
|
+
readonly lastCommittedCommand: CommandCommitEvidence | null;
|
|
1387
|
+
};
|
|
1388
|
+
|
|
1389
|
+
/**
|
|
1390
|
+
* Layer-3 error envelope routed through managed-storage callbacks. Callers
|
|
1391
|
+
* narrow `detail` through the source-owned `EcsErrorDetail` union.
|
|
1392
|
+
*/
|
|
1393
|
+
export interface ManagedArrayErrorEnvelope {
|
|
1394
|
+
readonly code: EcsErrorCode;
|
|
1395
|
+
readonly hint: string;
|
|
1396
|
+
readonly expected: string;
|
|
1397
|
+
readonly detail: unknown;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
export type CommandKind = 'spawn' | 'despawn' | 'addComponent' | 'removeComponent';
|
|
1401
|
+
|
|
1402
|
+
export interface CommandCommitEvidence {
|
|
1403
|
+
readonly index: number;
|
|
1404
|
+
readonly kind: CommandKind;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
/** Expected command preflight failure, with the exact batch location. */
|
|
1408
|
+
export class CommandFailedError extends Error {
|
|
1409
|
+
override readonly name = 'CommandFailedError';
|
|
1410
|
+
readonly code = 'command-failed' as const;
|
|
1411
|
+
readonly expected = 'all deferred commands pass preflight before commit';
|
|
1412
|
+
readonly hint =
|
|
1413
|
+
'Inspect detail.cause, repair the command at detail.commandIndex, and run the World again.';
|
|
1414
|
+
override readonly cause: unknown;
|
|
1415
|
+
readonly detail: {
|
|
1416
|
+
readonly systemName: string;
|
|
1417
|
+
readonly schedule: string;
|
|
1418
|
+
readonly commandIndex: number;
|
|
1419
|
+
readonly commandKind: CommandKind;
|
|
1420
|
+
readonly cause: unknown;
|
|
1421
|
+
};
|
|
1422
|
+
|
|
1423
|
+
constructor(
|
|
1424
|
+
systemName: string,
|
|
1425
|
+
schedule: string,
|
|
1426
|
+
commandIndex: number,
|
|
1427
|
+
commandKind: CommandKind,
|
|
1428
|
+
cause: unknown,
|
|
1429
|
+
) {
|
|
1430
|
+
super(
|
|
1431
|
+
`Deferred command failed before commit in ${schedule}/${systemName} ` +
|
|
1432
|
+
`at command ${commandIndex} (${commandKind}).`,
|
|
1433
|
+
);
|
|
1434
|
+
this.cause = cause;
|
|
1435
|
+
this.detail = { systemName, schedule, commandIndex, commandKind, cause };
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/** Unknown system failure. The World is poisoned because row writes may exist. */
|
|
1440
|
+
export class SystemFailedError extends Error {
|
|
1441
|
+
override readonly name = 'SystemFailedError';
|
|
1442
|
+
readonly code = 'system-failed' as const;
|
|
1443
|
+
readonly expected = 'a system completes without throwing or returning a failed Result';
|
|
1444
|
+
readonly hint =
|
|
1445
|
+
'Inspect detail.cause, stop using this poisoned World, and rebuild it from the owning App.';
|
|
1446
|
+
override readonly cause: unknown;
|
|
1447
|
+
readonly detail: {
|
|
1448
|
+
readonly systemName: string;
|
|
1449
|
+
readonly schedule: string;
|
|
1450
|
+
readonly cause: unknown;
|
|
1451
|
+
readonly lastCommittedCommand: CommandCommitEvidence | null;
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1454
|
+
constructor(
|
|
1455
|
+
systemName: string,
|
|
1456
|
+
schedule: string,
|
|
1457
|
+
cause: unknown,
|
|
1458
|
+
lastCommittedCommand: CommandCommitEvidence | null = null,
|
|
1459
|
+
) {
|
|
1460
|
+
super(`System ${schedule}/${systemName} failed; World is poisoned.`);
|
|
1461
|
+
this.cause = cause;
|
|
1462
|
+
this.detail = { systemName, schedule, cause, lastCommittedCommand };
|
|
1463
|
+
}
|
|
1464
|
+
}
|