@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,201 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { Component } from '../component';
|
|
3
|
+
import { componentId, defineComponent } from '../component';
|
|
4
|
+
import { Disabled, Entity } from '../entity';
|
|
5
|
+
import type { EntityHandle } from '../entity-handle';
|
|
6
|
+
import { SparseStorageRequiresTagError } from '../errors';
|
|
7
|
+
import { Update } from '../schedule-token';
|
|
8
|
+
import { sparseTagHas } from '../storage/change-detection';
|
|
9
|
+
import { World } from '../world';
|
|
10
|
+
|
|
11
|
+
import { worldInternal } from '../world-internal';
|
|
12
|
+
|
|
13
|
+
describe('sparse tag storage', () => {
|
|
14
|
+
it('defaults components to table and rejects sparse components with fields', () => {
|
|
15
|
+
const TableTag = defineComponent('DefaultTableTag', {});
|
|
16
|
+
expect(TableTag.storage).toBe('table');
|
|
17
|
+
expect(Disabled.storage).toBe('table');
|
|
18
|
+
expect(() =>
|
|
19
|
+
defineComponent('InvalidSparseData', { value: 'f32' }, { storage: 'sparse' }),
|
|
20
|
+
).toThrow(SparseStorageRequiresTagError);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('shares one Table across Archetypes that differ only by sparse membership', () => {
|
|
24
|
+
const Position = defineComponent('SharedTablePosition', { x: 'f32' });
|
|
25
|
+
const Selected = defineComponent('SharedTableSelected', {}, { storage: 'sparse' });
|
|
26
|
+
const world = new World();
|
|
27
|
+
const plain = world.spawn({ component: Position, data: { x: 1 } }).unwrap();
|
|
28
|
+
const selected = world
|
|
29
|
+
.spawn({ component: Position, data: { x: 2 } }, { component: Selected, data: {} })
|
|
30
|
+
.unwrap();
|
|
31
|
+
const graph = world[worldInternal].getGraph();
|
|
32
|
+
const plainArchetype = world[worldInternal].getEntityArchetype(plain);
|
|
33
|
+
const selectedArchetype = world[worldInternal].getEntityArchetype(selected);
|
|
34
|
+
|
|
35
|
+
expect(plainArchetype).not.toBe(selectedArchetype);
|
|
36
|
+
expect(plainArchetype?.tableId).toBe(selectedArchetype?.tableId);
|
|
37
|
+
expect(graph.tables).toHaveLength(1);
|
|
38
|
+
expect(graph.tables[0]?.components.map((component: Component) => component.name)).toEqual([
|
|
39
|
+
'Entity',
|
|
40
|
+
'SharedTablePosition',
|
|
41
|
+
]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('flips sparse membership without changing any Table state', () => {
|
|
45
|
+
const Wide = defineComponent('SparseFlipWide', {
|
|
46
|
+
a: 'f32',
|
|
47
|
+
b: 'f32',
|
|
48
|
+
c: 'f32',
|
|
49
|
+
d: 'f32',
|
|
50
|
+
});
|
|
51
|
+
const Selected = defineComponent('SparseFlipSelected', {}, { storage: 'sparse' });
|
|
52
|
+
const world = new World();
|
|
53
|
+
const entity = world.spawn({ component: Wide, data: { a: 1, b: 2, c: 3, d: 4 } }).unwrap();
|
|
54
|
+
const graph = world[worldInternal].getGraph();
|
|
55
|
+
const sourceArchetype = world[worldInternal].getEntityArchetype(entity);
|
|
56
|
+
if (sourceArchetype === undefined) throw new Error('source archetype missing');
|
|
57
|
+
const table = graph.tables[sourceArchetype.tableId];
|
|
58
|
+
if (table === undefined) throw new Error('table missing');
|
|
59
|
+
const tableRow = sourceArchetype.rows[0] ?? -1;
|
|
60
|
+
const before = {
|
|
61
|
+
size: table.size,
|
|
62
|
+
capacity: table.capacity,
|
|
63
|
+
version: table.version,
|
|
64
|
+
fields: [...table.storage.values()].flatMap(({ fields }) =>
|
|
65
|
+
[...fields.values()].map((column) => ({
|
|
66
|
+
buffer: column.view.buffer,
|
|
67
|
+
bytes: new Uint8Array(column.view.buffer).slice(),
|
|
68
|
+
})),
|
|
69
|
+
),
|
|
70
|
+
epochs: [...table.storage.values()].map(({ epochs }) => ({
|
|
71
|
+
added: epochs.added.slice(),
|
|
72
|
+
changed: epochs.changed.slice(),
|
|
73
|
+
})),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
world.addComponent(entity, { component: Selected, data: {} }).unwrap();
|
|
77
|
+
const selectedArchetype = world[worldInternal].getEntityArchetype(entity);
|
|
78
|
+
expect(selectedArchetype?.tableId).toBe(table.id);
|
|
79
|
+
expect(selectedArchetype?.rows[0]).toBe(tableRow);
|
|
80
|
+
expect(table.size).toBe(before.size);
|
|
81
|
+
expect(table.capacity).toBe(before.capacity);
|
|
82
|
+
expect(table.version).toBe(before.version);
|
|
83
|
+
for (const [index, column] of before.fields.entries()) {
|
|
84
|
+
const current = [...table.storage.values()].flatMap(({ fields }) => [...fields.values()])[
|
|
85
|
+
index
|
|
86
|
+
];
|
|
87
|
+
expect(current?.view.buffer).toBe(column.buffer);
|
|
88
|
+
expect(new Uint8Array(current?.view.buffer ?? new ArrayBuffer(0))).toEqual(column.bytes);
|
|
89
|
+
}
|
|
90
|
+
expect(
|
|
91
|
+
[...table.storage.values()].map(({ epochs }) => ({
|
|
92
|
+
added: epochs.added,
|
|
93
|
+
changed: epochs.changed,
|
|
94
|
+
})),
|
|
95
|
+
).toEqual(before.epochs);
|
|
96
|
+
|
|
97
|
+
world.removeComponent(entity, Selected).unwrap();
|
|
98
|
+
expect(world[worldInternal].getEntityArchetype(entity)?.tableId).toBe(table.id);
|
|
99
|
+
expect(table.size).toBe(before.size);
|
|
100
|
+
expect(table.version).toBe(before.version);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('deduplicates dense table queries and routes sparse predicates through Archetypes', () => {
|
|
104
|
+
const Position = defineComponent('SparseQueryPosition', { x: 'f32' });
|
|
105
|
+
const Selected = defineComponent('SparseQuerySelected', {}, { storage: 'sparse' });
|
|
106
|
+
const world = new World();
|
|
107
|
+
const first = world.spawn({ component: Position, data: { x: 1 } }).unwrap();
|
|
108
|
+
const second = world
|
|
109
|
+
.spawn({ component: Position, data: { x: 2 } }, { component: Selected, data: {} })
|
|
110
|
+
.unwrap();
|
|
111
|
+
|
|
112
|
+
expect(Array.from(world.query({ read: [Position] }).unwrap(), (row) => row.entity)).toEqual([
|
|
113
|
+
first,
|
|
114
|
+
second,
|
|
115
|
+
]);
|
|
116
|
+
const spans = [
|
|
117
|
+
...world
|
|
118
|
+
.query({ read: [Position] })
|
|
119
|
+
.unwrap()
|
|
120
|
+
.spans()
|
|
121
|
+
.unwrap(),
|
|
122
|
+
];
|
|
123
|
+
expect(spans).toHaveLength(1);
|
|
124
|
+
expect(spans[0]?.length).toBe(2);
|
|
125
|
+
expect([...world.query({ with: [Selected] }).unwrap()].map((row) => row.entity)).toEqual([
|
|
126
|
+
second,
|
|
127
|
+
]);
|
|
128
|
+
expect([...world.query({ without: [Selected] }).unwrap()].map((row) => row.entity)).toEqual([
|
|
129
|
+
first,
|
|
130
|
+
]);
|
|
131
|
+
const sparseSpan = world
|
|
132
|
+
.query({ with: [Selected] })
|
|
133
|
+
.unwrap()
|
|
134
|
+
.spans();
|
|
135
|
+
expect(sparseSpan.ok).toBe(false);
|
|
136
|
+
if (!sparseSpan.ok) expect(sparseSpan.error.detail.reason).toBe('sparse-component');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('keeps sparse Added and Changed evidence generation-safe', () => {
|
|
140
|
+
const Selected = defineComponent('SparseEpochSelected', {}, { storage: 'sparse' });
|
|
141
|
+
const world = new World();
|
|
142
|
+
const added = world.query({ added: [Selected] }).unwrap();
|
|
143
|
+
const changed = world.query({ changed: [Selected] }).unwrap();
|
|
144
|
+
const first = world.spawn({ component: Selected, data: {} }).unwrap();
|
|
145
|
+
expect([...added].map((row) => row.entity)).toEqual([first]);
|
|
146
|
+
expect([...changed].map((row) => row.entity)).toEqual([first]);
|
|
147
|
+
|
|
148
|
+
world.set(first, Selected, {}).unwrap();
|
|
149
|
+
expect([...changed].map((row) => row.entity)).toEqual([first]);
|
|
150
|
+
world.despawn(first).unwrap();
|
|
151
|
+
const replacement = world.spawn().unwrap();
|
|
152
|
+
expect(replacement as number).not.toBe(first as number);
|
|
153
|
+
const set = world[worldInternal].getGraph().sparseTags.get(componentId(Selected));
|
|
154
|
+
expect(set === undefined ? false : sparseTagHas(set, replacement)).toBe(false);
|
|
155
|
+
expect([...world.query({ with: [Selected] }).unwrap()]).toEqual([]);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('routes deferred sparse spawn, add, and remove through the same storage boundary', () => {
|
|
159
|
+
const Selected = defineComponent('DeferredSparseSelected', {}, { storage: 'sparse' });
|
|
160
|
+
const world = new World();
|
|
161
|
+
const existing = world.spawn().unwrap();
|
|
162
|
+
let observed: EntityHandle[] = [];
|
|
163
|
+
|
|
164
|
+
world.addSystem(Update, {
|
|
165
|
+
name: 'deferred-sparse-producer',
|
|
166
|
+
queries: [],
|
|
167
|
+
before: ['deferred-sparse-consumer'],
|
|
168
|
+
fn: (_world, _queries, commands) => {
|
|
169
|
+
commands.spawn({ component: Selected, data: {} });
|
|
170
|
+
commands.addComponent(existing, { component: Selected, data: {} });
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
world.addSystem(Update, {
|
|
174
|
+
name: 'deferred-sparse-consumer',
|
|
175
|
+
queries: [{ with: [Selected] }],
|
|
176
|
+
fn: (_world, [selected], commands) => {
|
|
177
|
+
observed = Array.from(selected ?? [], (row) => row.entity);
|
|
178
|
+
commands.removeComponent(existing, Selected);
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(world.update(0).ok).toBe(true);
|
|
183
|
+
expect(observed).toHaveLength(2);
|
|
184
|
+
expect(world.get(existing, Selected).ok).toBe(false);
|
|
185
|
+
expect([...world.query({ with: [Selected] }).unwrap()]).toHaveLength(1);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('reports sparse logical identity without pretending it is Table storage', () => {
|
|
189
|
+
const Selected = defineComponent('SparseInspectSelected', {}, { storage: 'sparse' });
|
|
190
|
+
const world = new World();
|
|
191
|
+
const entity = world.spawn({ component: Selected, data: {} }).unwrap();
|
|
192
|
+
const inspection = world.inspect();
|
|
193
|
+
const archetype = inspection.archetypes.find((item) => item.entityCount === 1);
|
|
194
|
+
const table = inspection.tables[archetype?.tableId ?? -1];
|
|
195
|
+
|
|
196
|
+
expect(world.get(entity, Entity).unwrap().self).toBe(entity);
|
|
197
|
+
expect(archetype?.componentNames).toContain('SparseInspectSelected');
|
|
198
|
+
expect(table?.componentNames).not.toContain('SparseInspectSelected');
|
|
199
|
+
expect(inspection.activeComponents).toContain('SparseInspectSelected');
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Compile-time type assertions for World.spawn() field-level error signalling
|
|
2
|
+
// after the fallback overload is removed (AC-09 historical) + after the M2
|
|
3
|
+
// `ComponentData.data` widening to `Partial<ShapeOf<S>>` (AC-01 / AC-02 of
|
|
4
|
+
// feat-20260517-spawn-default-fallback).
|
|
5
|
+
//
|
|
6
|
+
// Anchors: requirements §AC-09 / requirements-decisions §F (AC-09 field-level
|
|
7
|
+
// error signal restoration); research §R5.2 (P3 landing — AC-09 must live in
|
|
8
|
+
// ECS strict-typed __tests__, not the runtime test files that erase Camera /
|
|
9
|
+
// World types via 'unknown'); plan-strategy §2 decision D-4. The historical
|
|
10
|
+
// "missing required field" probe migrated to the AC-02 partial-data probe in
|
|
11
|
+
// world.test-d.ts §t6.b once M2 widened ComponentData<S>.data to
|
|
12
|
+
// Partial<ShapeOf<S>>.
|
|
13
|
+
//
|
|
14
|
+
// The fallback overload 'spawn(...componentDatas: ComponentData[])' was the
|
|
15
|
+
// historical TS escape hatch — calls that supplied a wrong `data.<field>`
|
|
16
|
+
// type or omitted a required field landed on the loose overload and emitted
|
|
17
|
+
// only a vague 'No overload matches this call'. After T-04 deletes the
|
|
18
|
+
// fallback, the only remaining candidate is the mapped-tuple primary, which
|
|
19
|
+
// surfaces field-level diagnostics ('Type X is not assignable to type Y') --
|
|
20
|
+
// the test below asserts that contract for the wrong-VALUE shape (the
|
|
21
|
+
// missing-FIELD shape is now a valid call after M2 / AC-02 and is covered by
|
|
22
|
+
// world.test-d.ts §t6.b).
|
|
23
|
+
|
|
24
|
+
import { describe, it } from 'vitest';
|
|
25
|
+
import { defineComponent } from '../component';
|
|
26
|
+
import { World } from '../world';
|
|
27
|
+
|
|
28
|
+
describe('spawn field-level errors (AC-09 wrong-value shape)', () => {
|
|
29
|
+
it('AC-09 wrong field type: schema {value: string} rejects {value: number}', () => {
|
|
30
|
+
const Name = defineComponent('Name', { value: { type: 'string' } });
|
|
31
|
+
const world = new World();
|
|
32
|
+
world.spawn({
|
|
33
|
+
component: Name,
|
|
34
|
+
data: {
|
|
35
|
+
// @ts-expect-error AC-09 data.value must be string per schema; mapped-
|
|
36
|
+
// tuple primary inference fires field-level TS2322 on the offending key.
|
|
37
|
+
value: 42,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { Update } from '../schedule-token';
|
|
2
|
+
// AC-L5 stale-error compile-assertion vehicle.
|
|
3
|
+
//
|
|
4
|
+
// Sediments 5 method-level `expectTypeOf` assertions (the 4 `lookupAlive`
|
|
5
|
+
// callers — `get` / `set` / `addComponent` / `removeComponent` — plus
|
|
6
|
+
// `addSystem`) into a dedicated test-d file so that `tsc -b` (and vitest
|
|
7
|
+
// typecheck) certify, every release, the load-bearing type-layer contract
|
|
8
|
+
// of F-1's stale-error precision work:
|
|
9
|
+
//
|
|
10
|
+
// 1. Each of the 4 mutation/read methods returns `Result<..., EcsError>`
|
|
11
|
+
// whose error variant — when narrowed to `StaleEntityError` — exposes
|
|
12
|
+
// `.operation: string | undefined` and `.component: string | undefined`
|
|
13
|
+
// (charter proposition 4: errors are structured payloads, not strings).
|
|
14
|
+
// 2. `addSystem`'s class-method generic shape is preserved (a method-level
|
|
15
|
+
// simplified counterpart of `types.test-d.ts:[w15]` — that file owns the
|
|
16
|
+
// QueryRow inference theme; this file owns the signature
|
|
17
|
+
// shape theme; both coexist per plan-strategy §2 S-3 OQ-3).
|
|
18
|
+
//
|
|
19
|
+
// File structure mirrors `minimal-example.test-d.ts` (vitest test-d form,
|
|
20
|
+
// `expectTypeOf` based, single top-level `describe` per method group).
|
|
21
|
+
|
|
22
|
+
import type { Result } from '@forgeax/engine-types';
|
|
23
|
+
import { describe, expectTypeOf, it } from 'vitest';
|
|
24
|
+
import type { CommandBuffer } from '../commands';
|
|
25
|
+
import { defineComponent } from '../component';
|
|
26
|
+
import type { EntityHandle } from '../entity-handle';
|
|
27
|
+
import { entityIndex } from '../entity-handle';
|
|
28
|
+
import type { EcsErrorCode } from '../errors';
|
|
29
|
+
import { StaleEntityError } from '../errors';
|
|
30
|
+
import type { EcsError } from '../world';
|
|
31
|
+
import { World } from '../world';
|
|
32
|
+
|
|
33
|
+
describe('[w6] AC-L5 — world.get returns Result<ShapeOf<S>, EcsError> with structured stale payload', () => {
|
|
34
|
+
it('return type is Result<{ x: number; y: number }, EcsError>; stale narrowing exposes .operation/.component', () => {
|
|
35
|
+
const Position = defineComponent('Position', { x: { type: 'f32' }, y: { type: 'f32' } });
|
|
36
|
+
|
|
37
|
+
const world = new World();
|
|
38
|
+
const e = world.spawn({ component: Position, data: { x: 0, y: 0 } }).unwrap();
|
|
39
|
+
const r = world.get(e, Position);
|
|
40
|
+
|
|
41
|
+
// Signature shape: Result<ShapeOf<S>, EcsError>.
|
|
42
|
+
expectTypeOf(r).toEqualTypeOf<Result<{ readonly x: number; readonly y: number }, EcsError>>();
|
|
43
|
+
|
|
44
|
+
if (!r.ok) {
|
|
45
|
+
const err = r.error;
|
|
46
|
+
// EcsError is a union; narrowing to StaleEntityError surfaces .operation/.component.
|
|
47
|
+
if (err instanceof StaleEntityError) {
|
|
48
|
+
expectTypeOf(err.operation).toEqualTypeOf<string | undefined>();
|
|
49
|
+
expectTypeOf(err.component).toEqualTypeOf<string | undefined>();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('[w6] AC-L5 — world.set returns Result<void, EcsError> with structured stale payload', () => {
|
|
56
|
+
it('return type is Result<void, EcsError>; stale narrowing exposes .operation/.component', () => {
|
|
57
|
+
const Position = defineComponent('Position', { x: { type: 'f32' }, y: { type: 'f32' } });
|
|
58
|
+
|
|
59
|
+
const world = new World();
|
|
60
|
+
const e = world.spawn({ component: Position, data: { x: 0, y: 0 } }).unwrap();
|
|
61
|
+
const r = world.set(e, Position, { x: 10 });
|
|
62
|
+
|
|
63
|
+
expectTypeOf(r).toEqualTypeOf<Result<void, EcsError>>();
|
|
64
|
+
|
|
65
|
+
if (!r.ok) {
|
|
66
|
+
const err = r.error;
|
|
67
|
+
if (err instanceof StaleEntityError) {
|
|
68
|
+
expectTypeOf(err.operation).toEqualTypeOf<string | undefined>();
|
|
69
|
+
expectTypeOf(err.component).toEqualTypeOf<string | undefined>();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('[w6] AC-L5 — world.addComponent returns Result<void, EcsError> with structured stale payload', () => {
|
|
76
|
+
it('return type is Result<void, EcsError>; stale narrowing exposes .operation/.component', () => {
|
|
77
|
+
const Position = defineComponent('Position', { x: { type: 'f32' }, y: { type: 'f32' } });
|
|
78
|
+
const Velocity = defineComponent('Velocity', { dx: { type: 'f32' }, dy: { type: 'f32' } });
|
|
79
|
+
|
|
80
|
+
const world = new World();
|
|
81
|
+
const e = world.spawn({ component: Position, data: { x: 0, y: 0 } }).unwrap();
|
|
82
|
+
const r = world.addComponent(e, { component: Velocity, data: { dx: 1, dy: 0 } });
|
|
83
|
+
|
|
84
|
+
expectTypeOf(r).toEqualTypeOf<Result<void, EcsError>>();
|
|
85
|
+
|
|
86
|
+
if (!r.ok) {
|
|
87
|
+
const err = r.error;
|
|
88
|
+
if (err instanceof StaleEntityError) {
|
|
89
|
+
expectTypeOf(err.operation).toEqualTypeOf<string | undefined>();
|
|
90
|
+
expectTypeOf(err.component).toEqualTypeOf<string | undefined>();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('[w6] AC-L5 — world.removeComponent returns Result<void, EcsError> with structured stale payload', () => {
|
|
97
|
+
it('return type is Result<void, EcsError>; stale narrowing exposes .operation/.component', () => {
|
|
98
|
+
const Position = defineComponent('Position', { x: { type: 'f32' }, y: { type: 'f32' } });
|
|
99
|
+
|
|
100
|
+
const world = new World();
|
|
101
|
+
const e = world.spawn({ component: Position, data: { x: 0, y: 0 } }).unwrap();
|
|
102
|
+
const r = world.removeComponent(e, Position);
|
|
103
|
+
|
|
104
|
+
expectTypeOf(r).toEqualTypeOf<Result<void, EcsError>>();
|
|
105
|
+
|
|
106
|
+
if (!r.ok) {
|
|
107
|
+
const err = r.error;
|
|
108
|
+
if (err instanceof StaleEntityError) {
|
|
109
|
+
expectTypeOf(err.operation).toEqualTypeOf<string | undefined>();
|
|
110
|
+
expectTypeOf(err.component).toEqualTypeOf<string | undefined>();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('[w6] AC-L5 — world.addSystem<const Qs> method signature shape (simplified counterpart of types.test-d.ts:[w15])', () => {
|
|
117
|
+
it('addSystem returns void; fn receives mapped queryResults + CommandBuffer at the method-level call site', () => {
|
|
118
|
+
const Position = defineComponent('Position', { x: { type: 'f32' }, y: { type: 'f32' } });
|
|
119
|
+
|
|
120
|
+
const world = new World();
|
|
121
|
+
const ret = world.addSystem(Update, {
|
|
122
|
+
name: 'ac-l5-shape-probe',
|
|
123
|
+
queries: [{ read: [Position] }],
|
|
124
|
+
fn: (_world, queryResults, commands) => {
|
|
125
|
+
const query = queryResults[0];
|
|
126
|
+
if (query) {
|
|
127
|
+
for (const row of query) expectTypeOf(row.get(Position).x).toBeNumber();
|
|
128
|
+
}
|
|
129
|
+
expectTypeOf(commands).toEqualTypeOf<CommandBuffer>();
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Class-method `addSystem` reports a structured schedule-scope result.
|
|
134
|
+
expectTypeOf(ret.ok).toEqualTypeOf<boolean>();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// w3: type-level assertion — entityIndex returns number (u32 slot, low 24 bits)
|
|
139
|
+
describe('[w3] Entity codec type contract', () => {
|
|
140
|
+
it('entityIndex returns number', () => {
|
|
141
|
+
const fake = 0 as EntityHandle;
|
|
142
|
+
expectTypeOf(entityIndex(fake)).toEqualTypeOf<number>();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// w13: EcsErrorCode exhaustion test-d — store-level type completeness gate.
|
|
147
|
+
// Verifies the closed union includes 'shared-ref-stale' and 'unique-ref-stale'
|
|
148
|
+
// so that any exhaustive switch without these codes triggers TS never error.
|
|
149
|
+
// Distinct from w19 (resolveAssetHandle real consumption path, AC-10).
|
|
150
|
+
describe('[w13] EcsErrorCode union completeness — stale codes present', () => {
|
|
151
|
+
it('exhaustive switch over EcsErrorCode must include shared-ref-stale', () => {
|
|
152
|
+
// If shared-ref-stale is NOT in the union, this switch block would
|
|
153
|
+
// still compile because the type level never would not trigger —
|
|
154
|
+
// this test-d proves the code IS in the union.
|
|
155
|
+
const assertExhaustive = (code: EcsErrorCode): string => {
|
|
156
|
+
switch (code) {
|
|
157
|
+
case 'stale-entity':
|
|
158
|
+
case 'component-already-present':
|
|
159
|
+
case 'component-not-present':
|
|
160
|
+
case 'cyclic-dependency':
|
|
161
|
+
case 'resource-not-found':
|
|
162
|
+
case 'system-before-unknown':
|
|
163
|
+
case 'unique-ref-released':
|
|
164
|
+
case 'unique-ref-double-release':
|
|
165
|
+
case 'unique-ref-stale':
|
|
166
|
+
case 'shared-ref-released':
|
|
167
|
+
case 'shared-ref-double-release':
|
|
168
|
+
case 'shared-ref-payload-invalid':
|
|
169
|
+
case 'shared-ref-stale':
|
|
170
|
+
case 'builtin-slot-not-owned':
|
|
171
|
+
case 'managed-buffer-out-of-bounds':
|
|
172
|
+
case 'managed-buffer-shrink-not-supported':
|
|
173
|
+
case 'fixed-size-mismatch':
|
|
174
|
+
case 'managed-array-invalid-value':
|
|
175
|
+
case 'relationship-self-cycle':
|
|
176
|
+
case 'relationship-detach-mismatch':
|
|
177
|
+
case 'remove-essential-component':
|
|
178
|
+
case 'spawn-data-unknown-field':
|
|
179
|
+
// feat-20260713-mount-override-component-add-and-shared-ref-round M2 / w9
|
|
180
|
+
case 'shared-field-invalid-value':
|
|
181
|
+
// feat-20260714-bevy-style-system-sets M1 / w3
|
|
182
|
+
case 'system-set-not-registered':
|
|
183
|
+
case 'time-delta-invalid':
|
|
184
|
+
case 'time-config-invalid':
|
|
185
|
+
case 'schedule-scope-mismatch':
|
|
186
|
+
case 'component-field-invalid-value':
|
|
187
|
+
case 'component-numeric-value-invalid':
|
|
188
|
+
case 'shared-kernel-ineligible':
|
|
189
|
+
case 'shared-kernel-failed':
|
|
190
|
+
case 'world-poisoned':
|
|
191
|
+
case 'command-failed':
|
|
192
|
+
case 'system-failed':
|
|
193
|
+
return code;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
expectTypeOf(assertExhaustive).toBeFunction();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('shared-ref-stale is assignable to EcsErrorCode', () => {
|
|
200
|
+
const stale: EcsErrorCode = 'shared-ref-stale';
|
|
201
|
+
expectTypeOf(stale).toEqualTypeOf<'shared-ref-stale'>();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('unique-ref-stale is assignable to EcsErrorCode', () => {
|
|
205
|
+
const stale: EcsErrorCode = 'unique-ref-stale';
|
|
206
|
+
expectTypeOf(stale).toEqualTypeOf<'unique-ref-stale'>();
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// w9 --- AC-01 type-level FieldValueType<'string'> contract.
|
|
2
|
+
//
|
|
3
|
+
// Locks AC-01: the `'string'` schema-vocab keyword resolves through
|
|
4
|
+
// `FieldValueType<...>` to a native JS `string`. After the StringView
|
|
5
|
+
// collapse (D-R1, requirements 2.2), there is no longer a 3-arm symmetry
|
|
6
|
+
// between StringView / FixedArrayView / VarArrayView --- the string field
|
|
7
|
+
// reads directly as a JS string by-reference, leaving the array fields
|
|
8
|
+
// alone in the view-class category.
|
|
9
|
+
//
|
|
10
|
+
// This file is a focused FieldValueType<'string'> contract test; the prior
|
|
11
|
+
// 3-arm view-class symmetry content was retired together with StringView.
|
|
12
|
+
|
|
13
|
+
import { describe, expectTypeOf, it } from 'vitest';
|
|
14
|
+
import type { FieldValueType, ShapeOf } from '../component';
|
|
15
|
+
|
|
16
|
+
describe('w9 --- FieldValueType<"string"> resolves to native JS string (AC-01)', () => {
|
|
17
|
+
it('FieldValueType<"string"> = string', () => {
|
|
18
|
+
expectTypeOf<FieldValueType<'string'>>().toEqualTypeOf<string>();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('ShapeOf threads "string" through FieldValueType to a native string field', () => {
|
|
22
|
+
type S = { value: 'string' };
|
|
23
|
+
type Expected = { value: string };
|
|
24
|
+
expectTypeOf<ShapeOf<S>>().toEqualTypeOf<Expected>();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// w12 --- 'string' SchemaVocabKeyword + FieldValueType<'string'> = string.
|
|
2
|
+
//
|
|
3
|
+
// Locks two tier-2 invariants after the StringView collapse (D-R1):
|
|
4
|
+
//
|
|
5
|
+
// 1. 'string' remains a member of the closed `SchemaVocabKeyword` union
|
|
6
|
+
// (the union member count is unchanged at 7 --- the collapse swapped
|
|
7
|
+
// the value shape only, not the keyword surface).
|
|
8
|
+
//
|
|
9
|
+
// 2. `FieldValueType<'string'>` resolves to a native JS `string` (not
|
|
10
|
+
// the retired StringView wrapper). `ShapeOf<{ value: 'string' }>`
|
|
11
|
+
// threads through to `{ value: string }`.
|
|
12
|
+
//
|
|
13
|
+
// The prior file's StringView 5-surface assertions and JS-string
|
|
14
|
+
// prototype-method `@ts-expect-error` cases were retired together with
|
|
15
|
+
// StringView; the file survives as the focused vocab + FieldValueType
|
|
16
|
+
// contract test.
|
|
17
|
+
|
|
18
|
+
import { describe, expectTypeOf, it } from 'vitest';
|
|
19
|
+
import type {
|
|
20
|
+
defineComponent,
|
|
21
|
+
FieldValueType,
|
|
22
|
+
SchemaOf,
|
|
23
|
+
SchemaVocabKeyword,
|
|
24
|
+
ShapeOf,
|
|
25
|
+
} from '../component';
|
|
26
|
+
|
|
27
|
+
describe('w12 --- "string" remains a SchemaVocabKeyword member (closed-union invariant)', () => {
|
|
28
|
+
it("'string' extends SchemaVocabKeyword", () => {
|
|
29
|
+
expectTypeOf<'string'>().toMatchTypeOf<SchemaVocabKeyword>();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('w12 --- FieldValueType<"string"> = string (post-collapse)', () => {
|
|
34
|
+
it("FieldValueType<'string'> = string", () => {
|
|
35
|
+
expectTypeOf<FieldValueType<'string'>>().toEqualTypeOf<string>();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('ShapeOf threads "string" through FieldValueType', () => {
|
|
39
|
+
type S = { value: 'string' };
|
|
40
|
+
type Expected = { value: string };
|
|
41
|
+
expectTypeOf<ShapeOf<S>>().toEqualTypeOf<Expected>();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('three application points all infer the value field as string', () => {
|
|
45
|
+
type Foo = ReturnType<typeof defineComponent<'Foo', { value: 'string' }>>;
|
|
46
|
+
type FooShape = ShapeOf<SchemaOf<Foo>>;
|
|
47
|
+
expectTypeOf<FooShape['value']>().toEqualTypeOf<string>();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { defineComponent } from '../component';
|
|
3
|
+
import { World } from '../world';
|
|
4
|
+
|
|
5
|
+
describe('structureEpoch boundaries', () => {
|
|
6
|
+
it('reports a strictly increasing revision for each successful structure write', () => {
|
|
7
|
+
const Tag = defineComponent('StructureEpochBoundaryTag', {});
|
|
8
|
+
const world = new World();
|
|
9
|
+
const values = [world.getStructureEpoch()];
|
|
10
|
+
const entity = world.spawn({ component: Tag, data: {} }).unwrap();
|
|
11
|
+
values.push(world.getStructureEpoch());
|
|
12
|
+
world
|
|
13
|
+
.addComponent(entity, {
|
|
14
|
+
component: defineComponent('StructureEpochBoundaryExtra', { value: 'f32' }),
|
|
15
|
+
data: { value: 1 },
|
|
16
|
+
})
|
|
17
|
+
.unwrap();
|
|
18
|
+
values.push(world.getStructureEpoch());
|
|
19
|
+
world.despawn(entity).unwrap();
|
|
20
|
+
values.push(world.getStructureEpoch());
|
|
21
|
+
|
|
22
|
+
expect(values).toEqual([...values].sort((left, right) => left - right));
|
|
23
|
+
expect(new Set(values).size).toBe(values.length);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('keeps scalar writes and stale writes outside the structure revision', () => {
|
|
27
|
+
const Value = defineComponent('StructureEpochBoundaryValue', { value: 'f32' });
|
|
28
|
+
const world = new World();
|
|
29
|
+
const entity = world.spawn({ component: Value, data: { value: 1 } }).unwrap();
|
|
30
|
+
const epoch = world.getStructureEpoch();
|
|
31
|
+
|
|
32
|
+
world.set(entity, Value, { value: 3 }).unwrap();
|
|
33
|
+
expect(world.getStructureEpoch()).toBe(epoch);
|
|
34
|
+
world.despawn(entity).unwrap();
|
|
35
|
+
const afterDespawn = world.getStructureEpoch();
|
|
36
|
+
expect(world.set(entity, Value, { value: 4 }).ok).toBe(false);
|
|
37
|
+
expect(world.getStructureEpoch()).toBe(afterDespawn);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { defineComponent } from '../component';
|
|
3
|
+
import { World } from '../world';
|
|
4
|
+
|
|
5
|
+
describe('World structureEpoch', () => {
|
|
6
|
+
it('bumps for entity and component structure writes', () => {
|
|
7
|
+
const Position = defineComponent('StructureEpochPosition', { x: 'f32' });
|
|
8
|
+
const Velocity = defineComponent('StructureEpochVelocity', { dx: 'f32' });
|
|
9
|
+
const world = new World();
|
|
10
|
+
const initial = world.getStructureEpoch();
|
|
11
|
+
|
|
12
|
+
const entity = world.spawn({ component: Position, data: { x: 1 } }).unwrap();
|
|
13
|
+
const afterSpawn = world.getStructureEpoch();
|
|
14
|
+
world.addComponent(entity, { component: Velocity, data: { dx: 2 } }).unwrap();
|
|
15
|
+
const afterAdd = world.getStructureEpoch();
|
|
16
|
+
world.removeComponent(entity, Velocity).unwrap();
|
|
17
|
+
const afterRemove = world.getStructureEpoch();
|
|
18
|
+
world.despawn(entity).unwrap();
|
|
19
|
+
|
|
20
|
+
expect(afterSpawn).toBeGreaterThan(initial);
|
|
21
|
+
expect(afterAdd).toBeGreaterThan(afterSpawn);
|
|
22
|
+
expect(afterRemove).toBeGreaterThan(afterAdd);
|
|
23
|
+
expect(world.getStructureEpoch()).toBeGreaterThan(afterRemove);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('does not bump for scalar writes or no-op writes', () => {
|
|
27
|
+
const Position = defineComponent('StructureEpochScalar', { x: 'f32' });
|
|
28
|
+
const world = new World();
|
|
29
|
+
const entity = world.spawn({ component: Position, data: { x: 1 } }).unwrap();
|
|
30
|
+
const beforeWrite = world.getStructureEpoch();
|
|
31
|
+
|
|
32
|
+
world.set(entity, Position, { x: 2 }).unwrap();
|
|
33
|
+
expect(world.getStructureEpoch()).toBe(beforeWrite);
|
|
34
|
+
world.set(entity, Position, {}).unwrap();
|
|
35
|
+
expect(world.getStructureEpoch()).toBe(beforeWrite);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('does not let stale writes change the current World revision', () => {
|
|
39
|
+
const Position = defineComponent('StructureEpochStale', { x: 'f32' });
|
|
40
|
+
const world = new World();
|
|
41
|
+
const entity = world.spawn({ component: Position, data: { x: 1 } }).unwrap();
|
|
42
|
+
world.despawn(entity).unwrap();
|
|
43
|
+
const beforeStaleWrite = world.getStructureEpoch();
|
|
44
|
+
|
|
45
|
+
expect(world.set(entity, Position, { x: 2 }).ok).toBe(false);
|
|
46
|
+
expect(world.getStructureEpoch()).toBe(beforeStaleWrite);
|
|
47
|
+
});
|
|
48
|
+
});
|