@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
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import type { Component, ComponentSchema, FieldReflection } from '../component';
|
|
2
|
+
import { componentDefinition } from '../component-schema';
|
|
3
|
+
|
|
4
|
+
export class TimeDeltaInvalidError extends Error {
|
|
5
|
+
override readonly name = 'TimeDeltaInvalidError';
|
|
6
|
+
readonly code = 'time-delta-invalid' as const;
|
|
7
|
+
readonly expected = 'a finite delta greater than or equal to 0';
|
|
8
|
+
readonly hint = 'Call world.update(deltaSeconds) with a finite non-negative delta.';
|
|
9
|
+
readonly detail: { readonly received: number };
|
|
10
|
+
|
|
11
|
+
constructor(received: number) {
|
|
12
|
+
super(
|
|
13
|
+
`Invalid world.update delta: ${received}.\n expected: a finite delta greater than or equal to 0\n hint: Call world.update(deltaSeconds) with a finite non-negative delta.`,
|
|
14
|
+
);
|
|
15
|
+
this.detail = { received };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class TimeConfigInvalidError extends Error {
|
|
20
|
+
override readonly name = 'TimeConfigInvalidError';
|
|
21
|
+
readonly code = 'time-config-invalid' as const;
|
|
22
|
+
readonly expected: string;
|
|
23
|
+
readonly hint = 'Increase maxDeltaSeconds or decrease maxStepsPerUpdate or fixedDeltaSeconds.';
|
|
24
|
+
readonly detail: {
|
|
25
|
+
readonly fixedDeltaSeconds: number;
|
|
26
|
+
readonly maxStepsPerUpdate: number;
|
|
27
|
+
readonly maxDeltaSeconds: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
constructor(detail: TimeConfigInvalidError['detail']) {
|
|
31
|
+
const expected = 'maxDeltaSeconds >= (maxStepsPerUpdate + 1) * fixedDeltaSeconds';
|
|
32
|
+
super(
|
|
33
|
+
`Invalid World time policy.\n expected: ${expected}\n hint: Increase maxDeltaSeconds or decrease maxStepsPerUpdate or fixedDeltaSeconds.`,
|
|
34
|
+
);
|
|
35
|
+
this.expected = expected;
|
|
36
|
+
this.detail = detail;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class ScheduleScopeMismatchError extends Error {
|
|
41
|
+
override readonly name = 'ScheduleScopeMismatchError';
|
|
42
|
+
readonly code = 'schedule-scope-mismatch' as const;
|
|
43
|
+
readonly expected: string;
|
|
44
|
+
readonly hint: string;
|
|
45
|
+
readonly detail: {
|
|
46
|
+
readonly sourceSchedule: string;
|
|
47
|
+
readonly targetSchedule: string;
|
|
48
|
+
readonly reference?: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
constructor(sourceSchedule: string, targetSchedule: string, reference?: string) {
|
|
52
|
+
const expected = `a reference owned by ${sourceSchedule}`;
|
|
53
|
+
const hint = `The referenced item belongs to ${targetSchedule}; register and order it in ${sourceSchedule}.`;
|
|
54
|
+
super(`Schedule scope mismatch.\n expected: ${expected}\n hint: ${hint}`);
|
|
55
|
+
this.expected = expected;
|
|
56
|
+
this.hint = hint;
|
|
57
|
+
this.detail = { sourceSchedule, targetSchedule, ...(reference ? { reference } : {}) };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returned when a closed enum field receives a value outside its reflected
|
|
63
|
+
* labels. The write owner runs this before any archetype or column mutation.
|
|
64
|
+
*/
|
|
65
|
+
export class ComponentFieldInvalidValueError extends Error {
|
|
66
|
+
override readonly name = 'ComponentFieldInvalidValueError';
|
|
67
|
+
readonly code = 'component-field-invalid-value' as const;
|
|
68
|
+
readonly hint: string;
|
|
69
|
+
readonly expected: string;
|
|
70
|
+
readonly detail: {
|
|
71
|
+
readonly entity: number | undefined;
|
|
72
|
+
readonly component: string;
|
|
73
|
+
readonly field: string;
|
|
74
|
+
readonly received: unknown;
|
|
75
|
+
readonly allowedValues: Readonly<Record<string, number>>;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
constructor(
|
|
79
|
+
entity: number | undefined,
|
|
80
|
+
component: string,
|
|
81
|
+
field: string,
|
|
82
|
+
received: unknown,
|
|
83
|
+
allowedValues: Readonly<Record<string, number>>,
|
|
84
|
+
) {
|
|
85
|
+
const entries = Object.entries(allowedValues)
|
|
86
|
+
.map(([label, value]) => `${label}=${value}`)
|
|
87
|
+
.join(', ');
|
|
88
|
+
const expected = `${component}.${field} in { ${entries} }`;
|
|
89
|
+
const hint = `Set ${component}.${field} to one of the reflected enum values: ${entries}`;
|
|
90
|
+
super(
|
|
91
|
+
`${component}.${field} received an invalid enum value.\n` +
|
|
92
|
+
` code: component-field-invalid-value\n` +
|
|
93
|
+
` component: ${component}\n` +
|
|
94
|
+
` field: ${field}\n` +
|
|
95
|
+
` received: ${String(received)}\n` +
|
|
96
|
+
` expected: ${expected}\n` +
|
|
97
|
+
` hint: ${hint}`,
|
|
98
|
+
);
|
|
99
|
+
this.hint = hint;
|
|
100
|
+
this.expected = expected;
|
|
101
|
+
this.detail = { entity, component, field, received, allowedValues };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export class ComponentNumericValueInvalidError extends Error {
|
|
106
|
+
override readonly name = 'ComponentNumericValueInvalidError';
|
|
107
|
+
readonly code = 'component-numeric-value-invalid' as const;
|
|
108
|
+
readonly expected = 'a numeric value other than NaN';
|
|
109
|
+
readonly hint: string;
|
|
110
|
+
readonly detail: {
|
|
111
|
+
readonly entity: number | undefined;
|
|
112
|
+
readonly component: string;
|
|
113
|
+
readonly field: string;
|
|
114
|
+
readonly received: number;
|
|
115
|
+
readonly index?: number;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
constructor(
|
|
119
|
+
entity: number | undefined,
|
|
120
|
+
component: string,
|
|
121
|
+
field: string,
|
|
122
|
+
received: number,
|
|
123
|
+
index?: number,
|
|
124
|
+
) {
|
|
125
|
+
const location =
|
|
126
|
+
index === undefined ? `${component}.${field}` : `${component}.${field}[${index}]`;
|
|
127
|
+
const hint = `Replace NaN at ${location} with an authored numeric value; Number.POSITIVE_INFINITY remains valid where the component domain permits it.`;
|
|
128
|
+
super(
|
|
129
|
+
`${location} received NaN.\n` +
|
|
130
|
+
` code: component-numeric-value-invalid\n` +
|
|
131
|
+
` expected: a numeric value other than NaN\n` +
|
|
132
|
+
` hint: ${hint}`,
|
|
133
|
+
);
|
|
134
|
+
this.hint = hint;
|
|
135
|
+
this.detail = {
|
|
136
|
+
entity,
|
|
137
|
+
component,
|
|
138
|
+
field,
|
|
139
|
+
received,
|
|
140
|
+
...(index === undefined ? {} : { index }),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const NUMERIC_FIELD_TYPES = new Set(['f32', 'f64', 'i32', 'u32', 'i16', 'u16', 'i8', 'u8', 'enum']);
|
|
146
|
+
|
|
147
|
+
export function validateNumericFieldValues<S extends ComponentSchema>(
|
|
148
|
+
component: Component<string, S>,
|
|
149
|
+
raw: Partial<Record<string, unknown>> | undefined,
|
|
150
|
+
entity?: number,
|
|
151
|
+
): ComponentNumericValueInvalidError | null {
|
|
152
|
+
if (raw === undefined) return null;
|
|
153
|
+
const fields = componentDefinition(component).fields as Readonly<Record<string, FieldReflection>>;
|
|
154
|
+
const rawValues = raw as Record<string, unknown>;
|
|
155
|
+
for (const fieldName of Object.keys(rawValues)) {
|
|
156
|
+
const reflection = fields[fieldName];
|
|
157
|
+
if (reflection === undefined) continue;
|
|
158
|
+
const value = rawValues[fieldName];
|
|
159
|
+
if (NUMERIC_FIELD_TYPES.has(reflection.type)) {
|
|
160
|
+
if (typeof value === 'number' && Number.isNaN(value)) {
|
|
161
|
+
return new ComponentNumericValueInvalidError(entity, component.name, fieldName, value);
|
|
162
|
+
}
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (
|
|
166
|
+
reflection.arrayMeta === undefined ||
|
|
167
|
+
!NUMERIC_FIELD_TYPES.has(reflection.arrayMeta.elementType)
|
|
168
|
+
) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const length =
|
|
172
|
+
Array.isArray(value) || ArrayBuffer.isView(value)
|
|
173
|
+
? (value as { readonly length?: number }).length
|
|
174
|
+
: undefined;
|
|
175
|
+
if (length === undefined) continue;
|
|
176
|
+
const values = value as ArrayLike<unknown>;
|
|
177
|
+
for (let index = 0; index < length; index++) {
|
|
178
|
+
const received = values[index];
|
|
179
|
+
if (typeof received === 'number' && Number.isNaN(received)) {
|
|
180
|
+
return new ComponentNumericValueInvalidError(
|
|
181
|
+
entity,
|
|
182
|
+
component.name,
|
|
183
|
+
fieldName,
|
|
184
|
+
received,
|
|
185
|
+
index,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Returned before an ECS write when a managed `array<T>` field receives a
|
|
195
|
+
* value that the storage boundary cannot interpret as an array payload. The
|
|
196
|
+
* old column writer treated arbitrary objects as an empty payload, which
|
|
197
|
+
* silently changed the row while retaining no evidence of the caller error.
|
|
198
|
+
*/
|
|
199
|
+
export class ManagedArrayInvalidValueError extends Error {
|
|
200
|
+
override readonly name = 'ManagedArrayInvalidValueError';
|
|
201
|
+
readonly code = 'managed-array-invalid-value' as const;
|
|
202
|
+
readonly expected = 'an Array or TypedArray payload (or null/undefined to clear it)';
|
|
203
|
+
readonly hint: string;
|
|
204
|
+
readonly detail: {
|
|
205
|
+
readonly component: string;
|
|
206
|
+
readonly field: string;
|
|
207
|
+
readonly fieldType: string;
|
|
208
|
+
readonly actualValue: unknown;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
constructor(componentName: string, fieldName: string, fieldType: string, actualValue: unknown) {
|
|
212
|
+
const hint =
|
|
213
|
+
`Set ${componentName}.${fieldName} to a plain array or TypedArray matching ` +
|
|
214
|
+
`${fieldType}; use null or undefined to clear the managed value.`;
|
|
215
|
+
super(
|
|
216
|
+
`${componentName}.${fieldName}: managed array received an invalid value.\n` +
|
|
217
|
+
` code: managed-array-invalid-value\n` +
|
|
218
|
+
` fieldType: ${fieldType}\n` +
|
|
219
|
+
` expected: an Array or TypedArray payload (or null/undefined to clear it)\n` +
|
|
220
|
+
` hint: ${hint}`,
|
|
221
|
+
);
|
|
222
|
+
this.hint = hint;
|
|
223
|
+
this.detail = { component: componentName, field: fieldName, fieldType, actualValue };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Validate the closed enum fields present in a write payload. Enums without
|
|
229
|
+
* labels remain open numeric fields for compatibility with existing schemas.
|
|
230
|
+
*/
|
|
231
|
+
export function validateEnumFieldValues<S extends ComponentSchema>(
|
|
232
|
+
component: Component<string, S>,
|
|
233
|
+
raw: Partial<Record<string, unknown>> | undefined,
|
|
234
|
+
entity?: number,
|
|
235
|
+
): ComponentFieldInvalidValueError | null {
|
|
236
|
+
if (raw === undefined) return null;
|
|
237
|
+
const fields = componentDefinition(component).fields as Readonly<Record<string, FieldReflection>>;
|
|
238
|
+
const rawValues = raw as Record<string, unknown>;
|
|
239
|
+
for (const fieldName of Object.keys(rawValues)) {
|
|
240
|
+
const reflection = fields[fieldName];
|
|
241
|
+
if (reflection?.type !== 'enum' || reflection.labels === undefined) continue;
|
|
242
|
+
const value = rawValues[fieldName];
|
|
243
|
+
const allowedValues = Object.values(reflection.labels);
|
|
244
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || !allowedValues.includes(value)) {
|
|
245
|
+
return new ComponentFieldInvalidValueError(
|
|
246
|
+
entity,
|
|
247
|
+
component.name,
|
|
248
|
+
fieldName,
|
|
249
|
+
value,
|
|
250
|
+
reflection.labels,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
258
|
+
// feat-20260519-light-casters-point-spot-pbr w2 — closed-union evolution +1.
|
|
259
|
+
//
|
|
260
|
+
// Adds 1 new member 'spawn-light-invalid-bounds' to EcsErrorCode (23 -> 24).
|
|
261
|
+
// AGENTS.md section Error model evolution contract: minor (add member only).
|
|
262
|
+
// Triggered by PointLight / SpotLight spawn-time payload validation
|
|
263
|
+
// (plan-strategy D-S3 a). detail.field three-branch
|
|
264
|
+
// ('range' | 'innerOuter' | 'outerNinety') keeps the four bound-violation
|
|
265
|
+
// shapes under one error code so callers narrow first on `.code` then on
|
|
266
|
+
// `.detail.field` (charter P3 progressive disclosure).
|
|
267
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Returned via `Result.err` from `world.spawn` when a PointLight or SpotLight
|
|
271
|
+
* payload field is out of the documented bound. Four bound violations share
|
|
272
|
+
* one `.code` and discriminate via `.detail.field`:
|
|
273
|
+
*
|
|
274
|
+
* - `range` — PointLight / SpotLight `range < 0` or `Number.isNaN(range)`.
|
|
275
|
+
* Use `Number.POSITIVE_INFINITY` for an unlimited range or a non-negative
|
|
276
|
+
* meter value.
|
|
277
|
+
* - `innerOuter` — SpotLight `outerConeDeg <= innerConeDeg`. Inner cone is
|
|
278
|
+
* the saturated bright region; outer cone is the falloff edge.
|
|
279
|
+
* - `outerNinety` — SpotLight `outerConeDeg > 90`. KHR_lights_punctual upper
|
|
280
|
+
* bound. A spot light cone wider than 90 degrees becomes a point light;
|
|
281
|
+
* use PointLight instead.
|
|
282
|
+
* - `direction` — DirectionalLight / SpotLight `direction` is missing or a
|
|
283
|
+
* zero vector `[0, 0, 0]`. Direction has no default (there is no universal
|
|
284
|
+
* default direction): omitting it lands the array layer-3 all-zero, which is
|
|
285
|
+
* the same illegal state as an explicit zero vector. Supply a non-zero
|
|
286
|
+
* direction (feat-20260709 M2 / D-1, add-only union member).
|
|
287
|
+
*
|
|
288
|
+
* `.code = 'spawn-light-invalid-bounds'`
|
|
289
|
+
* `.detail.field` is derived from `keyof typeof SPAWN_LIGHT_INVALID_BOUNDS_POLICY`;
|
|
290
|
+
* `.detail.got` is `number | readonly number[]`.
|
|
291
|
+
* `.hint` — names the offending field plus the valid replacement form.
|
|
292
|
+
*/
|
|
293
|
+
const SPAWN_LIGHT_INVALID_BOUNDS_POLICY = {
|
|
294
|
+
range: {
|
|
295
|
+
expected: 'range >= 0 or Number.POSITIVE_INFINITY',
|
|
296
|
+
hint: (componentName: string, got: number | readonly number[]) =>
|
|
297
|
+
`${componentName}.range = ${got} is invalid; use Number.POSITIVE_INFINITY for unlimited range, or a non-negative meter value`,
|
|
298
|
+
},
|
|
299
|
+
innerOuter: {
|
|
300
|
+
expected: 'outerConeDeg > innerConeDeg',
|
|
301
|
+
hint: (componentName: string, got: number | readonly number[]) =>
|
|
302
|
+
`${componentName}.outerConeDeg <= innerConeDeg (got ${got}); inner cone is the saturated bright region, outer cone is the falloff edge; outerConeDeg > innerConeDeg required`,
|
|
303
|
+
},
|
|
304
|
+
outerNinety: {
|
|
305
|
+
expected: 'outerConeDeg <= 90 (KHR_lights_punctual upper bound)',
|
|
306
|
+
hint: (componentName: string, got: number | readonly number[]) =>
|
|
307
|
+
`${componentName}.outerConeDeg = ${got} > 90; a spot light cone wider than 90 degrees becomes a point light; use PointLight instead`,
|
|
308
|
+
},
|
|
309
|
+
direction: {
|
|
310
|
+
expected: 'direction is a non-zero [x, y, z] vector',
|
|
311
|
+
hint: (componentName: string, got: number | readonly number[]) =>
|
|
312
|
+
`${componentName}.direction is missing or a zero vector (got ${JSON.stringify(got)}); direction has no default, provide a non-zero direction, e.g. [-0.5, -1, -0.3]`,
|
|
313
|
+
},
|
|
314
|
+
} satisfies Record<
|
|
315
|
+
string,
|
|
316
|
+
{
|
|
317
|
+
readonly expected: string;
|
|
318
|
+
readonly hint: (componentName: string, got: number | readonly number[]) => string;
|
|
319
|
+
}
|
|
320
|
+
>;
|
|
321
|
+
|
|
322
|
+
export class SpawnLightInvalidBoundsError extends Error {
|
|
323
|
+
override readonly name = 'SpawnLightInvalidBoundsError';
|
|
324
|
+
readonly code = 'spawn-light-invalid-bounds' as const;
|
|
325
|
+
readonly hint: string;
|
|
326
|
+
readonly expected: string;
|
|
327
|
+
readonly detail: {
|
|
328
|
+
readonly field: keyof typeof SPAWN_LIGHT_INVALID_BOUNDS_POLICY;
|
|
329
|
+
readonly got: number | readonly number[];
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
constructor(
|
|
333
|
+
componentName: string,
|
|
334
|
+
field: keyof typeof SPAWN_LIGHT_INVALID_BOUNDS_POLICY,
|
|
335
|
+
got: number | readonly number[],
|
|
336
|
+
) {
|
|
337
|
+
const policy = SPAWN_LIGHT_INVALID_BOUNDS_POLICY[field];
|
|
338
|
+
const hint = policy.hint(componentName, got);
|
|
339
|
+
const expectedStr = policy.expected;
|
|
340
|
+
super(
|
|
341
|
+
`${componentName}: spawn payload bound violation.\n` +
|
|
342
|
+
` code: spawn-light-invalid-bounds\n` +
|
|
343
|
+
` component: ${componentName}\n` +
|
|
344
|
+
` field: ${field}\n` +
|
|
345
|
+
` got: ${got}\n` +
|
|
346
|
+
` expected: ${expectedStr}\n` +
|
|
347
|
+
` hint: ${hint}`,
|
|
348
|
+
);
|
|
349
|
+
this.hint = hint;
|
|
350
|
+
this.expected = expectedStr;
|
|
351
|
+
this.detail = { field, got };
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Returned via `Result.err` from resource-setter helpers (e.g.
|
|
357
|
+
* `setTransparentSortConfig`) when a numeric payload field violates the
|
|
358
|
+
* closed bound declared by the resource contract. The first consumer is
|
|
359
|
+
* `TransparentSortConfig.mode ∈ {0, 1, 2}` (plan-strategy D-4); future
|
|
360
|
+
* resource validators with the same shape reuse this code by routing
|
|
361
|
+
* through `.detail.receivedKey` to disambiguate which resource validator
|
|
362
|
+
* surfaced the failure.
|
|
363
|
+
*
|
|
364
|
+
* Closed-set kebab code consistent with `spawn-light-invalid-bounds`
|
|
365
|
+
* (feat-20260519 / w2); AI users consume via `switch (err.code)` exhaustive
|
|
366
|
+
* narrows + `err.detail.receivedMode` (or `err.detail.receivedKey` /
|
|
367
|
+
* `err.expected`) property access — never string-parse the message.
|
|
368
|
+
*
|
|
369
|
+
* `.code = 'resource-invalid-value'`
|
|
370
|
+
* `.detail = { receivedMode: number; receivedKey?: string }`
|
|
371
|
+
* `.hint` — direct copy-paste recovery (e.g. "0=layer-z, 1=layer-y,
|
|
372
|
+
* 2=layer-yz" for the sort-config case).
|
|
373
|
+
* `.expected` — the bound contract literal (e.g. "mode ∈ {0, 1, 2}").
|
|
374
|
+
*
|
|
375
|
+
* @reuses RhiError structured shape — same `.code / .expected / .hint /
|
|
376
|
+
* .detail` quadruple AI users consume across rhi + ecs.
|
|
377
|
+
*/
|
|
378
|
+
export class ResourceInvalidValueError extends Error {
|
|
379
|
+
override readonly name = 'ResourceInvalidValueError';
|
|
380
|
+
readonly code = 'resource-invalid-value' as const;
|
|
381
|
+
readonly hint: string;
|
|
382
|
+
readonly expected: string;
|
|
383
|
+
readonly detail: { readonly receivedMode: number; readonly receivedKey?: string };
|
|
384
|
+
|
|
385
|
+
constructor(
|
|
386
|
+
expected: string,
|
|
387
|
+
hint: string,
|
|
388
|
+
detail: { readonly receivedMode: number; readonly receivedKey?: string },
|
|
389
|
+
) {
|
|
390
|
+
const keyClause = detail.receivedKey === undefined ? '' : ` key: ${detail.receivedKey}\n`;
|
|
391
|
+
super(
|
|
392
|
+
`resource: invalid value.\n` +
|
|
393
|
+
` code: resource-invalid-value\n` +
|
|
394
|
+
keyClause +
|
|
395
|
+
` receivedMode: ${detail.receivedMode}\n` +
|
|
396
|
+
` expected: ${expected}\n` +
|
|
397
|
+
` hint: ${hint}`,
|
|
398
|
+
);
|
|
399
|
+
this.hint = hint;
|
|
400
|
+
this.expected = expected;
|
|
401
|
+
this.detail = detail;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
406
|
+
// feat-20260521-sprite-atlas-animation M1 T-05 — closed-union evolution +1.
|
|
407
|
+
//
|
|
408
|
+
// Adds 1 new member 'sprite-animation-invalid' to EcsErrorCode (25 -> 26).
|
|
409
|
+
// AGENTS.md §Error model evolution contract: minor (add member only).
|
|
410
|
+
// Same-shape add-only mirror of SpawnLightInvalidBoundsError (feat-20260519
|
|
411
|
+
// w2 line 736-776) and ResourceInvalidValueError (feat-20260520 w13 line
|
|
412
|
+
// 862) — the kebab `'<noun>-invalid-...'` series keeps `switch (err.code)`
|
|
413
|
+
// exhaustive narrows visually consistent (charter P4 consistent abstraction;
|
|
414
|
+
// research F-7 candidate A).
|
|
415
|
+
//
|
|
416
|
+
// Triggered by `spriteAnimationTickSystem` (packages/runtime/src/systems/
|
|
417
|
+
// sprite-animation-tick.ts, landed in M4 T-23) when an entity's
|
|
418
|
+
// `SpriteAnimation` row violates one of two runtime invariants:
|
|
419
|
+
//
|
|
420
|
+
// - field='regions-length' -> `regions.length !== frameCount * 4`
|
|
421
|
+
// - field='frame-duration' -> `frameDuration <= 0`
|
|
422
|
+
//
|
|
423
|
+
// `.detail.field` two-branch (charter P3: AI users branch once on
|
|
424
|
+
// `err.code` and once on `err.detail.field` to reach the recovery hint
|
|
425
|
+
// without parsing the message). Plan-strategy section 2 D-1 binds the
|
|
426
|
+
// detail field shape; M4 T-19 / T-20 / T-21 cover the runtime fail-fast
|
|
427
|
+
// paths end-to-end.
|
|
428
|
+
// ────────────────────────────────────────────────────────────────────────────
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Returned via `Result.err` from `spriteAnimationTickSystem` (M4 T-23) when
|
|
432
|
+
* an entity's `SpriteAnimation` row violates a runtime invariant.
|
|
433
|
+
* Two invariants share one `.code` and discriminate via `.detail.field`:
|
|
434
|
+
*
|
|
435
|
+
* - `regions-length` — `SpriteAnimation.regions.length !== frameCount * 4`.
|
|
436
|
+
* `regions` packs `[uMin, vMin, uW, vH]` per frame so the length must be
|
|
437
|
+
* exactly `frameCount * 4`. Detail carries the offending `regionsLength`
|
|
438
|
+
* alongside the declared `frameCount` so the hint can spell the exact
|
|
439
|
+
* delta in callsite-friendly numbers.
|
|
440
|
+
* - `frame-duration` — `SpriteAnimation.frameDuration <= 0` (covers both
|
|
441
|
+
* `frameDuration === 0` and `frameDuration < 0`; T-21 binds the negative
|
|
442
|
+
* case to the same arm so AI users handle both via a single
|
|
443
|
+
* `if (err.detail.field === 'frame-duration')` branch — charter P4
|
|
444
|
+
* consistent abstraction).
|
|
445
|
+
*
|
|
446
|
+
* `.code = 'sprite-animation-invalid'`
|
|
447
|
+
* `.detail = { field: 'regions-length', regionsLength, frameCount } |
|
|
448
|
+
* { field: 'frame-duration', frameDuration }`
|
|
449
|
+
*
|
|
450
|
+
* Two top-level detail variants give each `.field` branch its own
|
|
451
|
+
* required sub-field shape so AI users get strong narrowing inside
|
|
452
|
+
* `switch (err.detail.field)` without optional sub-fields bleeding
|
|
453
|
+
* across branches (mirrors `SpawnLightInvalidBoundsError`'s shared
|
|
454
|
+
* `got: number` shape but adapted because regions-length /
|
|
455
|
+
* frame-duration carry different sub-field counts).
|
|
456
|
+
*
|
|
457
|
+
* `.hint` — names the offending invariant plus the valid replacement form.
|
|
458
|
+
*/
|
|
459
|
+
export class SpriteAnimationInvalidError extends Error {
|
|
460
|
+
override readonly name = 'SpriteAnimationInvalidError';
|
|
461
|
+
readonly code = 'sprite-animation-invalid' as const;
|
|
462
|
+
readonly hint: string;
|
|
463
|
+
readonly expected: string;
|
|
464
|
+
readonly detail:
|
|
465
|
+
| {
|
|
466
|
+
readonly field: 'regions-length';
|
|
467
|
+
readonly regionsLength: number;
|
|
468
|
+
readonly frameCount: number;
|
|
469
|
+
}
|
|
470
|
+
| {
|
|
471
|
+
readonly field: 'frame-duration';
|
|
472
|
+
readonly frameDuration: number;
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
private static resolvePolicy(detail: SpriteAnimationInvalidError['detail']): {
|
|
476
|
+
readonly expected: string;
|
|
477
|
+
readonly hint: string;
|
|
478
|
+
} {
|
|
479
|
+
switch (detail.field) {
|
|
480
|
+
case 'regions-length':
|
|
481
|
+
return {
|
|
482
|
+
expected: 'SpriteAnimation.regions.length === frameCount * 4',
|
|
483
|
+
hint: `SpriteAnimation.regions.length = ${detail.regionsLength} does not match frameCount * 4 = ${detail.frameCount * 4}; pack 4 floats [uMin, vMin, uW, vH] per frame (see <name>.atlas.meta.json sidecar 'regions' map)`,
|
|
484
|
+
};
|
|
485
|
+
case 'frame-duration':
|
|
486
|
+
return {
|
|
487
|
+
expected: 'SpriteAnimation.frameDuration > 0',
|
|
488
|
+
hint: `SpriteAnimation.frameDuration = ${detail.frameDuration} is invalid; use a positive seconds-per-frame value (e.g. 0.1 = 10 fps)`,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
constructor(detail: SpriteAnimationInvalidError['detail']) {
|
|
494
|
+
const policy = SpriteAnimationInvalidError.resolvePolicy(detail);
|
|
495
|
+
super(
|
|
496
|
+
`SpriteAnimation: invariant violated.\n` +
|
|
497
|
+
` code: sprite-animation-invalid\n` +
|
|
498
|
+
` field: ${detail.field}\n` +
|
|
499
|
+
` expected: ${policy.expected}\n` +
|
|
500
|
+
` hint: ${policy.hint}`,
|
|
501
|
+
);
|
|
502
|
+
this.hint = policy.hint;
|
|
503
|
+
this.expected = policy.expected;
|
|
504
|
+
this.detail = detail;
|
|
505
|
+
}
|
|
506
|
+
}
|