@treenity/core 1.0.47 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chain.d.ts +20 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +59 -0
- package/dist/chain.js.map +1 -0
- package/dist/client/handle.d.ts +13 -0
- package/dist/client/handle.d.ts.map +1 -0
- package/dist/client/handle.js +46 -0
- package/dist/client/handle.js.map +1 -0
- package/dist/client/index.d.ts +21 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +5 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/trpc.d.ts +13 -0
- package/dist/client/trpc.d.ts.map +1 -0
- package/dist/client/trpc.js +78 -0
- package/dist/client/trpc.js.map +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -0
- package/dist/comp/handle.d.ts +14 -0
- package/dist/comp/handle.d.ts.map +1 -0
- package/dist/comp/handle.js +21 -0
- package/dist/comp/handle.js.map +1 -0
- package/dist/comp/index.d.ts +42 -0
- package/dist/comp/index.d.ts.map +1 -0
- package/dist/comp/index.js +126 -0
- package/dist/comp/index.js.map +1 -0
- package/dist/comp/needs.d.ts +26 -0
- package/dist/comp/needs.d.ts.map +1 -0
- package/dist/comp/needs.js +101 -0
- package/dist/comp/needs.js.map +1 -0
- package/dist/comp/validate.d.ts +13 -0
- package/dist/comp/validate.d.ts.map +1 -0
- package/dist/comp/validate.js +117 -0
- package/dist/comp/validate.js.map +1 -0
- package/dist/comp.d.ts +2 -0
- package/dist/comp.d.ts.map +1 -0
- package/dist/comp.js +2 -0
- package/dist/comp.js.map +1 -0
- package/dist/contexts/schema/index.d.ts +7 -0
- package/dist/contexts/schema/index.d.ts.map +1 -0
- package/dist/contexts/schema/index.js +2 -0
- package/dist/contexts/schema/index.js.map +1 -0
- package/dist/contexts/schema.d.ts +2 -0
- package/dist/contexts/schema.d.ts.map +1 -0
- package/dist/contexts/schema.js +2 -0
- package/dist/contexts/schema.js.map +1 -0
- package/dist/contexts/service/index.d.ts +31 -0
- package/dist/contexts/service/index.d.ts.map +1 -0
- package/dist/contexts/service/index.js +16 -0
- package/dist/contexts/service/index.js.map +1 -0
- package/dist/contexts/service.d.ts +1 -14
- package/dist/contexts/service.d.ts.map +1 -1
- package/dist/contexts/service.js +2 -0
- package/dist/contexts/service.js.map +1 -0
- package/dist/contexts/telegram/index.d.ts +19 -0
- package/dist/contexts/telegram/index.d.ts.map +1 -0
- package/dist/contexts/telegram/index.js +89 -0
- package/dist/contexts/telegram/index.js.map +1 -0
- package/dist/contexts/text/index.d.ts +7 -0
- package/dist/contexts/text/index.d.ts.map +1 -0
- package/dist/contexts/text/index.js +9 -0
- package/dist/contexts/text/index.js.map +1 -0
- package/dist/contexts/text.d.ts +2 -0
- package/dist/contexts/text.d.ts.map +1 -0
- package/dist/contexts/text.js +2 -0
- package/dist/contexts/text.js.map +1 -0
- package/dist/core/component.d.ts +41 -0
- package/dist/core/component.d.ts.map +1 -0
- package/dist/core/component.js +105 -0
- package/dist/core/component.js.map +1 -0
- package/dist/core/context.d.ts +5 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +3 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +8 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/path.d.ts +7 -0
- package/dist/core/path.d.ts.map +1 -0
- package/dist/core/path.js +37 -0
- package/dist/core/path.js.map +1 -0
- package/dist/core/registry.d.ts +16 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +100 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +2 -0
- package/dist/core.js.map +1 -0
- package/dist/log.d.ts +8 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +31 -0
- package/dist/log.js.map +1 -0
- package/dist/mod/discover.d.ts +4 -0
- package/dist/mod/discover.d.ts.map +1 -0
- package/dist/mod/discover.js +103 -0
- package/dist/mod/discover.js.map +1 -0
- package/dist/mod/examples/ticker/seed.d.ts +3 -0
- package/dist/mod/examples/ticker/seed.d.ts.map +1 -0
- package/dist/mod/examples/ticker/seed.js +16 -0
- package/dist/mod/examples/ticker/seed.js.map +1 -0
- package/dist/mod/examples/ticker/service.d.ts +2 -0
- package/dist/mod/examples/ticker/service.d.ts.map +1 -0
- package/dist/mod/examples/ticker/service.js +18 -0
- package/dist/mod/examples/ticker/service.js.map +1 -0
- package/dist/mod/examples/ticker/types.d.ts +16 -0
- package/dist/mod/examples/ticker/types.d.ts.map +1 -0
- package/dist/mod/examples/ticker/types.js +20 -0
- package/dist/mod/examples/ticker/types.js.map +1 -0
- package/dist/mod/examples/ticker/view.d.ts +2 -0
- package/dist/mod/examples/ticker/view.d.ts.map +1 -0
- package/dist/mod/examples/ticker/view.js +10 -0
- package/dist/mod/examples/ticker/view.js.map +1 -0
- package/dist/mod/index.d.ts +11 -0
- package/dist/mod/index.d.ts.map +1 -0
- package/dist/mod/index.js +8 -0
- package/dist/mod/index.js.map +1 -0
- package/dist/mod/loader.d.ts +18 -0
- package/dist/mod/loader.d.ts.map +1 -0
- package/dist/mod/loader.js +154 -0
- package/dist/mod/loader.js.map +1 -0
- package/dist/mod/optimistic.d.ts +34 -0
- package/dist/mod/optimistic.d.ts.map +1 -0
- package/dist/mod/optimistic.js +176 -0
- package/dist/mod/optimistic.js.map +1 -0
- package/dist/mod/prefab.d.ts +18 -0
- package/dist/mod/prefab.d.ts.map +1 -0
- package/dist/mod/prefab.js +42 -0
- package/dist/mod/prefab.js.map +1 -0
- package/dist/mod/tracking.d.ts +8 -0
- package/dist/mod/tracking.d.ts.map +1 -0
- package/dist/mod/tracking.js +44 -0
- package/dist/mod/tracking.js.map +1 -0
- package/dist/mod/types.d.ts +31 -0
- package/dist/mod/types.d.ts.map +1 -0
- package/dist/mod/types.js +5 -0
- package/dist/mod/types.js.map +1 -0
- package/dist/mod.d.ts +2 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +2 -0
- package/dist/mod.js.map +1 -0
- package/dist/mods/autostart/server.d.ts +2 -0
- package/dist/mods/autostart/server.d.ts.map +1 -0
- package/dist/mods/autostart/server.js +2 -0
- package/dist/mods/autostart/server.js.map +1 -0
- package/dist/mods/autostart/service.d.ts +14 -0
- package/dist/mods/autostart/service.d.ts.map +1 -0
- package/dist/mods/autostart/service.js +88 -0
- package/dist/mods/autostart/service.js.map +1 -0
- package/dist/mods/llm/index.d.ts +13 -0
- package/dist/mods/llm/index.d.ts.map +1 -0
- package/dist/mods/llm/index.js +43 -0
- package/dist/mods/llm/index.js.map +1 -0
- package/dist/mods/llm/server.d.ts +2 -0
- package/dist/mods/llm/server.d.ts.map +1 -0
- package/dist/mods/llm/server.js +2 -0
- package/dist/mods/llm/server.js.map +1 -0
- package/dist/mods/mcp/server.d.ts +3 -0
- package/dist/mods/mcp/server.d.ts.map +1 -0
- package/dist/mods/mcp/server.js +3 -0
- package/dist/mods/mcp/server.js.map +1 -0
- package/dist/mods/mcp/service.d.ts +2 -0
- package/dist/mods/mcp/service.d.ts.map +1 -0
- package/dist/mods/mcp/service.js +17 -0
- package/dist/mods/mcp/service.js.map +1 -0
- package/dist/mods/mcp/types.d.ts +5 -0
- package/dist/mods/mcp/types.d.ts.map +1 -0
- package/dist/mods/mcp/types.js +7 -0
- package/dist/mods/mcp/types.js.map +1 -0
- package/dist/mods/treenity/agent-port.d.ts +2 -0
- package/dist/mods/treenity/agent-port.d.ts.map +1 -0
- package/dist/mods/treenity/agent-port.js +76 -0
- package/dist/mods/treenity/agent-port.js.map +1 -0
- package/dist/mods/treenity/groups.d.ts +2 -0
- package/dist/mods/treenity/groups.d.ts.map +1 -0
- package/dist/mods/treenity/groups.js +18 -0
- package/dist/mods/treenity/groups.js.map +1 -0
- package/dist/mods/treenity/mod-type.d.ts +2 -0
- package/dist/mods/treenity/mod-type.d.ts.map +1 -0
- package/dist/mods/treenity/mod-type.js +9 -0
- package/dist/mods/treenity/mod-type.js.map +1 -0
- package/dist/mods/treenity/seed.d.ts +2 -0
- package/dist/mods/treenity/seed.d.ts.map +1 -0
- package/dist/mods/treenity/seed.js +53 -0
- package/dist/mods/treenity/seed.js.map +1 -0
- package/dist/mods/treenity/server.d.ts +5 -0
- package/dist/mods/treenity/server.d.ts.map +1 -0
- package/dist/mods/treenity/server.js +5 -0
- package/dist/mods/treenity/server.js.map +1 -0
- package/dist/mods/treenity/system.d.ts +36 -0
- package/dist/mods/treenity/system.d.ts.map +1 -0
- package/dist/mods/treenity/system.js +61 -0
- package/dist/mods/treenity/system.js.map +1 -0
- package/dist/mods/uix/client.d.ts +3 -0
- package/dist/mods/uix/client.d.ts.map +1 -0
- package/dist/mods/uix/client.js +96 -0
- package/dist/mods/uix/client.js.map +1 -0
- package/dist/mods/uix/compile.d.ts +7 -0
- package/dist/mods/uix/compile.d.ts.map +1 -0
- package/dist/mods/uix/compile.js +94 -0
- package/dist/mods/uix/compile.js.map +1 -0
- package/dist/mods/uix/jsx-parser.d.ts +2 -0
- package/dist/mods/uix/jsx-parser.d.ts.map +1 -0
- package/dist/mods/uix/jsx-parser.js +565 -0
- package/dist/mods/uix/jsx-parser.js.map +1 -0
- package/dist/mods/uix/verify.d.ts +7 -0
- package/dist/mods/uix/verify.d.ts.map +1 -0
- package/dist/mods/uix/verify.js +59 -0
- package/dist/mods/uix/verify.js.map +1 -0
- package/dist/schema/catalog.d.ts +23 -0
- package/dist/schema/catalog.d.ts.map +1 -0
- package/dist/schema/catalog.js +82 -0
- package/dist/schema/catalog.js.map +1 -0
- package/dist/schema/extract-schemas.d.ts +5 -0
- package/dist/schema/extract-schemas.d.ts.map +1 -0
- package/dist/schema/extract-schemas.js +444 -0
- package/dist/schema/extract-schemas.js.map +1 -0
- package/dist/schema/load.d.ts +2 -0
- package/dist/schema/load.d.ts.map +1 -0
- package/dist/schema/load.js +23 -0
- package/dist/schema/load.js.map +1 -0
- package/dist/schema/test-fixture.types.d.ts +2 -0
- package/dist/schema/test-fixture.types.d.ts.map +1 -0
- package/dist/schema/test-fixture.types.js +19 -0
- package/dist/schema/test-fixture.types.js.map +1 -0
- package/dist/schema/test-opaque.d.ts +3 -0
- package/dist/schema/test-opaque.d.ts.map +1 -0
- package/dist/schema/test-opaque.js +43 -0
- package/dist/schema/test-opaque.js.map +1 -0
- package/dist/schema/types.d.ts +37 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +3 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/server/actions.d.ts +35 -0
- package/dist/server/actions.d.ts.map +1 -0
- package/dist/server/actions.js +170 -0
- package/dist/server/actions.js.map +1 -0
- package/dist/server/agent.d.ts +6 -0
- package/dist/server/agent.d.ts.map +1 -0
- package/dist/server/agent.js +15 -0
- package/dist/server/agent.js.map +1 -0
- package/dist/server/auth.d.ts +39 -0
- package/dist/server/auth.d.ts.map +1 -0
- package/dist/server/auth.js +335 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/client.d.ts +183 -0
- package/dist/server/client.d.ts.map +1 -0
- package/dist/server/client.js +22 -0
- package/dist/server/client.js.map +1 -0
- package/dist/server/cookies.d.ts +5 -0
- package/dist/server/cookies.d.ts.map +1 -0
- package/dist/server/cookies.js +24 -0
- package/dist/server/cookies.js.map +1 -0
- package/dist/server/doc-index.d.ts +38 -0
- package/dist/server/doc-index.d.ts.map +1 -0
- package/dist/server/doc-index.js +244 -0
- package/dist/server/doc-index.js.map +1 -0
- package/dist/server/errors.d.ts +7 -0
- package/dist/server/errors.d.ts.map +1 -0
- package/dist/server/errors.js +11 -0
- package/dist/server/errors.js.map +1 -0
- package/dist/server/factory.d.ts +23 -0
- package/dist/server/factory.d.ts.map +1 -0
- package/dist/server/factory.js +70 -0
- package/dist/server/factory.js.map +1 -0
- package/dist/server/main.d.ts +6 -0
- package/dist/server/main.d.ts.map +1 -0
- package/dist/server/main.js +45 -0
- package/dist/server/main.js.map +1 -0
- package/dist/server/mcp.d.ts +5 -0
- package/dist/server/mcp.d.ts.map +1 -0
- package/dist/server/mcp.js +280 -0
- package/dist/server/mcp.js.map +1 -0
- package/dist/server/migrate.d.ts +3 -0
- package/dist/server/migrate.d.ts.map +1 -0
- package/dist/server/migrate.js +56 -0
- package/dist/server/migrate.js.map +1 -0
- package/dist/server/mod-catalog.d.ts +14 -0
- package/dist/server/mod-catalog.d.ts.map +1 -0
- package/dist/server/mod-catalog.js +40 -0
- package/dist/server/mod-catalog.js.map +1 -0
- package/dist/server/mods-mount.d.ts +4 -0
- package/dist/server/mods-mount.d.ts.map +1 -0
- package/dist/server/mods-mount.js +169 -0
- package/dist/server/mods-mount.js.map +1 -0
- package/dist/server/mount-adapters.d.ts +9 -0
- package/dist/server/mount-adapters.d.ts.map +1 -0
- package/dist/server/mount-adapters.js +75 -0
- package/dist/server/mount-adapters.js.map +1 -0
- package/dist/server/mount.d.ts +3 -0
- package/dist/server/mount.d.ts.map +1 -0
- package/dist/server/mount.js +195 -0
- package/dist/server/mount.js.map +1 -0
- package/dist/server/prefab.d.ts +18 -0
- package/dist/server/prefab.d.ts.map +1 -0
- package/dist/server/prefab.js +70 -0
- package/dist/server/prefab.js.map +1 -0
- package/dist/server/seed/index.d.ts +6 -0
- package/dist/server/seed/index.d.ts.map +1 -0
- package/dist/server/seed/index.js +16 -0
- package/dist/server/seed/index.js.map +1 -0
- package/dist/server/server.d.ts +28 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +117 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/sub.d.ts +34 -0
- package/dist/server/sub.d.ts.map +1 -0
- package/dist/server/sub.js +174 -0
- package/dist/server/sub.js.map +1 -0
- package/dist/server/trpc.d.ts +192 -0
- package/dist/server/trpc.d.ts.map +1 -0
- package/dist/server/trpc.js +319 -0
- package/dist/server/trpc.js.map +1 -0
- package/dist/server/types-mount.d.ts +3 -0
- package/dist/server/types-mount.d.ts.map +1 -0
- package/dist/server/types-mount.js +144 -0
- package/dist/server/types-mount.js.map +1 -0
- package/dist/server/validate.d.ts +3 -0
- package/dist/server/validate.d.ts.map +1 -0
- package/dist/server/validate.js +20 -0
- package/dist/server/validate.js.map +1 -0
- package/dist/server/volatile.d.ts +11 -0
- package/dist/server/volatile.d.ts.map +1 -0
- package/dist/server/volatile.js +26 -0
- package/dist/server/volatile.js.map +1 -0
- package/dist/server/watch.d.ts +23 -0
- package/dist/server/watch.d.ts.map +1 -0
- package/dist/server/watch.js +178 -0
- package/dist/server/watch.js.map +1 -0
- package/dist/tree/cache.d.ts +3 -0
- package/dist/tree/cache.d.ts.map +1 -0
- package/dist/tree/cache.js +48 -0
- package/dist/tree/cache.js.map +1 -0
- package/dist/tree/fs.d.ts +3 -0
- package/dist/tree/fs.d.ts.map +1 -0
- package/dist/tree/fs.js +274 -0
- package/dist/tree/fs.js.map +1 -0
- package/dist/tree/index.d.ts +38 -0
- package/dist/tree/index.d.ts.map +1 -0
- package/dist/tree/index.js +182 -0
- package/dist/tree/index.js.map +1 -0
- package/dist/tree/inflight.d.ts +2 -0
- package/dist/tree/inflight.d.ts.map +1 -0
- package/dist/tree/inflight.js +15 -0
- package/dist/tree/inflight.js.map +1 -0
- package/dist/tree/json-codec.d.ts +2 -0
- package/dist/tree/json-codec.d.ts.map +1 -0
- package/dist/tree/json-codec.js +13 -0
- package/dist/tree/json-codec.js.map +1 -0
- package/dist/tree/mimefs.d.ts +13 -0
- package/dist/tree/mimefs.d.ts.map +1 -0
- package/dist/tree/mimefs.js +124 -0
- package/dist/tree/mimefs.js.map +1 -0
- package/dist/tree/mongo.d.ts +5 -0
- package/dist/tree/mongo.d.ts.map +1 -0
- package/dist/tree/mongo.js +110 -0
- package/dist/tree/mongo.js.map +1 -0
- package/dist/tree/patch.d.ts +30 -0
- package/dist/tree/patch.d.ts.map +1 -0
- package/dist/tree/patch.js +112 -0
- package/dist/tree/patch.js.map +1 -0
- package/dist/tree/query.d.ts +12 -0
- package/dist/tree/query.d.ts.map +1 -0
- package/dist/tree/query.js +61 -0
- package/dist/tree/query.js.map +1 -0
- package/dist/tree/repath.d.ts +3 -0
- package/dist/tree/repath.d.ts.map +1 -0
- package/dist/tree/repath.js +38 -0
- package/dist/tree/repath.js.map +1 -0
- package/dist/tree-chain.d.ts +18 -0
- package/dist/tree-chain.d.ts.map +1 -0
- package/dist/tree-chain.js +109 -0
- package/dist/tree-chain.js.map +1 -0
- package/dist/tree.d.ts +2 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +2 -0
- package/dist/tree.js.map +1 -0
- package/dist/uri.d.ts +11 -0
- package/dist/uri.d.ts.map +1 -0
- package/dist/uri.js +79 -0
- package/dist/uri.js.map +1 -0
- package/package.json +39 -40
- package/src/chain.test.ts +190 -0
- package/src/chain.ts +82 -0
- package/src/client/client.test.ts +192 -0
- package/src/client/handle.ts +53 -0
- package/src/client/index.ts +18 -0
- package/src/client/trpc.ts +91 -0
- package/src/client.ts +1 -0
- package/src/comp/CLAUDE.md +14 -0
- package/src/comp/handle.ts +36 -0
- package/src/comp/index.test.ts +129 -0
- package/src/comp/index.ts +175 -0
- package/src/comp/needs.test.ts +499 -0
- package/src/comp/needs.ts +113 -0
- package/src/comp/validate.test.ts +304 -0
- package/src/comp/validate.ts +125 -0
- package/src/comp.ts +1 -0
- package/src/contexts/schema/index.ts +7 -0
- package/src/contexts/schema.ts +1 -0
- package/src/contexts/service/index.test.ts +323 -0
- package/src/contexts/service/index.ts +43 -0
- package/src/contexts/service.ts +1 -0
- package/src/contexts/telegram/index.ts +115 -0
- package/src/contexts/text/index.test.ts +31 -0
- package/src/contexts/text/index.ts +18 -0
- package/src/contexts/text.ts +1 -0
- package/src/core/component.ts +151 -0
- package/src/core/context.ts +14 -0
- package/src/core/index.test.ts +203 -0
- package/src/core/index.ts +9 -0
- package/src/core/path.ts +35 -0
- package/src/core/registry.ts +115 -0
- package/src/core.ts +1 -0
- package/src/log.test.ts +70 -0
- package/src/log.ts +28 -0
- package/src/mod/discover.test.ts +133 -0
- package/src/mod/discover.ts +100 -0
- package/src/mod/docs/00-index.md +19 -0
- package/src/mod/docs/01-primitives.md +38 -0
- package/src/mod/docs/02-core-api.md +68 -0
- package/src/mod/docs/03-registry.md +30 -0
- package/src/mod/docs/04-store.md +62 -0
- package/src/mod/docs/05-comp.md +111 -0
- package/src/mod/docs/06-actions.md +193 -0
- package/src/mod/docs/07-realtime.md +100 -0
- package/src/mod/docs/08-services.md +33 -0
- package/src/mod/docs/09-mounts.md +43 -0
- package/src/mod/docs/10-acl.md +60 -0
- package/src/mod/docs/11-server.md +62 -0
- package/src/mod/docs/12-conventions.md +65 -0
- package/src/mod/docs/13-example.md +132 -0
- package/src/mod/docs/14-mod-format.md +304 -0
- package/src/mod/docs/15-documenting-types.md +156 -0
- package/src/mod/examples/ticker/seed.ts +19 -0
- package/src/mod/examples/ticker/service.ts +20 -0
- package/src/mod/examples/ticker/ticker.test.ts +18 -0
- package/src/mod/examples/ticker/types.ts +22 -0
- package/src/mod/examples/ticker/view.tsx +19 -0
- package/src/mod/index.ts +12 -0
- package/src/mod/loader.test.ts +168 -0
- package/src/mod/loader.ts +174 -0
- package/src/mod/optimistic.test.ts +446 -0
- package/src/mod/optimistic.ts +210 -0
- package/src/mod/prefab.ts +62 -0
- package/src/mod/tracking.test.ts +59 -0
- package/src/mod/tracking.ts +51 -0
- package/src/mod/types.ts +40 -0
- package/src/mod.ts +1 -0
- package/src/mods/autostart/CLAUDE.md +6 -0
- package/src/mods/autostart/autostart.test.ts +85 -0
- package/src/mods/autostart/server.ts +1 -0
- package/src/mods/autostart/service.ts +98 -0
- package/src/mods/llm/CLAUDE.md +6 -0
- package/src/mods/llm/index.ts +57 -0
- package/src/mods/llm/llm.test.ts +109 -0
- package/src/mods/llm/server.ts +1 -0
- package/src/mods/mcp/CLAUDE.md +6 -0
- package/src/mods/mcp/server.ts +2 -0
- package/src/mods/mcp/service.ts +19 -0
- package/src/mods/mcp/types.ts +7 -0
- package/src/mods/treenity/agent-port.ts +93 -0
- package/src/mods/treenity/groups.ts +19 -0
- package/src/mods/treenity/mod-type.ts +10 -0
- package/src/mods/treenity/seed.ts +56 -0
- package/src/mods/treenity/server.ts +4 -0
- package/src/mods/treenity/system.ts +70 -0
- package/src/mods/uix/CLAUDE.md +7 -0
- package/src/mods/uix/client.ts +117 -0
- package/src/mods/uix/compile.test.ts +228 -0
- package/src/mods/uix/compile.ts +110 -0
- package/src/mods/uix/jsx-parser.test.ts +554 -0
- package/src/mods/uix/jsx-parser.ts +489 -0
- package/src/mods/uix/lazy-load.test.ts +261 -0
- package/src/mods/uix/uix-repomix.md +3509 -0
- package/src/mods/uix/verify.ts +68 -0
- package/src/schema/CLAUDE.md +13 -0
- package/src/schema/catalog.ts +101 -0
- package/src/schema/extract-schemas.test.ts +84 -0
- package/src/schema/extract-schemas.ts +462 -0
- package/src/schema/generated/autostart.json +44 -0
- package/src/schema/generated/board.column.json +25 -0
- package/src/schema/generated/board.task.json +147 -0
- package/src/schema/generated/brahman.action.back.json +12 -0
- package/src/schema/generated/brahman.action.broadcast.json +31 -0
- package/src/schema/generated/brahman.action.call.json +57 -0
- package/src/schema/generated/brahman.action.emittext.json +23 -0
- package/src/schema/generated/brahman.action.eval.json +23 -0
- package/src/schema/generated/brahman.action.file.json +28 -0
- package/src/schema/generated/brahman.action.forward.json +29 -0
- package/src/schema/generated/brahman.action.getvalue.json +28 -0
- package/src/schema/generated/brahman.action.ifelse.json +42 -0
- package/src/schema/generated/brahman.action.keywordselect.json +46 -0
- package/src/schema/generated/brahman.action.message.json +127 -0
- package/src/schema/generated/brahman.action.onerror.json +29 -0
- package/src/schema/generated/brahman.action.page.json +22 -0
- package/src/schema/generated/brahman.action.params.json +36 -0
- package/src/schema/generated/brahman.action.question.json +43 -0
- package/src/schema/generated/brahman.action.remove.json +12 -0
- package/src/schema/generated/brahman.action.resethistory.json +12 -0
- package/src/schema/generated/brahman.action.resetsession.json +12 -0
- package/src/schema/generated/brahman.action.selectlang.json +20 -0
- package/src/schema/generated/brahman.action.setvalue.json +27 -0
- package/src/schema/generated/brahman.action.tag.json +27 -0
- package/src/schema/generated/brahman.bot.json +68 -0
- package/src/schema/generated/brahman.page.json +25 -0
- package/src/schema/generated/brahman.session.json +29 -0
- package/src/schema/generated/brahman.user.json +58 -0
- package/src/schema/generated/cafe.contact.json +56 -0
- package/src/schema/generated/cafe.mail.json +29 -0
- package/src/schema/generated/default.json +15 -0
- package/src/schema/generated/doc.page.json +23 -0
- package/src/schema/generated/examples.demo.generator.json +16 -0
- package/src/schema/generated/examples.demo.sensor.json +35 -0
- package/src/schema/generated/groups.json +20 -0
- package/src/schema/generated/launcher.json +91 -0
- package/src/schema/generated/mcp.server.json +15 -0
- package/src/schema/generated/mindmap.map.json +22 -0
- package/src/schema/generated/sim.agent.json +24 -0
- package/src/schema/generated/sim.ai.json +24 -0
- package/src/schema/generated/sim.config.json +38 -0
- package/src/schema/generated/sim.descriptive.json +26 -0
- package/src/schema/generated/sim.events.json +47 -0
- package/src/schema/generated/sim.item.json +20 -0
- package/src/schema/generated/sim.memory.json +17 -0
- package/src/schema/generated/sim.nearby.json +17 -0
- package/src/schema/generated/sim.position.json +25 -0
- package/src/schema/generated/sim.round.json +64 -0
- package/src/schema/generated/sim.world.json +32 -0
- package/src/schema/generated/t.agent.port.json +74 -0
- package/src/schema/generated/t.llm.json +20 -0
- package/src/schema/generated/t.mod.json +27 -0
- package/src/schema/generated/t3d.animator.json +46 -0
- package/src/schema/generated/t3d.audio.json +58 -0
- package/src/schema/generated/t3d.camera.json +50 -0
- package/src/schema/generated/t3d.collider.json +84 -0
- package/src/schema/generated/t3d.light.json +90 -0
- package/src/schema/generated/t3d.line.json +47 -0
- package/src/schema/generated/t3d.lod.json +28 -0
- package/src/schema/generated/t3d.material.json +131 -0
- package/src/schema/generated/t3d.mesh.json +65 -0
- package/src/schema/generated/t3d.object.json +64 -0
- package/src/schema/generated/t3d.particles.json +109 -0
- package/src/schema/generated/t3d.rigidbody.json +81 -0
- package/src/schema/generated/t3d.scene.json +7 -0
- package/src/schema/generated/t3d.script.json +23 -0
- package/src/schema/generated/t3d.text.json +86 -0
- package/src/schema/generated/t3d.trail.json +45 -0
- package/src/schema/generated/task.json +96 -0
- package/src/schema/generated/test.fixture.json +43 -0
- package/src/schema/generated/ticker.config.json +43 -0
- package/src/schema/generated/ticker.price.json +20 -0
- package/src/schema/generated/todo.item.json +25 -0
- package/src/schema/generated/todo.list.json +33 -0
- package/src/schema/generated/treenity.system.json +259 -0
- package/src/schema/generated/whisper.audio.json +25 -0
- package/src/schema/generated/whisper.checklist.json +17 -0
- package/src/schema/generated/whisper.config.json +30 -0
- package/src/schema/generated/whisper.inbox.json +24 -0
- package/src/schema/generated/whisper.meta.json +35 -0
- package/src/schema/generated/whisper.text.json +16 -0
- package/src/schema/load.ts +24 -0
- package/src/schema/schema.test.ts +86 -0
- package/src/schema/test-fixture.types.ts +21 -0
- package/src/schema/test-opaque.ts +42 -0
- package/src/schema/types.ts +33 -0
- package/src/server/CLAUDE.md +26 -0
- package/src/server/actions.test.ts +272 -0
- package/src/server/actions.ts +274 -0
- package/src/server/agent-sub.test.ts +90 -0
- package/src/server/agent.test.ts +305 -0
- package/src/server/agent.ts +17 -0
- package/src/server/api.test.ts +454 -0
- package/src/server/auth.test.ts +441 -0
- package/src/server/auth.ts +386 -0
- package/src/server/client.ts +24 -0
- package/src/server/conditions.test.ts +128 -0
- package/src/server/cookies.ts +25 -0
- package/src/server/coverage.test.ts +827 -0
- package/src/server/doc-index.ts +286 -0
- package/src/server/e2e.test.ts +966 -0
- package/src/server/errors.ts +11 -0
- package/src/server/factory.ts +99 -0
- package/src/server/main.ts +56 -0
- package/src/server/mcp.ts +326 -0
- package/src/server/migrate.test.ts +123 -0
- package/src/server/migrate.ts +62 -0
- package/src/server/mod-catalog.ts +59 -0
- package/src/server/mods-mount.ts +179 -0
- package/src/server/mount-adapters.ts +82 -0
- package/src/server/mount.parametrized.test.ts +52 -0
- package/src/server/mount.query.test.ts +127 -0
- package/src/server/mount.test.ts +497 -0
- package/src/server/mount.ts +208 -0
- package/src/server/prefab.test.ts +415 -0
- package/src/server/prefab.ts +104 -0
- package/src/server/seed/index.ts +24 -0
- package/src/server/server.ts +153 -0
- package/src/server/stress.test.ts +844 -0
- package/src/server/sub.test.ts +55 -0
- package/src/server/sub.ts +224 -0
- package/src/server/trpc.ts +369 -0
- package/src/server/types-mount.ts +142 -0
- package/src/server/validate.test.ts +91 -0
- package/src/server/validate.ts +22 -0
- package/src/server/volatile.test.ts +140 -0
- package/src/server/volatile.ts +32 -0
- package/src/server/watch.test.ts +594 -0
- package/src/server/watch.ts +202 -0
- package/src/server/workflow.test.ts +82 -0
- package/src/tree/CLAUDE.md +13 -0
- package/src/tree/cache.test.ts +213 -0
- package/src/tree/cache.ts +51 -0
- package/src/tree/fs.test.ts +247 -0
- package/src/tree/fs.ts +251 -0
- package/src/tree/index.test.ts +164 -0
- package/src/tree/index.ts +216 -0
- package/src/tree/inflight.ts +15 -0
- package/src/tree/json-codec.ts +16 -0
- package/src/tree/mimefs.test.ts +289 -0
- package/src/tree/mimefs.ts +142 -0
- package/src/tree/mongo.ts +125 -0
- package/src/tree/patch.test.ts +192 -0
- package/src/tree/patch.ts +133 -0
- package/src/tree/query.test.ts +110 -0
- package/src/tree/query.ts +70 -0
- package/src/tree/repath.test.ts +86 -0
- package/src/tree/repath.ts +53 -0
- package/src/tree-chain.test.ts +723 -0
- package/src/tree-chain.ts +144 -0
- package/src/tree.ts +1 -0
- package/src/uri.test.ts +86 -0
- package/src/uri.ts +82 -0
- package/CHANGELOG.md +0 -305
- package/README.md +0 -18
- package/dist/context.d.ts +0 -41
- package/dist/context.d.ts.map +0 -1
- package/dist/context.mjs +0 -81
- package/dist/context.mjs.map +0 -1
- package/dist/contexts/node-engine.d.ts +0 -12
- package/dist/contexts/node-engine.d.ts.map +0 -1
- package/dist/contexts/node-engine.mjs +0 -7
- package/dist/contexts/node-engine.mjs.map +0 -1
- package/dist/contexts/noflo/types.d.ts +0 -20
- package/dist/contexts/noflo/types.d.ts.map +0 -1
- package/dist/contexts/object.d.ts +0 -11
- package/dist/contexts/object.d.ts.map +0 -1
- package/dist/contexts/object.mjs +0 -15
- package/dist/contexts/object.mjs.map +0 -1
- package/dist/contexts/proto.d.ts +0 -11
- package/dist/contexts/proto.d.ts.map +0 -1
- package/dist/contexts/proto.mjs +0 -7
- package/dist/contexts/proto.mjs.map +0 -1
- package/dist/contexts/react-context.d.ts +0 -21
- package/dist/contexts/react-context.d.ts.map +0 -1
- package/dist/contexts/react-context.mjs +0 -24
- package/dist/contexts/react-context.mjs.map +0 -1
- package/dist/contexts/service.mjs +0 -7
- package/dist/contexts/service.mjs.map +0 -1
- package/dist/get-type-cache.d.ts +0 -2
- package/dist/get-type-cache.d.ts.map +0 -1
- package/dist/get-type-cache.mjs +0 -7
- package/dist/get-type-cache.mjs.map +0 -1
- package/dist/index.d.ts +0 -13
- package/dist/index.d.ts.map +0 -1
- package/dist/index.mjs +0 -10
- package/dist/index.mjs.map +0 -1
- package/dist/link/link.d.ts +0 -25
- package/dist/link/link.d.ts.map +0 -1
- package/dist/link/link.mjs +0 -72
- package/dist/link/link.mjs.map +0 -1
- package/dist/link/link.test.d.ts +0 -2
- package/dist/link/link.test.d.ts.map +0 -1
- package/dist/loading.d.ts +0 -9
- package/dist/loading.d.ts.map +0 -1
- package/dist/meta-type.d.ts +0 -58
- package/dist/meta-type.d.ts.map +0 -1
- package/dist/meta-type.mjs +0 -104
- package/dist/meta-type.mjs.map +0 -1
- package/dist/meta.d.ts +0 -20
- package/dist/meta.d.ts.map +0 -1
- package/dist/meta.mjs +0 -16
- package/dist/meta.mjs.map +0 -1
- package/dist/node/index.d.ts +0 -2
- package/dist/node/index.d.ts.map +0 -1
- package/dist/node/types.d.ts +0 -37
- package/dist/node/types.d.ts.map +0 -1
- package/dist/test/context.test.d.ts +0 -2
- package/dist/test/context.test.d.ts.map +0 -1
- package/dist/types.d.ts +0 -14
- package/dist/types.d.ts.map +0 -1
- package/dist/types.mjs +0 -16
- package/dist/types.mjs.map +0 -1
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
// Coverage gap tests — exercises uncovered code paths across the codebase.
|
|
2
|
+
// Excludes Mongo (requires connection). Focuses on:
|
|
3
|
+
// - mount-adapters (memory, query, overlay, types, fs validation)
|
|
4
|
+
// - sift queries via memory store getChildren
|
|
5
|
+
// - sub.ts remove CDC path
|
|
6
|
+
// - actions.ts (executeAction, setComponent, applyTemplate)
|
|
7
|
+
// - fs store OCC
|
|
8
|
+
// - validate.ts edge cases
|
|
9
|
+
// - volatile extractPaths
|
|
10
|
+
|
|
11
|
+
import { registerType } from '#comp';
|
|
12
|
+
import { createNode, isComponent, type NodeData, register, resolve } from '#core';
|
|
13
|
+
import { clearRegistry } from '#core/index.test';
|
|
14
|
+
import { createMemoryTree, createOverlayTree, type Tree } from '#tree';
|
|
15
|
+
import { createFsTree } from '#tree/fs';
|
|
16
|
+
import { createQueryTree, mapNodeForSift, mapSiftQuery, matchesFilter } from '#tree/query';
|
|
17
|
+
import assert from 'node:assert/strict';
|
|
18
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
19
|
+
import { tmpdir } from 'node:os';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { afterEach, beforeEach, describe, it } from 'node:test';
|
|
22
|
+
import { applyTemplate, executeAction, executeStream, setComponent } from './actions';
|
|
23
|
+
import { OpError } from './errors';
|
|
24
|
+
import { withMounts } from './mount';
|
|
25
|
+
import { getActiveQueryCount, unwatchAllQueries, unwatchQuery, watchQuery, withSubscriptions } from './sub';
|
|
26
|
+
import { createTypesStore } from './types-mount';
|
|
27
|
+
import { withValidation } from './validate';
|
|
28
|
+
import { extractPaths, isVolatile, withVolatile } from './volatile';
|
|
29
|
+
|
|
30
|
+
// ── Helpers ──
|
|
31
|
+
|
|
32
|
+
function fullPipeline(rootStore?: Tree) {
|
|
33
|
+
const bootstrap = rootStore ?? createMemoryTree();
|
|
34
|
+
const mountable = withMounts(bootstrap);
|
|
35
|
+
const volatile = withVolatile(mountable);
|
|
36
|
+
const validated = withValidation(volatile);
|
|
37
|
+
const events: any[] = [];
|
|
38
|
+
const store = withSubscriptions(validated, (e) => events.push(e));
|
|
39
|
+
return { bootstrap, store, events };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── mount-adapters (non-Mongo) ──
|
|
43
|
+
|
|
44
|
+
describe('Mount adapters', () => {
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
clearRegistry();
|
|
47
|
+
|
|
48
|
+
// Register mount adapters manually (same as mount-adapters.ts but without Mongo import side effects)
|
|
49
|
+
register('t.mount.memory', 'mount', () => createMemoryTree());
|
|
50
|
+
register('t.mount.types', 'mount', (_node: any, deps: Tree) => createTypesStore(deps));
|
|
51
|
+
register('t.mount.query', 'mount', (config: any, parentStore: Tree, _ctx: any, globalStore?: Tree) => {
|
|
52
|
+
const n = config as NodeData;
|
|
53
|
+
const qv = n['query'];
|
|
54
|
+
const query = isComponent(qv) ? qv as { source: string; match: Record<string, unknown> } : undefined;
|
|
55
|
+
if (!query?.source || !query?.match) throw new Error("t.mount.query requires 'query' component with source and match");
|
|
56
|
+
return createQueryTree({ source: query.source, match: query.match }, globalStore || parentStore);
|
|
57
|
+
});
|
|
58
|
+
register('t.mount.overlay', 'mount', async (config: any, parentStore: Tree, ctx: any, globalStore?: Tree) => {
|
|
59
|
+
const n = config as NodeData;
|
|
60
|
+
const mount = n['mount'] as any;
|
|
61
|
+
if (!mount?.layers?.length) throw new Error('t.mount.overlay: layers required');
|
|
62
|
+
const stores: Tree[] = [];
|
|
63
|
+
for (const name of mount.layers) {
|
|
64
|
+
const comp = n[name] as any;
|
|
65
|
+
if (!comp) throw new Error(`t.mount.overlay: component "${name}" not found`);
|
|
66
|
+
const adapter = resolve(comp.$type, 'mount');
|
|
67
|
+
if (!adapter) throw new Error(`No mount adapter for "${comp.$type}"`);
|
|
68
|
+
stores.push(await adapter(comp, stores[0] ?? ({} as Tree), ctx, globalStore));
|
|
69
|
+
}
|
|
70
|
+
let result = stores[0];
|
|
71
|
+
for (let i = 1; i < stores.length; i++) result = createOverlayTree(stores[i], result);
|
|
72
|
+
return result;
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('t.mount.memory creates independent store', async () => {
|
|
77
|
+
const root = createMemoryTree();
|
|
78
|
+
await root.set(createNode('/mnt', 'folder', {}, {
|
|
79
|
+
mount: { $type: 't.mount.memory' },
|
|
80
|
+
}));
|
|
81
|
+
const ms = withMounts(root);
|
|
82
|
+
|
|
83
|
+
await ms.set(createNode('/mnt/a', 'item'));
|
|
84
|
+
assert.ok(await ms.get('/mnt/a'));
|
|
85
|
+
// Not visible in root store (mounted into separate memory)
|
|
86
|
+
assert.equal(await root.get('/mnt/a'), undefined);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('t.mount.query requires query component', async () => {
|
|
90
|
+
const root = createMemoryTree();
|
|
91
|
+
await root.set(createNode('/bad', 'folder', {}, {
|
|
92
|
+
mount: { $type: 't.mount.query' },
|
|
93
|
+
// missing 'query' component
|
|
94
|
+
}));
|
|
95
|
+
const ms = withMounts(root);
|
|
96
|
+
|
|
97
|
+
await assert.rejects(() => ms.getChildren('/bad'));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('t.mount.overlay merges two memory stores', async () => {
|
|
101
|
+
const root = createMemoryTree();
|
|
102
|
+
await root.set(createNode('/ovl', 'folder', {}, {
|
|
103
|
+
mount: { $type: 't.mount.overlay', layers: ['base', 'upper'] },
|
|
104
|
+
base: { $type: 't.mount.memory' },
|
|
105
|
+
upper: { $type: 't.mount.memory' },
|
|
106
|
+
}));
|
|
107
|
+
const ms = withMounts(root);
|
|
108
|
+
|
|
109
|
+
await ms.set(createNode('/ovl/x', 'item'));
|
|
110
|
+
assert.ok(await ms.get('/ovl/x'));
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('t.mount.overlay throws without layers', async () => {
|
|
114
|
+
const root = createMemoryTree();
|
|
115
|
+
await root.set(createNode('/bad', 'folder', {}, {
|
|
116
|
+
mount: { $type: 't.mount.overlay' },
|
|
117
|
+
}));
|
|
118
|
+
const ms = withMounts(root);
|
|
119
|
+
|
|
120
|
+
await assert.rejects(() => ms.getChildren('/bad'));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('t.mount.overlay throws on missing component', async () => {
|
|
124
|
+
const root = createMemoryTree();
|
|
125
|
+
await root.set(createNode('/bad2', 'folder', {}, {
|
|
126
|
+
mount: { $type: 't.mount.overlay', layers: ['base', 'ghost'] },
|
|
127
|
+
base: { $type: 't.mount.memory' },
|
|
128
|
+
// 'ghost' component missing
|
|
129
|
+
}));
|
|
130
|
+
const ms = withMounts(root);
|
|
131
|
+
|
|
132
|
+
await assert.rejects(() => ms.getChildren('/bad2'));
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('t.mount.overlay throws on unknown adapter type', async () => {
|
|
136
|
+
const root = createMemoryTree();
|
|
137
|
+
await root.set(createNode('/bad3', 'folder', {}, {
|
|
138
|
+
mount: { $type: 't.mount.overlay', layers: ['base'] },
|
|
139
|
+
base: { $type: 'unknown.adapter' },
|
|
140
|
+
}));
|
|
141
|
+
const ms = withMounts(root);
|
|
142
|
+
|
|
143
|
+
await assert.rejects(() => ms.getChildren('/bad3'));
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ── Sift queries via memory store ──
|
|
148
|
+
|
|
149
|
+
describe('Sift queries via memory store', () => {
|
|
150
|
+
let store: Tree;
|
|
151
|
+
|
|
152
|
+
beforeEach(async () => {
|
|
153
|
+
store = createMemoryTree();
|
|
154
|
+
// Seed diverse data
|
|
155
|
+
for (let i = 0; i < 20; i++) {
|
|
156
|
+
await store.set({
|
|
157
|
+
$path: `/items/${i}`,
|
|
158
|
+
$type: i % 3 === 0 ? 'order' : 'task',
|
|
159
|
+
status: { $type: 'status', value: i % 2 === 0 ? 'active' : 'done' },
|
|
160
|
+
priority: i % 5,
|
|
161
|
+
tags: i < 10 ? ['urgent'] : ['low'],
|
|
162
|
+
} as any);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('$eq (implicit)', async () => {
|
|
167
|
+
const result = await store.getChildren('/items', { query: { priority: 0 } });
|
|
168
|
+
assert.ok(result.items.length > 0);
|
|
169
|
+
for (const n of result.items) assert.equal((n as any).priority, 0);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('$gt / $lt', async () => {
|
|
173
|
+
const result = await store.getChildren('/items', { query: { priority: { $gt: 3 } } });
|
|
174
|
+
assert.ok(result.items.length > 0);
|
|
175
|
+
for (const n of result.items) assert.ok((n as any).priority > 3);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('$in', async () => {
|
|
179
|
+
const result = await store.getChildren('/items', { query: { priority: { $in: [0, 4] } } });
|
|
180
|
+
for (const n of result.items) assert.ok([0, 4].includes((n as any).priority));
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('$and (composite)', async () => {
|
|
184
|
+
const result = await store.getChildren('/items', {
|
|
185
|
+
query: { $and: [{ priority: { $gte: 2 } }, { 'status.value': 'active' }] },
|
|
186
|
+
});
|
|
187
|
+
for (const n of result.items) {
|
|
188
|
+
assert.ok((n as any).priority >= 2);
|
|
189
|
+
assert.equal((n as any).status.value, 'active');
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('$or', async () => {
|
|
194
|
+
const result = await store.getChildren('/items', {
|
|
195
|
+
query: { $or: [{ priority: 0 }, { priority: 4 }] },
|
|
196
|
+
});
|
|
197
|
+
for (const n of result.items) assert.ok([0, 4].includes((n as any).priority));
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('$not / $ne', async () => {
|
|
201
|
+
const result = await store.getChildren('/items', { query: { priority: { $ne: 0 } } });
|
|
202
|
+
for (const n of result.items) assert.notEqual((n as any).priority, 0);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('$type mapping — _type instead of $type', async () => {
|
|
206
|
+
const result = await store.getChildren('/items', { query: { _type: 'order' } });
|
|
207
|
+
assert.ok(result.items.length > 0);
|
|
208
|
+
for (const n of result.items) assert.equal(n.$type, 'order');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('dot-path nested queries', async () => {
|
|
212
|
+
const result = await store.getChildren('/items', { query: { 'status.value': 'done' } });
|
|
213
|
+
assert.ok(result.items.length > 0);
|
|
214
|
+
for (const n of result.items) assert.equal((n as any).status.value, 'done');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('$elemMatch on arrays', async () => {
|
|
218
|
+
const result = await store.getChildren('/items', { query: { tags: { $in: ['urgent'] } } });
|
|
219
|
+
assert.equal(result.items.length, 10);
|
|
220
|
+
for (const n of result.items) assert.ok((n as any).tags.includes('urgent'));
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('$exists', async () => {
|
|
224
|
+
await store.set({ $path: '/items/special', $type: 'special', rare: true } as any);
|
|
225
|
+
const result = await store.getChildren('/items', { query: { rare: { $exists: true } } });
|
|
226
|
+
assert.equal(result.items.length, 1);
|
|
227
|
+
assert.equal(result.items[0].$path, '/items/special');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('empty query returns all', async () => {
|
|
231
|
+
const result = await store.getChildren('/items', { query: {} });
|
|
232
|
+
assert.equal(result.items.length, 20);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('query + pagination', async () => {
|
|
236
|
+
const page = await store.getChildren('/items', {
|
|
237
|
+
query: { 'status.value': 'active' },
|
|
238
|
+
limit: 3,
|
|
239
|
+
offset: 0,
|
|
240
|
+
});
|
|
241
|
+
assert.equal(page.items.length, 3);
|
|
242
|
+
assert.ok(page.total >= 3);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('query on non-existent path returns empty', async () => {
|
|
246
|
+
const result = await store.getChildren('/nonexistent', { query: { x: 1 } });
|
|
247
|
+
assert.equal(result.items.length, 0);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// ── mapSiftQuery / mapNodeForSift edge cases ──
|
|
252
|
+
|
|
253
|
+
describe('query.ts mapping helpers', () => {
|
|
254
|
+
it('mapSiftQuery renames all $ fields', () => {
|
|
255
|
+
const mapped = mapSiftQuery({ $type: 'x', $path: '/a', $acl: [], $owner: 'u', $rev: 1 }) as Record<string, unknown>;
|
|
256
|
+
assert.equal(mapped._type, 'x');
|
|
257
|
+
assert.equal(mapped._path, '/a');
|
|
258
|
+
assert.deepEqual(mapped._acl, []);
|
|
259
|
+
assert.equal(mapped._owner, 'u');
|
|
260
|
+
assert.equal(mapped._rev, 1);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('mapSiftQuery handles nested arrays and objects', () => {
|
|
264
|
+
const mapped = mapSiftQuery({ $and: [{ $type: 'a' }, { $path: '/b' }] });
|
|
265
|
+
assert.deepEqual(mapped, { $and: [{ _type: 'a' }, { _path: '/b' }] });
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('mapSiftQuery passes primitives through', () => {
|
|
269
|
+
assert.equal(mapSiftQuery('hello'), 'hello');
|
|
270
|
+
assert.equal(mapSiftQuery(42), 42);
|
|
271
|
+
assert.equal(mapSiftQuery(null), null);
|
|
272
|
+
assert.equal(mapSiftQuery(true), true);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('mapNodeForSift converts $ prefixed keys', () => {
|
|
276
|
+
const mapped = mapNodeForSift({ $path: '/x', $type: 'y', name: 'z' } as NodeData);
|
|
277
|
+
assert.equal(mapped._path, '/x');
|
|
278
|
+
assert.equal(mapped._type, 'y');
|
|
279
|
+
assert.equal(mapped.name, 'z');
|
|
280
|
+
assert.equal(mapped.$path, undefined);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// ── sub.ts: remove CDC path ──
|
|
285
|
+
|
|
286
|
+
describe('sub.ts: remove + CDC', () => {
|
|
287
|
+
beforeEach(() => {
|
|
288
|
+
clearRegistry();
|
|
289
|
+
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('remove emits rmVps when node was in query', async () => {
|
|
293
|
+
const mem = createMemoryTree();
|
|
294
|
+
const events: any[] = [];
|
|
295
|
+
const store = withSubscriptions(mem, (e) => events.push(e));
|
|
296
|
+
|
|
297
|
+
// Register a query watch
|
|
298
|
+
watchQuery('/active', '/items', { 'status.value': 'active' }, 'user1');
|
|
299
|
+
|
|
300
|
+
// Create a matching node
|
|
301
|
+
await store.set({ $path: '/items/a', $type: 'item', status: { $type: 'status', value: 'active' } } as NodeData);
|
|
302
|
+
|
|
303
|
+
events.length = 0;
|
|
304
|
+
|
|
305
|
+
// Remove it — should emit rmVps
|
|
306
|
+
await store.remove('/items/a');
|
|
307
|
+
assert.equal(events.length, 1);
|
|
308
|
+
assert.equal(events[0].type, 'remove');
|
|
309
|
+
assert.deepEqual(events[0].rmVps, ['/active']);
|
|
310
|
+
|
|
311
|
+
unwatchAllQueries('user1');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('remove of non-matching node has empty rmVps', async () => {
|
|
315
|
+
const mem = createMemoryTree();
|
|
316
|
+
const events: any[] = [];
|
|
317
|
+
const store = withSubscriptions(mem, (e) => events.push(e));
|
|
318
|
+
|
|
319
|
+
watchQuery('/active', '/items', { 'status.value': 'active' }, 'user1');
|
|
320
|
+
|
|
321
|
+
await store.set({ $path: '/items/b', $type: 'item', status: { $type: 'status', value: 'done' } } as NodeData);
|
|
322
|
+
events.length = 0;
|
|
323
|
+
|
|
324
|
+
await store.remove('/items/b');
|
|
325
|
+
assert.equal(events.length, 1);
|
|
326
|
+
assert.equal(events[0].type, 'remove');
|
|
327
|
+
assert.equal(events[0].rmVps, undefined);
|
|
328
|
+
|
|
329
|
+
unwatchAllQueries('user1');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('remove of non-existent node emits nothing', async () => {
|
|
333
|
+
const mem = createMemoryTree();
|
|
334
|
+
const events: any[] = [];
|
|
335
|
+
const store = withSubscriptions(mem, (e) => events.push(e));
|
|
336
|
+
|
|
337
|
+
await store.remove('/ghost');
|
|
338
|
+
assert.equal(events.length, 0);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('unwatchQuery removes single user', () => {
|
|
342
|
+
watchQuery('/vp1', '/src', { x: 1 }, 'userA');
|
|
343
|
+
watchQuery('/vp1', '/src', { x: 1 }, 'userB');
|
|
344
|
+
assert.equal(getActiveQueryCount(), 1);
|
|
345
|
+
|
|
346
|
+
unwatchQuery('/vp1', 'userA');
|
|
347
|
+
assert.equal(getActiveQueryCount(), 1, 'entry should remain (userB watching)');
|
|
348
|
+
|
|
349
|
+
unwatchQuery('/vp1', 'userB');
|
|
350
|
+
assert.equal(getActiveQueryCount(), 0, 'entry fully cleaned');
|
|
351
|
+
|
|
352
|
+
// Idempotent
|
|
353
|
+
unwatchQuery('/vp1', 'userB');
|
|
354
|
+
assert.equal(getActiveQueryCount(), 0);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('unwatchAllQueries cleans up all entries for user', () => {
|
|
358
|
+
watchQuery('/a', '/s', { x: 1 }, 'u1');
|
|
359
|
+
watchQuery('/b', '/s', { y: 2 }, 'u1');
|
|
360
|
+
watchQuery('/a', '/s', { x: 1 }, 'u2');
|
|
361
|
+
assert.equal(getActiveQueryCount(), 2);
|
|
362
|
+
|
|
363
|
+
unwatchAllQueries('u1');
|
|
364
|
+
assert.equal(getActiveQueryCount(), 1, '/a should remain (u2 watching)');
|
|
365
|
+
|
|
366
|
+
unwatchAllQueries('u2');
|
|
367
|
+
assert.equal(getActiveQueryCount(), 0);
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// ── actions.ts: executeAction, setComponent, applyTemplate ──
|
|
372
|
+
|
|
373
|
+
describe('actions.ts operations', () => {
|
|
374
|
+
beforeEach(() => {
|
|
375
|
+
clearRegistry();
|
|
376
|
+
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('executeAction generates Immer patches', async () => {
|
|
380
|
+
class Counter {
|
|
381
|
+
count = 0;
|
|
382
|
+
increment() { this.count++; }
|
|
383
|
+
}
|
|
384
|
+
registerType('counter', Counter);
|
|
385
|
+
|
|
386
|
+
const store = createMemoryTree();
|
|
387
|
+
await store.set(createNode('/c', 'item', {}, {
|
|
388
|
+
counter: { $type: 'counter', count: 0 },
|
|
389
|
+
}));
|
|
390
|
+
|
|
391
|
+
await executeAction(store, '/c', 'counter', undefined, 'increment');
|
|
392
|
+
|
|
393
|
+
const node = await store.get('/c');
|
|
394
|
+
assert.equal((node!['counter'] as any).count, 1);
|
|
395
|
+
// $rev incremented
|
|
396
|
+
assert.equal(node!.$rev, 2); // once from initial set, once from executeAction
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('executeAction without component name uses node type', async () => {
|
|
400
|
+
register('mytype', 'action:doStuff', (actx: any) => {
|
|
401
|
+
actx.node.result = 42;
|
|
402
|
+
return 'ok';
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const store = createMemoryTree();
|
|
406
|
+
await store.set(createNode('/n', 'mytype'));
|
|
407
|
+
|
|
408
|
+
const result = await executeAction(store, '/n', undefined, undefined, 'doStuff');
|
|
409
|
+
assert.equal(result, 'ok');
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('executeAction throws NOT_FOUND for missing node', async () => {
|
|
413
|
+
const store = createMemoryTree();
|
|
414
|
+
await assert.rejects(
|
|
415
|
+
() => executeAction(store, '/missing', undefined, undefined, 'x'),
|
|
416
|
+
(e: any) => { assert.equal(e.code, 'NOT_FOUND'); return true; },
|
|
417
|
+
);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('executeAction throws NOT_FOUND for missing component', async () => {
|
|
421
|
+
const store = createMemoryTree();
|
|
422
|
+
await store.set(createNode('/n', 'item'));
|
|
423
|
+
await assert.rejects(
|
|
424
|
+
() => executeAction(store, '/n', 'ghost', undefined, 'x'),
|
|
425
|
+
(e: any) => { assert.equal(e.code, 'NOT_FOUND'); return true; },
|
|
426
|
+
);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('executeAction throws BAD_REQUEST for missing action', async () => {
|
|
430
|
+
class Dummy { x = 1; }
|
|
431
|
+
registerType('dummy', Dummy);
|
|
432
|
+
|
|
433
|
+
const store = createMemoryTree();
|
|
434
|
+
await store.set(createNode('/n', 'item', {}, {
|
|
435
|
+
dummy: { $type: 'dummy', x: 1 },
|
|
436
|
+
}));
|
|
437
|
+
|
|
438
|
+
await assert.rejects(
|
|
439
|
+
() => executeAction(store, '/n', 'dummy', undefined, 'nonexistent'),
|
|
440
|
+
(e: any) => { assert.equal(e.code, 'BAD_REQUEST'); return true; },
|
|
441
|
+
);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('pure action (no patches) does not persist', async () => {
|
|
445
|
+
register('reader', 'action:read', (_actx: any, data: any) => {
|
|
446
|
+
return { echo: data };
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const store = createMemoryTree();
|
|
450
|
+
await store.set(createNode('/r', 'reader'));
|
|
451
|
+
|
|
452
|
+
const result = await executeAction(store, '/r', undefined, undefined, 'read', { msg: 'hello' });
|
|
453
|
+
assert.deepEqual(result, { echo: { msg: 'hello' } });
|
|
454
|
+
// $rev stays at 1 — no store.set() called
|
|
455
|
+
const node = await store.get('/r');
|
|
456
|
+
assert.equal(node!.$rev, 1);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('executeAction throws NOT_FOUND for missing node (no type)', async () => {
|
|
460
|
+
const store = createMemoryTree();
|
|
461
|
+
await assert.rejects(
|
|
462
|
+
() => executeAction(store, '/missing', undefined, undefined, 'x'),
|
|
463
|
+
(e: any) => { assert.equal(e.code, 'NOT_FOUND'); return true; },
|
|
464
|
+
);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('executeStream: yields values from async generator action', async () => {
|
|
468
|
+
class Emitter {
|
|
469
|
+
async *emit(data: { n: number }) {
|
|
470
|
+
for (let i = 0; i < data.n; i++) yield i;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
registerType('emitter', Emitter);
|
|
474
|
+
|
|
475
|
+
const store = createMemoryTree();
|
|
476
|
+
await store.set(createNode('/e', 'item', {}, {
|
|
477
|
+
emitter: { $type: 'emitter' },
|
|
478
|
+
}));
|
|
479
|
+
|
|
480
|
+
const collected: unknown[] = [];
|
|
481
|
+
for await (const item of executeStream(store, '/e', 'emitter', undefined, 'emit', { n: 3 })) {
|
|
482
|
+
collected.push(item);
|
|
483
|
+
}
|
|
484
|
+
assert.deepEqual(collected, [0, 1, 2]);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('executeStream: BAD_REQUEST for non-generator action', async () => {
|
|
488
|
+
class Plain { run() { return 42; } }
|
|
489
|
+
registerType('plain', Plain);
|
|
490
|
+
|
|
491
|
+
const store = createMemoryTree();
|
|
492
|
+
await store.set(createNode('/p', 'item', {}, { plain: { $type: 'plain' } }));
|
|
493
|
+
|
|
494
|
+
await assert.rejects(
|
|
495
|
+
async () => {
|
|
496
|
+
for await (const _ of executeStream(store, '/p', 'plain', undefined, 'run')) { /* */ }
|
|
497
|
+
},
|
|
498
|
+
(e: any) => { assert.equal(e.code, 'BAD_REQUEST'); return true; },
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('setComponent updates single component', async () => {
|
|
503
|
+
const store = createMemoryTree();
|
|
504
|
+
await store.set(createNode('/s', 'item', {}, {
|
|
505
|
+
meta: { $type: 'meta', title: 'old' },
|
|
506
|
+
}));
|
|
507
|
+
|
|
508
|
+
await setComponent(store, '/s', 'meta', { $type: 'meta', title: 'new' });
|
|
509
|
+
|
|
510
|
+
const node = await store.get('/s');
|
|
511
|
+
assert.equal((node as any).meta.title, 'new');
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('setComponent throws NOT_FOUND for missing node', async () => {
|
|
515
|
+
const store = createMemoryTree();
|
|
516
|
+
await assert.rejects(
|
|
517
|
+
() => setComponent(store, '/missing', 'x', {}),
|
|
518
|
+
(e: any) => { assert.equal(e.code, 'NOT_FOUND'); return true; },
|
|
519
|
+
);
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it('setComponent throws CONFLICT on stale rev', async () => {
|
|
523
|
+
const store = createMemoryTree();
|
|
524
|
+
await store.set(createNode('/s', 'item'));
|
|
525
|
+
await assert.rejects(
|
|
526
|
+
() => setComponent(store, '/s', 'x', {}, 999),
|
|
527
|
+
(e: any) => { assert.equal(e.code, 'CONFLICT'); return true; },
|
|
528
|
+
);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('applyTemplate copies children to target', async () => {
|
|
532
|
+
const store = createMemoryTree();
|
|
533
|
+
await store.set(createNode('/templates/blog', 'template'));
|
|
534
|
+
await store.set(createNode('/templates/blog/header', 'block', { text: 'Hello' }));
|
|
535
|
+
await store.set(createNode('/templates/blog/body', 'block', { text: 'Content' }));
|
|
536
|
+
|
|
537
|
+
// Existing children at target get removed
|
|
538
|
+
await store.set(createNode('/pages/p1', 'page'));
|
|
539
|
+
await store.set(createNode('/pages/p1/old', 'block'));
|
|
540
|
+
|
|
541
|
+
const result = await applyTemplate(store, '/templates/blog', '/pages/p1');
|
|
542
|
+
assert.equal(result.blocks, 2);
|
|
543
|
+
assert.equal(result.applied, '/templates/blog');
|
|
544
|
+
|
|
545
|
+
// Old child gone
|
|
546
|
+
assert.equal(await store.get('/pages/p1/old'), undefined);
|
|
547
|
+
|
|
548
|
+
// New children present
|
|
549
|
+
const header = await store.get('/pages/p1/header');
|
|
550
|
+
assert.ok(header);
|
|
551
|
+
assert.equal((header as any).text, 'Hello');
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it('applyTemplate throws NOT_FOUND for missing template', async () => {
|
|
555
|
+
const store = createMemoryTree();
|
|
556
|
+
await assert.rejects(
|
|
557
|
+
() => applyTemplate(store, '/missing', '/target'),
|
|
558
|
+
(e: any) => { assert.equal(e.code, 'NOT_FOUND'); return true; },
|
|
559
|
+
);
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// ── fs store: OCC + remove edge cases ──
|
|
564
|
+
|
|
565
|
+
describe('FsStore OCC', () => {
|
|
566
|
+
let dir: string;
|
|
567
|
+
|
|
568
|
+
afterEach(async () => {
|
|
569
|
+
if (dir) await rm(dir, { recursive: true, force: true });
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it('OCC rejects stale rev', async () => {
|
|
573
|
+
dir = await mkdtemp(join(tmpdir(), 'treenity-fs-occ-'));
|
|
574
|
+
const store = await createFsTree(dir);
|
|
575
|
+
|
|
576
|
+
await store.set(createNode('/n', 'item')); // rev becomes 1
|
|
577
|
+
const node = await store.get('/n');
|
|
578
|
+
|
|
579
|
+
// Simulate stale read
|
|
580
|
+
await store.set({ ...node!, $rev: node!.$rev }); // rev becomes 2
|
|
581
|
+
|
|
582
|
+
// Now try with stale rev=1
|
|
583
|
+
await assert.rejects(() => store.set({ ...node!, $rev: 1 }));
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it('OCC throws when node does not exist but rev provided', async () => {
|
|
587
|
+
dir = await mkdtemp(join(tmpdir(), 'treenity-fs-occ2-'));
|
|
588
|
+
const store = await createFsTree(dir);
|
|
589
|
+
|
|
590
|
+
await assert.rejects(
|
|
591
|
+
() => store.set({ ...createNode('/ghost', 'item'), $rev: 1 }),
|
|
592
|
+
);
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// ── validate.ts edge cases ──
|
|
597
|
+
|
|
598
|
+
describe('Validation edge cases', () => {
|
|
599
|
+
beforeEach(() => {
|
|
600
|
+
clearRegistry();
|
|
601
|
+
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it('rejects boolean where string expected', async () => {
|
|
605
|
+
register('typed', 'schema', () => ({
|
|
606
|
+
properties: { name: { type: 'string' } },
|
|
607
|
+
}));
|
|
608
|
+
|
|
609
|
+
const store = withValidation(createMemoryTree());
|
|
610
|
+
|
|
611
|
+
await assert.rejects(
|
|
612
|
+
() => store.set({ $path: '/v', $type: 'x', comp: { $type: 'typed', name: 123 } } as any),
|
|
613
|
+
);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it('rejects string where number expected', async () => {
|
|
617
|
+
register('numtype', 'schema', () => ({
|
|
618
|
+
properties: { count: { type: 'number' } },
|
|
619
|
+
}));
|
|
620
|
+
|
|
621
|
+
const store = withValidation(createMemoryTree());
|
|
622
|
+
|
|
623
|
+
await assert.rejects(
|
|
624
|
+
() => store.set({ $path: '/v', $type: 'x', comp: { $type: 'numtype', count: 'not a number' } } as any),
|
|
625
|
+
);
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
it('rejects number where boolean expected', async () => {
|
|
629
|
+
register('booltype', 'schema', () => ({
|
|
630
|
+
properties: { flag: { type: 'boolean' } },
|
|
631
|
+
}));
|
|
632
|
+
|
|
633
|
+
const store = withValidation(createMemoryTree());
|
|
634
|
+
|
|
635
|
+
await assert.rejects(
|
|
636
|
+
() => store.set({ $path: '/v', $type: 'x', comp: { $type: 'booltype', flag: 42 } } as any),
|
|
637
|
+
);
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('allows null/undefined values (optional by default)', async () => {
|
|
641
|
+
register('opttype', 'schema', () => ({
|
|
642
|
+
properties: { name: { type: 'string' } },
|
|
643
|
+
}));
|
|
644
|
+
|
|
645
|
+
const store = withValidation(createMemoryTree());
|
|
646
|
+
// null and undefined should pass — optional by default
|
|
647
|
+
await store.set({ $path: '/v', $type: 'x', comp: { $type: 'opttype', name: null } } as any);
|
|
648
|
+
await store.set({ $path: '/v2', $type: 'x', comp: { $type: 'opttype' } } as any);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('skips components without schema', async () => {
|
|
652
|
+
const store = withValidation(createMemoryTree());
|
|
653
|
+
await store.set({ $path: '/v', $type: 'x', comp: { $type: 'untyped', anything: 'goes' } } as any);
|
|
654
|
+
const node = await store.get('/v');
|
|
655
|
+
assert.ok(node, 'node should be stored');
|
|
656
|
+
assert.equal((node as any).comp.anything, 'goes');
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it('skips schemas without properties', async () => {
|
|
660
|
+
register('emptyschema', 'schema', () => ({}));
|
|
661
|
+
|
|
662
|
+
const store = withValidation(createMemoryTree());
|
|
663
|
+
await store.set({ $path: '/v', $type: 'x', comp: { $type: 'emptyschema', x: 1 } } as any);
|
|
664
|
+
const node = await store.get('/v');
|
|
665
|
+
assert.ok(node, 'node should be stored');
|
|
666
|
+
assert.equal((node as any).comp.x, 1);
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
// ── volatile.ts: extractPaths ──
|
|
671
|
+
|
|
672
|
+
describe('extractPaths', () => {
|
|
673
|
+
it('extracts from { items: [...] }', () => {
|
|
674
|
+
const paths = extractPaths({ items: [{ $path: '/a' }, { $path: '/b' }] });
|
|
675
|
+
assert.deepEqual(paths, ['/a', '/b']);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it('extracts from single node', () => {
|
|
679
|
+
const paths = extractPaths({ $path: '/single' });
|
|
680
|
+
assert.deepEqual(paths, ['/single']);
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it('returns empty for null/undefined', () => {
|
|
684
|
+
assert.deepEqual(extractPaths(null), []);
|
|
685
|
+
assert.deepEqual(extractPaths(undefined), []);
|
|
686
|
+
assert.deepEqual(extractPaths('string'), []);
|
|
687
|
+
assert.deepEqual(extractPaths(42), []);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('filters items without $path', () => {
|
|
691
|
+
const paths = extractPaths({ items: [{ $path: '/a' }, { name: 'no path' }, { $path: '/b' }] });
|
|
692
|
+
assert.deepEqual(paths, ['/a', '/b']);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('returns empty for object without $path or items', () => {
|
|
696
|
+
assert.deepEqual(extractPaths({ foo: 'bar' }), []);
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// ── volatile.ts: isVolatile ──
|
|
701
|
+
|
|
702
|
+
describe('isVolatile', () => {
|
|
703
|
+
beforeEach(() => {
|
|
704
|
+
clearRegistry();
|
|
705
|
+
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it('returns true for $volatile flag', () => {
|
|
709
|
+
assert.equal(isVolatile({ $path: '/a', $type: 'x', $volatile: true } as any), true);
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
it('returns false without $volatile and no handler', () => {
|
|
713
|
+
assert.equal(isVolatile({ $path: '/a', $type: 'x' } as any), false);
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
it('returns true when type has volatile handler', () => {
|
|
717
|
+
register('ephemeral', 'volatile', () => true);
|
|
718
|
+
assert.equal(isVolatile({ $path: '/a', $type: 'ephemeral' } as any), true);
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
it('$volatile false overrides type handler', () => {
|
|
722
|
+
register('ephemeral2', 'volatile', () => true);
|
|
723
|
+
assert.equal(isVolatile({ $path: '/a', $type: 'ephemeral2', $volatile: false } as any), false);
|
|
724
|
+
});
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
// ── OpError ──
|
|
728
|
+
|
|
729
|
+
describe('OpError', () => {
|
|
730
|
+
it('has correct name and code', () => {
|
|
731
|
+
const err = new OpError('NOT_FOUND', 'thing not found');
|
|
732
|
+
assert.equal(err.name, 'OpError');
|
|
733
|
+
assert.equal(err.code, 'NOT_FOUND');
|
|
734
|
+
assert.equal(err.message, 'thing not found');
|
|
735
|
+
assert.ok(err instanceof Error);
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
// ── TypesStore edge cases ──
|
|
740
|
+
|
|
741
|
+
describe('TypesStore', () => {
|
|
742
|
+
beforeEach(() => {
|
|
743
|
+
clearRegistry();
|
|
744
|
+
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
it('get returns undefined for unregistered type', async () => {
|
|
748
|
+
const backing = createMemoryTree();
|
|
749
|
+
const ts = createTypesStore(backing);
|
|
750
|
+
const result = await ts.get('/sys/types/nonexistent');
|
|
751
|
+
assert.equal(result, undefined);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
it('remove throws on registry type', async () => {
|
|
755
|
+
register('vendor.widget', 'schema', () => ({ properties: {} }));
|
|
756
|
+
const backing = createMemoryTree();
|
|
757
|
+
const ts = createTypesStore(backing);
|
|
758
|
+
|
|
759
|
+
await assert.rejects(() => ts.remove('/sys/types/vendor/widget'));
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
it('remove works on dynamic (stored) type', async () => {
|
|
763
|
+
const backing = createMemoryTree();
|
|
764
|
+
await backing.set(createNode('/sys/types/custom/thing', 'type'));
|
|
765
|
+
|
|
766
|
+
const ts = createTypesStore(backing);
|
|
767
|
+
const result = await ts.remove('/sys/types/custom/thing');
|
|
768
|
+
assert.equal(result, true);
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
it('set goes to backing store', async () => {
|
|
772
|
+
const backing = createMemoryTree();
|
|
773
|
+
const ts = createTypesStore(backing);
|
|
774
|
+
await ts.set(createNode('/sys/types/dynamic/type', 'type'));
|
|
775
|
+
|
|
776
|
+
const node = await backing.get('/sys/types/dynamic/type');
|
|
777
|
+
assert.ok(node);
|
|
778
|
+
});
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
// ── QueryStore: merged query from parent ──
|
|
782
|
+
|
|
783
|
+
describe('QueryStore advanced', () => {
|
|
784
|
+
it('merges external query with config match via $and', async () => {
|
|
785
|
+
const parent = createMemoryTree();
|
|
786
|
+
await parent.set({ $path: '/items/1', $type: 'item', status: { $type: 's', value: 'active' }, priority: 1 } as any);
|
|
787
|
+
await parent.set({ $path: '/items/2', $type: 'item', status: { $type: 's', value: 'active' }, priority: 5 } as any);
|
|
788
|
+
await parent.set({ $path: '/items/3', $type: 'item', status: { $type: 's', value: 'done' }, priority: 1 } as any);
|
|
789
|
+
|
|
790
|
+
const qs = createQueryTree({ source: '/items', match: { 'status.value': 'active' } }, parent);
|
|
791
|
+
// Pass additional query — should merge with $and
|
|
792
|
+
const result = await qs.getChildren('/view', { query: { priority: { $gt: 3 } } });
|
|
793
|
+
assert.equal(result.items.length, 1);
|
|
794
|
+
assert.equal(result.items[0].$path, '/items/2');
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
it('returns queryMount metadata', async () => {
|
|
798
|
+
const parent = createMemoryTree();
|
|
799
|
+
await parent.set({ $path: '/items/1', $type: 'item', status: { $type: 's', value: 'active' } } as any);
|
|
800
|
+
|
|
801
|
+
const qs = createQueryTree({ source: '/items', match: { 'status.value': 'active' } }, parent);
|
|
802
|
+
const result = await qs.getChildren('/view');
|
|
803
|
+
assert.deepEqual(result.queryMount, { source: '/items', match: { 'status.value': 'active' } });
|
|
804
|
+
});
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
// ── matchesFilter edge cases ──
|
|
808
|
+
|
|
809
|
+
describe('matchesFilter advanced', () => {
|
|
810
|
+
it('$regex matching', () => {
|
|
811
|
+
const node = { $path: '/a', $type: 'x', name: 'hello-world' } as NodeData;
|
|
812
|
+
assert.equal(matchesFilter(node, { name: { $regex: 'hello' } }), true);
|
|
813
|
+
assert.equal(matchesFilter(node, { name: { $regex: '^nope' } }), false);
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
it('$path in match maps correctly', () => {
|
|
817
|
+
const node = { $path: '/items/42', $type: 'item' } as NodeData;
|
|
818
|
+
assert.equal(matchesFilter(node, { $path: '/items/42' }), true);
|
|
819
|
+
assert.equal(matchesFilter(node, { $path: '/items/99' }), false);
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it('$acl / $owner in match', () => {
|
|
823
|
+
const node = { $path: '/a', $type: 'x', $owner: 'alice', $acl: [{ g: 'admin', p: 7 }] } as any;
|
|
824
|
+
assert.equal(matchesFilter(node, { $owner: 'alice' }), true);
|
|
825
|
+
assert.equal(matchesFilter(node, { $owner: 'bob' }), false);
|
|
826
|
+
});
|
|
827
|
+
});
|