@treenity/core 1.0.48 → 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 -314
- 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,216 @@
|
|
|
1
|
+
// Treenity Tree — Layer 1
|
|
2
|
+
// Storage interface + in-memory implementation
|
|
3
|
+
// Depends only on core types.
|
|
4
|
+
|
|
5
|
+
import { isRef, type NodeData, type Ref, toStorageKeys } from '#core';
|
|
6
|
+
import sift from 'sift';
|
|
7
|
+
import { applyOps, defaultPatch, type PatchOp } from './patch';
|
|
8
|
+
|
|
9
|
+
// ── Pagination ──
|
|
10
|
+
|
|
11
|
+
export type PageOpts = { limit?: number; offset?: number };
|
|
12
|
+
export type Page<T> = { items: T[]; total: number; queryMount?: { source: string, match: Record<string, unknown> } };
|
|
13
|
+
|
|
14
|
+
export function paginate<T>(items: T[], opts?: PageOpts): Page<T> {
|
|
15
|
+
const total = items.length;
|
|
16
|
+
if (!opts?.limit) return { items, total };
|
|
17
|
+
const offset = opts.offset ?? 0;
|
|
18
|
+
return { items: items.slice(offset, offset + opts.limit), total };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ── Interface ──
|
|
22
|
+
|
|
23
|
+
export type ChildrenOpts = { depth?: number; query?: Record<string, unknown> } & PageOpts;
|
|
24
|
+
|
|
25
|
+
export interface Tree {
|
|
26
|
+
get(path: string, ctx?: unknown): Promise<NodeData | undefined>;
|
|
27
|
+
getChildren(path: string, opts?: ChildrenOpts, ctx?: unknown): Promise<Page<NodeData>>;
|
|
28
|
+
set(node: NodeData, ctx?: unknown): Promise<void>;
|
|
29
|
+
remove(path: string, ctx?: unknown): Promise<boolean>;
|
|
30
|
+
patch(path: string, ops: PatchOp[], ctx?: unknown): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── In-memory implementation ──
|
|
34
|
+
|
|
35
|
+
// ── Ref resolution ──
|
|
36
|
+
|
|
37
|
+
export async function resolveRef(store: Tree, node: NodeData): Promise<NodeData> {
|
|
38
|
+
if (!isRef(node)) return node;
|
|
39
|
+
const target = await store.get((node as unknown as Ref).$ref);
|
|
40
|
+
if (!target) throw new Error(`Ref not found: ${(node as unknown as Ref).$ref}`);
|
|
41
|
+
return target;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── Filter store ──
|
|
45
|
+
// Like overlay, but set() routes to upper only when filter matches, else lower.
|
|
46
|
+
// Reads merge both layers (upper wins). Remove tries both.
|
|
47
|
+
// NOTE: limit/offset pagination is approximate — merging happens after both stores paginate.
|
|
48
|
+
|
|
49
|
+
export function createFilterTree(
|
|
50
|
+
upper: Tree,
|
|
51
|
+
lower: Tree,
|
|
52
|
+
toUpper: (node: NodeData) => boolean,
|
|
53
|
+
): Tree {
|
|
54
|
+
return {
|
|
55
|
+
async get(path, ctx) {
|
|
56
|
+
return (await upper.get(path, ctx)) ?? (await lower.get(path, ctx));
|
|
57
|
+
},
|
|
58
|
+
async getChildren(parent, opts, ctx) {
|
|
59
|
+
const passthrough = opts ? { depth: opts.depth, query: opts.query } : undefined;
|
|
60
|
+
const [u, l] = await Promise.all([
|
|
61
|
+
upper.getChildren(parent, passthrough, ctx),
|
|
62
|
+
lower.getChildren(parent, passthrough, ctx),
|
|
63
|
+
]);
|
|
64
|
+
const byPath = new Map<string, NodeData>();
|
|
65
|
+
for (const n of l.items) byPath.set(n.$path, n);
|
|
66
|
+
for (const n of u.items) byPath.set(n.$path, n);
|
|
67
|
+
const result = paginate([...byPath.values()], opts);
|
|
68
|
+
// Forward queryMount from lower store (mount system → CDC Matrix)
|
|
69
|
+
if (l.queryMount) result.queryMount = l.queryMount;
|
|
70
|
+
return result;
|
|
71
|
+
},
|
|
72
|
+
async set(node, ctx) {
|
|
73
|
+
if (toUpper(node)) await upper.set(node, ctx);
|
|
74
|
+
else await lower.set(node, ctx);
|
|
75
|
+
},
|
|
76
|
+
async remove(path, ctx) {
|
|
77
|
+
const a = await upper.remove(path, ctx);
|
|
78
|
+
const b = await lower.remove(path, ctx);
|
|
79
|
+
return a || b;
|
|
80
|
+
},
|
|
81
|
+
async patch(path, ops, ctx) {
|
|
82
|
+
const node = await upper.get(path, ctx) ?? await lower.get(path, ctx);
|
|
83
|
+
if (!node) throw new Error(`Node not found: ${path}`);
|
|
84
|
+
if (toUpper(node)) await upper.patch(path, ops, ctx);
|
|
85
|
+
else await lower.patch(path, ops, ctx);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Overlay store ──
|
|
91
|
+
// Reads: upper first, fall back to lower. Writes: upper only.
|
|
92
|
+
// Like createFilterTree(upper, lower, () => true) but remove only affects upper.
|
|
93
|
+
|
|
94
|
+
export function createOverlayTree(upper: Tree, lower: Tree): Tree {
|
|
95
|
+
return {
|
|
96
|
+
...createFilterTree(upper, lower, () => true),
|
|
97
|
+
async remove(path, ctx) {
|
|
98
|
+
return upper.remove(path, ctx);
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── In-memory implementation ──
|
|
104
|
+
|
|
105
|
+
function mapNodeForSift(node: NodeData): Record<string, unknown> {
|
|
106
|
+
return toStorageKeys(node);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type TreeNode<T> = {
|
|
110
|
+
data?: T;
|
|
111
|
+
children: Map<string, TreeNode<T>>;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export function treeNavigate<T>(root: TreeNode<T>, path: string): TreeNode<T> | undefined {
|
|
115
|
+
if (path === '/') return root;
|
|
116
|
+
const parts = path.slice(1).split('/');
|
|
117
|
+
let node = root;
|
|
118
|
+
for (const part of parts) {
|
|
119
|
+
const child = node.children.get(part);
|
|
120
|
+
if (!child) return undefined;
|
|
121
|
+
node = child;
|
|
122
|
+
}
|
|
123
|
+
return node;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function treeEnsure<T>(root: TreeNode<T>, path: string): TreeNode<T> {
|
|
127
|
+
if (path === '/') return root;
|
|
128
|
+
const parts = path.slice(1).split('/');
|
|
129
|
+
let node = root;
|
|
130
|
+
for (const part of parts) {
|
|
131
|
+
let child = node.children.get(part);
|
|
132
|
+
if (!child) {
|
|
133
|
+
child = { children: new Map() } as TreeNode<T>;
|
|
134
|
+
node.children.set(part, child);
|
|
135
|
+
}
|
|
136
|
+
node = child;
|
|
137
|
+
}
|
|
138
|
+
return node;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function createMemoryTree(): Tree {
|
|
142
|
+
const root: TreeNode<NodeData> = { children: new Map() };
|
|
143
|
+
const navigate = (path: string) => treeNavigate(root, path);
|
|
144
|
+
const ensurePath = (path: string) => treeEnsure(root, path);
|
|
145
|
+
|
|
146
|
+
function collectChildren(
|
|
147
|
+
node: TreeNode<NodeData>,
|
|
148
|
+
parentPath: string,
|
|
149
|
+
maxDepth: number,
|
|
150
|
+
currentDepth: number = 1,
|
|
151
|
+
): NodeData[] {
|
|
152
|
+
const result: NodeData[] = [];
|
|
153
|
+
if (currentDepth > maxDepth) return result;
|
|
154
|
+
|
|
155
|
+
for (const [name, child] of node.children) {
|
|
156
|
+
if (child.data) result.push(child.data);
|
|
157
|
+
if (currentDepth < maxDepth) {
|
|
158
|
+
const childPath = parentPath === '/' ? `/${name}` : `${parentPath}/${name}`;
|
|
159
|
+
result.push(...collectChildren(child, childPath, maxDepth, currentDepth + 1));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
async get(path, _ctx) {
|
|
167
|
+
if (typeof path !== 'string') throw new Error(`store.get: path must be string, got ${typeof path}`);
|
|
168
|
+
return navigate(path)?.data;
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
async getChildren(parent, opts, _ctx) {
|
|
172
|
+
const node = navigate(parent);
|
|
173
|
+
if (!node) return { items: [], total: 0 };
|
|
174
|
+
const depth = opts?.depth ?? 1;
|
|
175
|
+
let result = collectChildren(node, parent, depth);
|
|
176
|
+
if (opts?.query) {
|
|
177
|
+
const test = sift(opts.query);
|
|
178
|
+
result = result.filter(n => test(mapNodeForSift(n)));
|
|
179
|
+
}
|
|
180
|
+
return paginate(result, opts);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
async set(node, _ctx) {
|
|
184
|
+
const treeNode = ensurePath(node.$path);
|
|
185
|
+
|
|
186
|
+
if (node.$rev != null) {
|
|
187
|
+
// OCC: caller knows about rev — must match stored
|
|
188
|
+
const prevRev = treeNode.data?.$rev;
|
|
189
|
+
if (node.$rev !== prevRev) {
|
|
190
|
+
throw new Error(`OptimisticConcurrencyError: node ${node.$path} modified by another transaction. Expected $rev ${prevRev}, got ${node.$rev}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
node.$rev = (node.$rev ?? 0) + 1;
|
|
195
|
+
treeNode.data = structuredClone(node);
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
async remove(path, _ctx) {
|
|
199
|
+
const treeNode = navigate(path);
|
|
200
|
+
if (!treeNode?.data) return false;
|
|
201
|
+
treeNode.data = undefined;
|
|
202
|
+
return true;
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
async patch(path, ops, _ctx) {
|
|
206
|
+
const treeNode = navigate(path);
|
|
207
|
+
if (!treeNode?.data) throw new Error(`Node not found: ${path}`);
|
|
208
|
+
const copy = structuredClone(treeNode.data);
|
|
209
|
+
applyOps(copy, ops);
|
|
210
|
+
copy.$rev = (copy.$rev ?? 0) + 1;
|
|
211
|
+
treeNode.data = copy;
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export { type PatchOp, type Rfc6902Op, PatchTestError, applyOps, toRfc6902, fromRfc6902, defaultPatch } from './patch';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Inflight dedup — async memoize that auto-clears on settle.
|
|
2
|
+
// First caller runs fn(); concurrent callers share the same Promise.
|
|
3
|
+
// Once resolved/rejected, entry is deleted so retries work.
|
|
4
|
+
|
|
5
|
+
export function createInflight<V>(): (key: string, fn: () => Promise<V>) => Promise<V> {
|
|
6
|
+
const map = new Map<string, Promise<V>>();
|
|
7
|
+
|
|
8
|
+
return (key, fn) => {
|
|
9
|
+
let p = map.get(key);
|
|
10
|
+
if (p) return p;
|
|
11
|
+
p = fn().finally(() => map.delete(key));
|
|
12
|
+
map.set(key, p);
|
|
13
|
+
return p;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// JSON file ↔ node codec for RawFS store
|
|
2
|
+
// JSON files are plain objects — no metadata wrapping
|
|
3
|
+
|
|
4
|
+
import type { NodeData } from '#core';
|
|
5
|
+
import { register } from '#core';
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
7
|
+
|
|
8
|
+
export function registerJsonCodec() {
|
|
9
|
+
register('application/json', 'decode', async (filePath: string, nodePath: string) => {
|
|
10
|
+
const raw = await readFile(filePath, 'utf-8');
|
|
11
|
+
const obj = JSON.parse(raw);
|
|
12
|
+
return { ...obj, $path: nodePath, $type: obj.$type ?? 'application/json' } as NodeData;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
registerJsonCodec();
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import type { NodeData } from '#core';
|
|
2
|
+
import { register } from '#core';
|
|
3
|
+
import { clearRegistry } from '#core/index.test';
|
|
4
|
+
import assert from 'node:assert/strict';
|
|
5
|
+
import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { afterEach, beforeEach, describe, it } from 'node:test';
|
|
9
|
+
import { registerJsonCodec } from './json-codec';
|
|
10
|
+
import { createRawFsStore } from './mimefs';
|
|
11
|
+
|
|
12
|
+
describe('RawFsStore', () => {
|
|
13
|
+
let dir: string;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => { clearRegistry(); registerJsonCodec(); });
|
|
16
|
+
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
if (dir) await rm(dir, { recursive: true, force: true });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
async function setup() {
|
|
22
|
+
dir = await mkdtemp(join(tmpdir(), 'treenity-rawfs-test-'));
|
|
23
|
+
return createRawFsStore(dir);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
it('file → typed node', async () => {
|
|
27
|
+
const store = await setup();
|
|
28
|
+
await writeFile(join(dir, 'photo.jpg'), 'fake-jpeg');
|
|
29
|
+
const node = await store.get('/photo.jpg');
|
|
30
|
+
|
|
31
|
+
assert.equal(node?.$path, '/photo.jpg');
|
|
32
|
+
assert.equal(node?.$type, 'image/jpeg');
|
|
33
|
+
assert.ok((node as any).meta?.size > 0);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('directory → dir node', async () => {
|
|
37
|
+
const store = await setup();
|
|
38
|
+
await mkdir(join(dir, 'albums'));
|
|
39
|
+
const node = await store.get('/albums');
|
|
40
|
+
|
|
41
|
+
assert.equal(node?.$path, '/albums');
|
|
42
|
+
assert.equal(node?.$type, 'dir');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('missing path → undefined', async () => {
|
|
46
|
+
const store = await setup();
|
|
47
|
+
assert.equal(await store.get('/nope'), undefined);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('getChildren lists typed entries', async () => {
|
|
51
|
+
const store = await setup();
|
|
52
|
+
await writeFile(join(dir, 'a.txt'), 'text');
|
|
53
|
+
await writeFile(join(dir, 'b.csv'), 'col1,col2');
|
|
54
|
+
await mkdir(join(dir, 'sub'));
|
|
55
|
+
|
|
56
|
+
const { items } = await store.getChildren('/');
|
|
57
|
+
assert.equal(items.length, 3);
|
|
58
|
+
|
|
59
|
+
const byPath = Object.fromEntries(items.map(n => [n.$path, n.$type]));
|
|
60
|
+
assert.equal(byPath['/a.txt'], 'text/plain');
|
|
61
|
+
assert.equal(byPath['/b.csv'], 'text/csv');
|
|
62
|
+
assert.equal(byPath['/sub'], 'dir');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('getChildren respects depth', async () => {
|
|
66
|
+
const store = await setup();
|
|
67
|
+
await mkdir(join(dir, 'a'));
|
|
68
|
+
await writeFile(join(dir, 'a', 'deep.md'), '# hello');
|
|
69
|
+
|
|
70
|
+
const d1 = await store.getChildren('/', { depth: 1 });
|
|
71
|
+
assert.equal(d1.items.length, 1);
|
|
72
|
+
assert.equal(d1.items[0].$type, 'dir');
|
|
73
|
+
|
|
74
|
+
const d2 = await store.getChildren('/', { depth: 2 });
|
|
75
|
+
assert.equal(d2.items.length, 2);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('skips hidden files', async () => {
|
|
79
|
+
const store = await setup();
|
|
80
|
+
await writeFile(join(dir, '.hidden'), 'secret');
|
|
81
|
+
await writeFile(join(dir, 'visible.txt'), 'hi');
|
|
82
|
+
|
|
83
|
+
const { items } = await store.getChildren('/');
|
|
84
|
+
assert.equal(items.length, 1);
|
|
85
|
+
assert.equal(items[0].$path, '/visible.txt');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('mime type detection', async () => {
|
|
89
|
+
const store = await setup();
|
|
90
|
+
const cases: [string, string][] = [
|
|
91
|
+
['doc.pdf', 'application/pdf'],
|
|
92
|
+
['style.css', 'text/css'],
|
|
93
|
+
['data.json', 'application/json'], // needs valid JSON — codec parses it
|
|
94
|
+
['clip.mp4', 'video/mp4'],
|
|
95
|
+
['song.mp3', 'audio/mpeg'],
|
|
96
|
+
['page.html', 'text/html'],
|
|
97
|
+
['notes.md', 'text/markdown'],
|
|
98
|
+
['unknown.xyz', 'application/octet-stream'],
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
for (const [name, expectedType] of cases) {
|
|
102
|
+
const content = name.endsWith('.json') ? '{}' : 'data';
|
|
103
|
+
await writeFile(join(dir, name), content);
|
|
104
|
+
const node = await store.get('/' + name);
|
|
105
|
+
assert.equal(node?.$type, expectedType, `${name} should be ${expectedType}`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('json file → parsed object, no meta', async () => {
|
|
110
|
+
const store = await setup();
|
|
111
|
+
await writeFile(join(dir, 'config.json'), JSON.stringify({ name: 'test', count: 42 }));
|
|
112
|
+
const node = await store.get('/config.json');
|
|
113
|
+
|
|
114
|
+
assert.equal(node?.$path, '/config.json');
|
|
115
|
+
assert.equal(node?.$type, 'application/json');
|
|
116
|
+
assert.equal((node as any).name, 'test');
|
|
117
|
+
assert.equal((node as any).count, 42);
|
|
118
|
+
assert.equal((node as any).meta, undefined);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('json file preserves $type from content', async () => {
|
|
122
|
+
const store = await setup();
|
|
123
|
+
await writeFile(join(dir, 'typed.json'), JSON.stringify({ $type: 'my.custom', foo: 'bar' }));
|
|
124
|
+
const node = await store.get('/typed.json');
|
|
125
|
+
|
|
126
|
+
assert.equal(node?.$type, 'my.custom');
|
|
127
|
+
assert.equal((node as any).foo, 'bar');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('custom decode enriches node', async () => {
|
|
131
|
+
const store = await setup();
|
|
132
|
+
await writeFile(join(dir, 'data.csv'), 'name,age\nalice,30\nbob,25');
|
|
133
|
+
|
|
134
|
+
register('text/csv', 'decode', async (filePath: string, nodePath: string) => {
|
|
135
|
+
const { readFile } = await import('node:fs/promises');
|
|
136
|
+
const content = await readFile(filePath, 'utf-8');
|
|
137
|
+
const lines = content.trim().split('\n');
|
|
138
|
+
return {
|
|
139
|
+
$path: nodePath,
|
|
140
|
+
$type: 'text/csv',
|
|
141
|
+
columns: lines[0].split(','),
|
|
142
|
+
rowCount: lines.length - 1,
|
|
143
|
+
} as any;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const node = await store.get('/data.csv');
|
|
147
|
+
assert.equal(node?.$type, 'text/csv');
|
|
148
|
+
assert.deepEqual((node as any).columns, ['name', 'age']);
|
|
149
|
+
assert.equal((node as any).rowCount, 2);
|
|
150
|
+
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// --- Encode tests ---
|
|
154
|
+
|
|
155
|
+
it('set() with registered encode writes file', async () => {
|
|
156
|
+
const store = await setup();
|
|
157
|
+
|
|
158
|
+
register('text/plain', 'encode', async (node: NodeData, filePath: string) => {
|
|
159
|
+
await writeFile(filePath, (node as any).content ?? '');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
await store.set({ $path: '/hello.txt', $type: 'text/plain', content: 'world' } as any);
|
|
163
|
+
|
|
164
|
+
const raw = await readFile(join(dir, 'hello.txt'), 'utf-8');
|
|
165
|
+
assert.equal(raw, 'world');
|
|
166
|
+
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('set() without encode throws', async () => {
|
|
170
|
+
const store = await setup();
|
|
171
|
+
await assert.rejects(
|
|
172
|
+
() => store.set({ $path: '/x.bin', $type: 'application/octet-stream' } as any),
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('set() creates parent directories', async () => {
|
|
177
|
+
const store = await setup();
|
|
178
|
+
|
|
179
|
+
register('text/plain', 'encode', async (node: NodeData, filePath: string) => {
|
|
180
|
+
await writeFile(filePath, 'nested');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
await store.set({ $path: '/deep/nested/file.txt', $type: 'text/plain' } as any);
|
|
184
|
+
|
|
185
|
+
const raw = await readFile(join(dir, 'deep', 'nested', 'file.txt'), 'utf-8');
|
|
186
|
+
assert.equal(raw, 'nested');
|
|
187
|
+
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('remove() deletes file', async () => {
|
|
191
|
+
const store = await setup();
|
|
192
|
+
await writeFile(join(dir, 'gone.txt'), 'bye');
|
|
193
|
+
|
|
194
|
+
const result = await store.remove('/gone.txt');
|
|
195
|
+
assert.equal(result, true);
|
|
196
|
+
await assert.rejects(() => stat(join(dir, 'gone.txt')), { code: 'ENOENT' });
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('remove() missing file returns false', async () => {
|
|
200
|
+
const store = await setup();
|
|
201
|
+
const result = await store.remove('/nope.txt');
|
|
202
|
+
assert.equal(result, false);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('remove() deletes empty directory', async () => {
|
|
206
|
+
const store = await setup();
|
|
207
|
+
await mkdir(join(dir, 'empty'));
|
|
208
|
+
|
|
209
|
+
const result = await store.remove('/empty');
|
|
210
|
+
assert.equal(result, true);
|
|
211
|
+
await assert.rejects(() => stat(join(dir, 'empty')), { code: 'ENOENT' });
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// --- .env codec ---
|
|
215
|
+
|
|
216
|
+
it('.env file → application/x-env', async () => {
|
|
217
|
+
const store = await setup();
|
|
218
|
+
await writeFile(join(dir, 'config.env'), 'PORT=3000\nDB=treenity\n');
|
|
219
|
+
|
|
220
|
+
const node = await store.get('/config.env');
|
|
221
|
+
assert.equal(node?.$type, 'application/x-env');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('.env roundtrip: decode → encode → decode', async () => {
|
|
225
|
+
const store = await setup();
|
|
226
|
+
|
|
227
|
+
// Register env decode
|
|
228
|
+
register('application/x-env', 'decode', async (filePath: string, nodePath: string) => {
|
|
229
|
+
const content = await readFile(filePath, 'utf-8');
|
|
230
|
+
const env: Record<string, string> = {};
|
|
231
|
+
for (const line of content.split('\n')) {
|
|
232
|
+
const trimmed = line.trim();
|
|
233
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
234
|
+
const eq = trimmed.indexOf('=');
|
|
235
|
+
if (eq === -1) continue;
|
|
236
|
+
env[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
|
|
237
|
+
}
|
|
238
|
+
return { $path: nodePath, $type: 'application/x-env', env } as any;
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Register env encode
|
|
242
|
+
register('application/x-env', 'encode', async (node: NodeData, filePath: string) => {
|
|
243
|
+
const env = (node as any).env as Record<string, string>;
|
|
244
|
+
if (!env) throw new Error('env component required');
|
|
245
|
+
const lines = Object.entries(env).map(([k, v]) => `${k}=${v}`);
|
|
246
|
+
await writeFile(filePath, lines.join('\n') + '\n');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Write via store
|
|
250
|
+
await store.set({
|
|
251
|
+
$path: '/config.env',
|
|
252
|
+
$type: 'application/x-env',
|
|
253
|
+
env: { PORT: '3000', DB_NAME: 'treenity', FEATURE_X: 'true' },
|
|
254
|
+
} as any);
|
|
255
|
+
|
|
256
|
+
// Verify file on disk
|
|
257
|
+
const raw = await readFile(join(dir, 'config.env'), 'utf-8');
|
|
258
|
+
assert.ok(raw.includes('PORT=3000'));
|
|
259
|
+
assert.ok(raw.includes('DB_NAME=treenity'));
|
|
260
|
+
|
|
261
|
+
// Read back via store — roundtrip
|
|
262
|
+
const node = await store.get('/config.env');
|
|
263
|
+
assert.equal(node?.$type, 'application/x-env');
|
|
264
|
+
assert.deepEqual((node as any).env, { PORT: '3000', DB_NAME: 'treenity', FEATURE_X: 'true' });
|
|
265
|
+
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('.env decode skips comments and empty lines', async () => {
|
|
269
|
+
const store = await setup();
|
|
270
|
+
|
|
271
|
+
register('application/x-env', 'decode', async (filePath: string, nodePath: string) => {
|
|
272
|
+
const content = await readFile(filePath, 'utf-8');
|
|
273
|
+
const env: Record<string, string> = {};
|
|
274
|
+
for (const line of content.split('\n')) {
|
|
275
|
+
const trimmed = line.trim();
|
|
276
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
277
|
+
const eq = trimmed.indexOf('=');
|
|
278
|
+
if (eq === -1) continue;
|
|
279
|
+
env[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
|
|
280
|
+
}
|
|
281
|
+
return { $path: nodePath, $type: 'application/x-env', env } as any;
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
await writeFile(join(dir, 'config.env'), '# comment\n\nKEY=value\n# another\nFOO=bar\n');
|
|
285
|
+
const node = await store.get('/config.env');
|
|
286
|
+
assert.deepEqual((node as any).env, { KEY: 'value', FOO: 'bar' });
|
|
287
|
+
|
|
288
|
+
});
|
|
289
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Treenity RawFS Tree — Layer 1
|
|
2
|
+
// Bidirectional store that maps real filesystem files to typed nodes.
|
|
3
|
+
// Files become nodes with $type from mime type. Directories become $type "dir".
|
|
4
|
+
// "decode" context: file → node (read). "encode" context: node → file (write).
|
|
5
|
+
|
|
6
|
+
import type { NodeData } from '#core';
|
|
7
|
+
import { resolve as ctxResolve } from '#core/registry';
|
|
8
|
+
import { mkdir, readdir, rmdir, stat, unlink } from 'node:fs/promises';
|
|
9
|
+
import { dirname, extname, join, resolve } from 'node:path';
|
|
10
|
+
import type { Tree } from './index';
|
|
11
|
+
import { paginate } from './index';
|
|
12
|
+
import './json-codec'; // register JSON decode handler
|
|
13
|
+
import { defaultPatch } from './patch';
|
|
14
|
+
|
|
15
|
+
const MIME: Record<string, string> = {
|
|
16
|
+
'.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png',
|
|
17
|
+
'.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml',
|
|
18
|
+
'.ico': 'image/x-icon', '.bmp': 'image/bmp',
|
|
19
|
+
'.mp4': 'video/mp4', '.webm': 'video/webm', '.mov': 'video/quicktime',
|
|
20
|
+
'.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.ogg': 'audio/ogg', '.flac': 'audio/flac',
|
|
21
|
+
'.pdf': 'application/pdf', '.zip': 'application/zip', '.gz': 'application/gzip',
|
|
22
|
+
'.json': 'application/json', '.xml': 'application/xml', '.yaml': 'application/yaml',
|
|
23
|
+
'.yml': 'application/yaml',
|
|
24
|
+
'.csv': 'text/csv', '.txt': 'text/plain', '.md': 'text/markdown',
|
|
25
|
+
'.html': 'text/html', '.css': 'text/css',
|
|
26
|
+
'.js': 'text/javascript', '.ts': 'text/typescript', '.tsx': 'text/typescript',
|
|
27
|
+
'.py': 'text/x-python', '.sh': 'text/x-shellscript',
|
|
28
|
+
'.env': 'application/x-env',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function getMime(filename: string): string {
|
|
32
|
+
return MIME[extname(filename).toLowerCase()] ?? 'application/octet-stream';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type DecodeHandler = (filePath: string, nodePath: string) => Promise<NodeData>;
|
|
36
|
+
export type EncodeHandler = (node: NodeData, filePath: string) => Promise<void>;
|
|
37
|
+
|
|
38
|
+
declare module '#core/context' {
|
|
39
|
+
interface ContextHandlers {
|
|
40
|
+
decode: DecodeHandler;
|
|
41
|
+
encode: EncodeHandler;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function createRawFsStore(rootDir: string): Promise<Tree> {
|
|
46
|
+
rootDir = resolve(rootDir);
|
|
47
|
+
|
|
48
|
+
function toFilePath(path: string): string {
|
|
49
|
+
const full = resolve(join(rootDir, path));
|
|
50
|
+
if (!full.startsWith(rootDir)) throw new Error(`Path traversal blocked`);
|
|
51
|
+
return full;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function fileToNode(filePath: string, nodePath: string): Promise<NodeData> {
|
|
55
|
+
const st = await stat(filePath);
|
|
56
|
+
|
|
57
|
+
if (st.isDirectory()) {
|
|
58
|
+
return { $path: nodePath, $type: 'dir' } as NodeData;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const mime = getMime(filePath);
|
|
62
|
+
|
|
63
|
+
const decode = ctxResolve(mime, 'decode');
|
|
64
|
+
if (decode) return decode(filePath, nodePath);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
$path: nodePath,
|
|
68
|
+
$type: mime,
|
|
69
|
+
meta: { size: st.size, modified: st.mtime.toISOString(), created: st.birthtime.toISOString() },
|
|
70
|
+
} as NodeData;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const tree: Tree = {
|
|
74
|
+
async get(path) {
|
|
75
|
+
const file = toFilePath(path);
|
|
76
|
+
try {
|
|
77
|
+
return await fileToNode(file, path);
|
|
78
|
+
} catch (e: any) {
|
|
79
|
+
if (e.code === 'ENOENT') return undefined;
|
|
80
|
+
throw e;
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
async getChildren(parent, opts) {
|
|
85
|
+
const dir = toFilePath(parent);
|
|
86
|
+
const depth = opts?.depth ?? 1;
|
|
87
|
+
|
|
88
|
+
const results: NodeData[] = [];
|
|
89
|
+
|
|
90
|
+
async function walk(dirPath: string, parentNodePath: string, currentDepth: number) {
|
|
91
|
+
if (currentDepth > depth) return;
|
|
92
|
+
let entries;
|
|
93
|
+
try { entries = await readdir(dirPath, { withFileTypes: true }); } catch { return; }
|
|
94
|
+
|
|
95
|
+
for (const e of entries) {
|
|
96
|
+
if (e.name.startsWith('.')) continue; // skip hidden files
|
|
97
|
+
const filePath = join(dirPath, e.name);
|
|
98
|
+
const nodePath = parentNodePath === '/' ? `/${e.name}` : `${parentNodePath}/${e.name}`;
|
|
99
|
+
results.push(await fileToNode(filePath, nodePath));
|
|
100
|
+
|
|
101
|
+
if (e.isDirectory() && currentDepth < depth) {
|
|
102
|
+
await walk(filePath, nodePath, currentDepth + 1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await walk(dir, parent, 1);
|
|
108
|
+
return paginate(results, opts);
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
async set(node) {
|
|
112
|
+
const filePath = toFilePath(node.$path);
|
|
113
|
+
const encode = ctxResolve(node.$type, 'encode');
|
|
114
|
+
if (!encode) throw new Error(`No encode registered for type "${node.$type}"`);
|
|
115
|
+
|
|
116
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
117
|
+
await encode(node, filePath);
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
async remove(path) {
|
|
121
|
+
const filePath = toFilePath(path);
|
|
122
|
+
try {
|
|
123
|
+
const st = await stat(filePath);
|
|
124
|
+
if (st.isDirectory()) {
|
|
125
|
+
await rmdir(filePath);
|
|
126
|
+
} else {
|
|
127
|
+
await unlink(filePath);
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
} catch (e: any) {
|
|
131
|
+
if (e.code === 'ENOENT') return false;
|
|
132
|
+
throw e;
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
async patch(path, ops, ctx) {
|
|
137
|
+
return defaultPatch(tree.get, tree.set, path, ops, ctx);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return tree;
|
|
142
|
+
}
|