dotdo 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +446 -315
- package/dist/ai/index.js +19 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/template-literals.js +852 -0
- package/dist/ai/template-literals.js.map +1 -0
- package/dist/api/analytics/router.js +601 -0
- package/dist/api/analytics/router.js.map +1 -0
- package/dist/api/index.js +158 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/middleware/auth-federation.js +573 -0
- package/dist/api/middleware/auth-federation.js.map +1 -0
- package/dist/api/middleware/auth.js +544 -0
- package/dist/api/middleware/auth.js.map +1 -0
- package/dist/api/middleware/error-handling.js +176 -0
- package/dist/api/middleware/error-handling.js.map +1 -0
- package/dist/api/middleware/request-id.js +21 -0
- package/dist/api/middleware/request-id.js.map +1 -0
- package/dist/api/pages.js +1180 -0
- package/dist/api/pages.js.map +1 -0
- package/dist/api/routes/api.js +612 -0
- package/dist/api/routes/api.js.map +1 -0
- package/dist/api/routes/browsers.js +471 -0
- package/dist/api/routes/browsers.js.map +1 -0
- package/dist/api/routes/do.js +188 -0
- package/dist/api/routes/do.js.map +1 -0
- package/dist/api/routes/mcp.js +459 -0
- package/dist/api/routes/mcp.js.map +1 -0
- package/dist/api/routes/obs.js +445 -0
- package/dist/api/routes/obs.js.map +1 -0
- package/dist/api/routes/openapi.js +794 -0
- package/dist/api/routes/openapi.js.map +1 -0
- package/dist/api/routes/rpc.js +1103 -0
- package/dist/api/routes/rpc.js.map +1 -0
- package/dist/api/routes/sandboxes.js +389 -0
- package/dist/api/routes/sandboxes.js.map +1 -0
- package/dist/api/test-do.js +38 -0
- package/dist/api/test-do.js.map +1 -0
- package/dist/api/types.js +11 -0
- package/dist/api/types.js.map +1 -0
- package/dist/cli/bin.js +2 -0
- package/dist/cli/main.js +52342 -0
- package/dist/db/actions.js +212 -0
- package/dist/db/actions.js.map +1 -0
- package/dist/db/auth.js +506 -0
- package/dist/db/auth.js.map +1 -0
- package/dist/db/branches.js +65 -0
- package/dist/db/branches.js.map +1 -0
- package/dist/db/clickhouse.js +1074 -0
- package/dist/db/clickhouse.js.map +1 -0
- package/dist/db/dlq.js +39 -0
- package/dist/db/dlq.js.map +1 -0
- package/dist/db/events.js +28 -0
- package/dist/db/events.js.map +1 -0
- package/dist/db/exec.js +64 -0
- package/dist/db/exec.js.map +1 -0
- package/dist/db/files.js +85 -0
- package/dist/db/files.js.map +1 -0
- package/dist/db/flags.js +24 -0
- package/dist/db/flags.js.map +1 -0
- package/dist/db/git.js +116 -0
- package/dist/db/git.js.map +1 -0
- package/dist/db/iceberg/inverted-index.js +862 -0
- package/dist/db/iceberg/inverted-index.js.map +1 -0
- package/dist/db/iceberg/puffin.js +878 -0
- package/dist/db/iceberg/puffin.js.map +1 -0
- package/dist/db/iceberg/search-manifest.js +422 -0
- package/dist/db/iceberg/search-manifest.js.map +1 -0
- package/dist/db/iceberg/types.js +8 -0
- package/dist/db/iceberg/types.js.map +1 -0
- package/dist/db/index.js +121 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/integrations.js +368 -0
- package/dist/db/integrations.js.map +1 -0
- package/dist/db/json-indexes.js +332 -0
- package/dist/db/json-indexes.js.map +1 -0
- package/dist/db/linked-accounts.js +287 -0
- package/dist/db/linked-accounts.js.map +1 -0
- package/dist/db/nouns.js +183 -0
- package/dist/db/nouns.js.map +1 -0
- package/dist/db/objects.js +170 -0
- package/dist/db/objects.js.map +1 -0
- package/dist/db/primitives/dag-scheduler/index.js +869 -0
- package/dist/db/primitives/dag-scheduler/index.js.map +1 -0
- package/dist/db/primitives/exactly-once-context.js +237 -0
- package/dist/db/primitives/exactly-once-context.js.map +1 -0
- package/dist/db/primitives/index.js +62 -0
- package/dist/db/primitives/index.js.map +1 -0
- package/dist/db/primitives/keyed-router.js +145 -0
- package/dist/db/primitives/keyed-router.js.map +1 -0
- package/dist/db/primitives/observability.js +162 -0
- package/dist/db/primitives/observability.js.map +1 -0
- package/dist/db/primitives/schema-evolution.js +643 -0
- package/dist/db/primitives/schema-evolution.js.map +1 -0
- package/dist/db/primitives/stateful-operator/index.js +770 -0
- package/dist/db/primitives/stateful-operator/index.js.map +1 -0
- package/dist/db/primitives/temporal-store.js +306 -0
- package/dist/db/primitives/temporal-store.js.map +1 -0
- package/dist/db/primitives/typed-column-store.js +1229 -0
- package/dist/db/primitives/typed-column-store.js.map +1 -0
- package/dist/db/primitives/utils/duration.js +162 -0
- package/dist/db/primitives/utils/duration.js.map +1 -0
- package/dist/db/primitives/utils/murmur3.js +118 -0
- package/dist/db/primitives/utils/murmur3.js.map +1 -0
- package/dist/db/primitives/watermark-service.js +136 -0
- package/dist/db/primitives/watermark-service.js.map +1 -0
- package/dist/db/primitives/window-manager.js +764 -0
- package/dist/db/primitives/window-manager.js.map +1 -0
- package/dist/db/relationships.js +66 -0
- package/dist/db/relationships.js.map +1 -0
- package/dist/db/schema-minimal.js +61 -0
- package/dist/db/schema-minimal.js.map +1 -0
- package/dist/db/search.js +28 -0
- package/dist/db/search.js.map +1 -0
- package/dist/db/stores.js +1665 -0
- package/dist/db/stores.js.map +1 -0
- package/dist/db/things.js +297 -0
- package/dist/db/things.js.map +1 -0
- package/dist/db/vault.js +171 -0
- package/dist/db/vault.js.map +1 -0
- package/dist/db/verbs.js +102 -0
- package/dist/db/verbs.js.map +1 -0
- package/dist/do/base.js +48 -0
- package/dist/do/base.js.map +1 -0
- package/dist/do/bash.js +35 -0
- package/dist/do/bash.js.map +1 -0
- package/dist/do/fs.js +25 -0
- package/dist/do/fs.js.map +1 -0
- package/dist/do/full.js +61 -0
- package/dist/do/full.js.map +1 -0
- package/dist/do/git.js +28 -0
- package/dist/do/git.js.map +1 -0
- package/dist/do/index.js +52 -0
- package/dist/do/index.js.map +1 -0
- package/dist/do/tiny.js +31 -0
- package/dist/do/tiny.js.map +1 -0
- package/dist/lib/DOAuth.js +261 -0
- package/dist/lib/DOAuth.js.map +1 -0
- package/dist/lib/DODispatcher.js +72 -0
- package/dist/lib/DODispatcher.js.map +1 -0
- package/dist/lib/Modifier.js +189 -0
- package/dist/lib/Modifier.js.map +1 -0
- package/dist/lib/StateStorage.js +403 -0
- package/dist/lib/StateStorage.js.map +1 -0
- package/dist/lib/TypeRegistry.js +122 -0
- package/dist/lib/TypeRegistry.js.map +1 -0
- package/dist/lib/agent/tools/bash.js +336 -0
- package/dist/lib/agent/tools/bash.js.map +1 -0
- package/dist/lib/agent/tools/edit.js +157 -0
- package/dist/lib/agent/tools/edit.js.map +1 -0
- package/dist/lib/agent/tools/glob.js +137 -0
- package/dist/lib/agent/tools/glob.js.map +1 -0
- package/dist/lib/agent/tools/grep.js +315 -0
- package/dist/lib/agent/tools/grep.js.map +1 -0
- package/dist/lib/agent/tools/index.js +71 -0
- package/dist/lib/agent/tools/index.js.map +1 -0
- package/dist/lib/agent/tools/read.js +212 -0
- package/dist/lib/agent/tools/read.js.map +1 -0
- package/dist/lib/agent/tools/types.js +197 -0
- package/dist/lib/agent/tools/types.js.map +1 -0
- package/dist/lib/agent/tools/write.js +159 -0
- package/dist/lib/agent/tools/write.js.map +1 -0
- package/dist/lib/ai/gateway.js +247 -0
- package/dist/lib/ai/gateway.js.map +1 -0
- package/dist/lib/ai/tool-loop-agent.js +591 -0
- package/dist/lib/ai/tool-loop-agent.js.map +1 -0
- package/dist/lib/auto-wiring.js +439 -0
- package/dist/lib/auto-wiring.js.map +1 -0
- package/dist/lib/browse/browserbase.js +163 -0
- package/dist/lib/browse/browserbase.js.map +1 -0
- package/dist/lib/browse/cloudflare.js +144 -0
- package/dist/lib/browse/cloudflare.js.map +1 -0
- package/dist/lib/browse/index.js +62 -0
- package/dist/lib/browse/index.js.map +1 -0
- package/dist/lib/browse/types.js +13 -0
- package/dist/lib/browse/types.js.map +1 -0
- package/dist/lib/cache/index.js +37 -0
- package/dist/lib/cache/index.js.map +1 -0
- package/dist/lib/cache/visibility.js +638 -0
- package/dist/lib/cache/visibility.js.map +1 -0
- package/dist/lib/capabilities.js +268 -0
- package/dist/lib/capabilities.js.map +1 -0
- package/dist/lib/channels/base.js +106 -0
- package/dist/lib/channels/base.js.map +1 -0
- package/dist/lib/channels/discord.js +94 -0
- package/dist/lib/channels/discord.js.map +1 -0
- package/dist/lib/channels/email.js +204 -0
- package/dist/lib/channels/email.js.map +1 -0
- package/dist/lib/channels/index.js +90 -0
- package/dist/lib/channels/index.js.map +1 -0
- package/dist/lib/channels/mdxui-chat.js +95 -0
- package/dist/lib/channels/mdxui-chat.js.map +1 -0
- package/dist/lib/channels/slack-blockkit.js +121 -0
- package/dist/lib/channels/slack-blockkit.js.map +1 -0
- package/dist/lib/channels/types.js +7 -0
- package/dist/lib/channels/types.js.map +1 -0
- package/dist/lib/cloudflare/ai.js +654 -0
- package/dist/lib/cloudflare/ai.js.map +1 -0
- package/dist/lib/cloudflare/index.js +88 -0
- package/dist/lib/cloudflare/index.js.map +1 -0
- package/dist/lib/cloudflare/kv.js +342 -0
- package/dist/lib/cloudflare/kv.js.map +1 -0
- package/dist/lib/cloudflare/queues.js +434 -0
- package/dist/lib/cloudflare/queues.js.map +1 -0
- package/dist/lib/cloudflare/r2.js +604 -0
- package/dist/lib/cloudflare/r2.js.map +1 -0
- package/dist/lib/cloudflare/vectorize.js +494 -0
- package/dist/lib/cloudflare/vectorize.js.map +1 -0
- package/dist/lib/cloudflare/workflows.js +569 -0
- package/dist/lib/cloudflare/workflows.js.map +1 -0
- package/dist/lib/colo/caching.js +196 -0
- package/dist/lib/colo/caching.js.map +1 -0
- package/dist/lib/colo/detection.js +194 -0
- package/dist/lib/colo/detection.js.map +1 -0
- package/dist/lib/colo/external-data.js +219 -0
- package/dist/lib/colo/external-data.js.map +1 -0
- package/dist/lib/colo/globe-data.js +179 -0
- package/dist/lib/colo/globe-data.js.map +1 -0
- package/dist/lib/colo/index.js +16 -0
- package/dist/lib/colo/index.js.map +1 -0
- package/dist/lib/decorators.js +37 -0
- package/dist/lib/decorators.js.map +1 -0
- package/dist/lib/discovery.js +81 -0
- package/dist/lib/discovery.js.map +1 -0
- package/dist/lib/executors/AgenticFunctionExecutor.js +619 -0
- package/dist/lib/executors/AgenticFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/BaseFunctionExecutor.js +328 -0
- package/dist/lib/executors/BaseFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/CascadeExecutor.js +418 -0
- package/dist/lib/executors/CascadeExecutor.js.map +1 -0
- package/dist/lib/executors/CodeFunctionExecutor.js +904 -0
- package/dist/lib/executors/CodeFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/GenerativeFunctionExecutor.js +904 -0
- package/dist/lib/executors/GenerativeFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/HumanFunctionExecutor.js +884 -0
- package/dist/lib/executors/HumanFunctionExecutor.js.map +1 -0
- package/dist/lib/executors/ParallelStepExecutor.js +308 -0
- package/dist/lib/executors/ParallelStepExecutor.js.map +1 -0
- package/dist/lib/executors/types.js +12 -0
- package/dist/lib/executors/types.js.map +1 -0
- package/dist/lib/experiments.js +89 -0
- package/dist/lib/experiments.js.map +1 -0
- package/dist/lib/flags/store.js +262 -0
- package/dist/lib/flags/store.js.map +1 -0
- package/dist/lib/functions/FunctionComposition.js +467 -0
- package/dist/lib/functions/FunctionComposition.js.map +1 -0
- package/dist/lib/functions/FunctionMiddleware.js +457 -0
- package/dist/lib/functions/FunctionMiddleware.js.map +1 -0
- package/dist/lib/functions/FunctionRegistry.js +426 -0
- package/dist/lib/functions/FunctionRegistry.js.map +1 -0
- package/dist/lib/functions/createFunction.js +1048 -0
- package/dist/lib/functions/createFunction.js.map +1 -0
- package/dist/lib/humans/index.js +68 -0
- package/dist/lib/humans/index.js.map +1 -0
- package/dist/lib/humans/templates.js +117 -0
- package/dist/lib/humans/templates.js.map +1 -0
- package/dist/lib/identity.js +98 -0
- package/dist/lib/identity.js.map +1 -0
- package/dist/lib/index.js +9 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logging/error-logger.js +163 -0
- package/dist/lib/logging/error-logger.js.map +1 -0
- package/dist/lib/logging/index.js +160 -0
- package/dist/lib/logging/index.js.map +1 -0
- package/dist/lib/mixins/bash.js +825 -0
- package/dist/lib/mixins/bash.js.map +1 -0
- package/dist/lib/mixins/fs.js +648 -0
- package/dist/lib/mixins/fs.js.map +1 -0
- package/dist/lib/mixins/git.js +1011 -0
- package/dist/lib/mixins/git.js.map +1 -0
- package/dist/lib/mixins/index.js +29 -0
- package/dist/lib/mixins/index.js.map +1 -0
- package/dist/lib/mixins/npm.js +662 -0
- package/dist/lib/mixins/npm.js.map +1 -0
- package/dist/lib/noun-id.js +278 -0
- package/dist/lib/noun-id.js.map +1 -0
- package/dist/lib/rate-limit/sliding-window.js +148 -0
- package/dist/lib/rate-limit/sliding-window.js.map +1 -0
- package/dist/lib/rate-limit.js +110 -0
- package/dist/lib/rate-limit.js.map +1 -0
- package/dist/lib/rpc/bindings.js +548 -0
- package/dist/lib/rpc/bindings.js.map +1 -0
- package/dist/lib/rpc/index.js +64 -0
- package/dist/lib/rpc/index.js.map +1 -0
- package/dist/lib/safe-stringify.js +223 -0
- package/dist/lib/safe-stringify.js.map +1 -0
- package/dist/lib/sandbox/miniflare-sandbox.js +1007 -0
- package/dist/lib/sandbox/miniflare-sandbox.js.map +1 -0
- package/dist/lib/sqids.js +110 -0
- package/dist/lib/sqids.js.map +1 -0
- package/dist/lib/sql/adapters/index.js +10 -0
- package/dist/lib/sql/adapters/index.js.map +1 -0
- package/dist/lib/sql/adapters/node-sql-parser.js +552 -0
- package/dist/lib/sql/adapters/node-sql-parser.js.map +1 -0
- package/dist/lib/sql/adapters/pgsql-parser.js +1189 -0
- package/dist/lib/sql/adapters/pgsql-parser.js.map +1 -0
- package/dist/lib/sql/index.js +277 -0
- package/dist/lib/sql/index.js.map +1 -0
- package/dist/lib/sql/types.js +56 -0
- package/dist/lib/sql/types.js.map +1 -0
- package/dist/lib/type-classifier.js +126 -0
- package/dist/lib/type-classifier.js.map +1 -0
- package/dist/lib/utils/html.js +47 -0
- package/dist/lib/utils/html.js.map +1 -0
- package/dist/lib/validation.js +48 -0
- package/dist/lib/validation.js.map +1 -0
- package/dist/lib/vault/store.js +411 -0
- package/dist/lib/vault/store.js.map +1 -0
- package/dist/metrics/hunch.js +739 -0
- package/dist/metrics/hunch.js.map +1 -0
- package/dist/objects/API.js +302 -0
- package/dist/objects/API.js.map +1 -0
- package/dist/objects/Agent.js +179 -0
- package/dist/objects/Agent.js.map +1 -0
- package/dist/objects/AgenticFunctionExecutor.js +8 -0
- package/dist/objects/AgenticFunctionExecutor.js.map +1 -0
- package/dist/objects/App.js +83 -0
- package/dist/objects/App.js.map +1 -0
- package/dist/objects/Browser.js +884 -0
- package/dist/objects/Browser.js.map +1 -0
- package/dist/objects/Business.js +107 -0
- package/dist/objects/Business.js.map +1 -0
- package/dist/objects/CLI.js +221 -0
- package/dist/objects/CLI.js.map +1 -0
- package/dist/objects/CodeFunctionExecutor.js +8 -0
- package/dist/objects/CodeFunctionExecutor.js.map +1 -0
- package/dist/objects/Collection.js +161 -0
- package/dist/objects/Collection.js.map +1 -0
- package/dist/objects/DO.js +41 -0
- package/dist/objects/DO.js.map +1 -0
- package/dist/objects/DOBase.js +2309 -0
- package/dist/objects/DOBase.js.map +1 -0
- package/dist/objects/DOFull.js +1676 -0
- package/dist/objects/DOFull.js.map +1 -0
- package/dist/objects/DOTiny.js +207 -0
- package/dist/objects/DOTiny.js.map +1 -0
- package/dist/objects/Directory.js +199 -0
- package/dist/objects/Directory.js.map +1 -0
- package/dist/objects/Entity.js +413 -0
- package/dist/objects/Entity.js.map +1 -0
- package/dist/objects/Function.js +116 -0
- package/dist/objects/Function.js.map +1 -0
- package/dist/objects/Human.js +231 -0
- package/dist/objects/Human.js.map +1 -0
- package/dist/objects/HumanFunctionExecutor.js +8 -0
- package/dist/objects/HumanFunctionExecutor.js.map +1 -0
- package/dist/objects/IcebergMetadataDO.js +938 -0
- package/dist/objects/IcebergMetadataDO.js.map +1 -0
- package/dist/objects/IntegrationsDO.js +1174 -0
- package/dist/objects/IntegrationsDO.js.map +1 -0
- package/dist/objects/ObservabilityBroadcaster.js +149 -0
- package/dist/objects/ObservabilityBroadcaster.js.map +1 -0
- package/dist/objects/Package.js +154 -0
- package/dist/objects/Package.js.map +1 -0
- package/dist/objects/Product.js +193 -0
- package/dist/objects/Product.js.map +1 -0
- package/dist/objects/SDK.js +152 -0
- package/dist/objects/SDK.js.map +1 -0
- package/dist/objects/SaaS.js +235 -0
- package/dist/objects/SaaS.js.map +1 -0
- package/dist/objects/SandboxDO.js +759 -0
- package/dist/objects/SandboxDO.js.map +1 -0
- package/dist/objects/Service.js +337 -0
- package/dist/objects/Service.js.map +1 -0
- package/dist/objects/Site.js +80 -0
- package/dist/objects/Site.js.map +1 -0
- package/dist/objects/Startup.js +479 -0
- package/dist/objects/Startup.js.map +1 -0
- package/dist/objects/ThingsDO.js +170 -0
- package/dist/objects/ThingsDO.js.map +1 -0
- package/dist/objects/VectorShardDO.js +648 -0
- package/dist/objects/VectorShardDO.js.map +1 -0
- package/dist/objects/Worker.js +144 -0
- package/dist/objects/Worker.js.map +1 -0
- package/dist/objects/Workflow.js +196 -0
- package/dist/objects/Workflow.js.map +1 -0
- package/dist/objects/WorkflowFactory.js +313 -0
- package/dist/objects/WorkflowFactory.js.map +1 -0
- package/dist/objects/WorkflowRuntime.js +863 -0
- package/dist/objects/WorkflowRuntime.js.map +1 -0
- package/dist/objects/circuit-breaker-bulkhead.js +178 -0
- package/dist/objects/circuit-breaker-bulkhead.js.map +1 -0
- package/dist/objects/createFunction.js +934 -0
- package/dist/objects/createFunction.js.map +1 -0
- package/dist/objects/index.js +80 -0
- package/dist/objects/index.js.map +1 -0
- package/dist/objects/lifecycle/Branch.js +275 -0
- package/dist/objects/lifecycle/Branch.js.map +1 -0
- package/dist/objects/lifecycle/Clone.js +1499 -0
- package/dist/objects/lifecycle/Clone.js.map +1 -0
- package/dist/objects/lifecycle/Compact.js +237 -0
- package/dist/objects/lifecycle/Compact.js.map +1 -0
- package/dist/objects/lifecycle/Promote.js +476 -0
- package/dist/objects/lifecycle/Promote.js.map +1 -0
- package/dist/objects/lifecycle/Shard.js +560 -0
- package/dist/objects/lifecycle/Shard.js.map +1 -0
- package/dist/objects/lifecycle/index.js +15 -0
- package/dist/objects/lifecycle/index.js.map +1 -0
- package/dist/objects/lifecycle/types.js +33 -0
- package/dist/objects/lifecycle/types.js.map +1 -0
- package/dist/objects/mixins/infrastructure.js +171 -0
- package/dist/objects/mixins/infrastructure.js.map +1 -0
- package/dist/objects/modules/StoresModule.js +153 -0
- package/dist/objects/modules/StoresModule.js.map +1 -0
- package/dist/objects/persistence/checkpoint-manager.js +606 -0
- package/dist/objects/persistence/checkpoint-manager.js.map +1 -0
- package/dist/objects/persistence/index.js +72 -0
- package/dist/objects/persistence/index.js.map +1 -0
- package/dist/objects/persistence/migration-runner.js +562 -0
- package/dist/objects/persistence/migration-runner.js.map +1 -0
- package/dist/objects/persistence/replication-manager.js +501 -0
- package/dist/objects/persistence/replication-manager.js.map +1 -0
- package/dist/objects/persistence/tiered-storage-manager.js +595 -0
- package/dist/objects/persistence/tiered-storage-manager.js.map +1 -0
- package/dist/objects/persistence/types.js +14 -0
- package/dist/objects/persistence/types.js.map +1 -0
- package/dist/objects/persistence/wal-manager.js +653 -0
- package/dist/objects/persistence/wal-manager.js.map +1 -0
- package/dist/objects/presets/index.js +20 -0
- package/dist/objects/presets/index.js.map +1 -0
- package/dist/objects/presets/primitives.js +188 -0
- package/dist/objects/presets/primitives.js.map +1 -0
- package/dist/objects/primitives/alarm-adapter.js +141 -0
- package/dist/objects/primitives/alarm-adapter.js.map +1 -0
- package/dist/objects/primitives/index.js +337 -0
- package/dist/objects/primitives/index.js.map +1 -0
- package/dist/objects/primitives/storage-adapter.js +182 -0
- package/dist/objects/primitives/storage-adapter.js.map +1 -0
- package/dist/objects/primitives/with-primitives.js +102 -0
- package/dist/objects/primitives/with-primitives.js.map +1 -0
- package/dist/objects/services/StoreManager.js +227 -0
- package/dist/objects/services/StoreManager.js.map +1 -0
- package/dist/objects/services/index.js +13 -0
- package/dist/objects/services/index.js.map +1 -0
- package/dist/objects/transport/auth-layer.js +1451 -0
- package/dist/objects/transport/auth-layer.js.map +1 -0
- package/dist/objects/transport/capnweb-target.js +355 -0
- package/dist/objects/transport/capnweb-target.js.map +1 -0
- package/dist/objects/transport/chain.js +441 -0
- package/dist/objects/transport/chain.js.map +1 -0
- package/dist/objects/transport/handler.js +58 -0
- package/dist/objects/transport/handler.js.map +1 -0
- package/dist/objects/transport/index.js +53 -0
- package/dist/objects/transport/index.js.map +1 -0
- package/dist/objects/transport/mcp-server.js +690 -0
- package/dist/objects/transport/mcp-server.js.map +1 -0
- package/dist/objects/transport/rest-autowire.js +1507 -0
- package/dist/objects/transport/rest-autowire.js.map +1 -0
- package/dist/objects/transport/rest-router.js +440 -0
- package/dist/objects/transport/rest-router.js.map +1 -0
- package/dist/objects/transport/rpc-server.js +1536 -0
- package/dist/objects/transport/rpc-server.js.map +1 -0
- package/dist/objects/transport/shared.js +575 -0
- package/dist/objects/transport/shared.js.map +1 -0
- package/dist/objects/transport/sync-engine.js +291 -0
- package/dist/objects/transport/sync-engine.js.map +1 -0
- package/dist/objects/transport/types.js +8 -0
- package/dist/objects/transport/types.js.map +1 -0
- package/dist/primitives/bashx/src/ast/analyze.js +1472 -0
- package/dist/primitives/bashx/src/ast/analyze.js.map +1 -0
- package/dist/primitives/bashx/src/ast/parser.js +1488 -0
- package/dist/primitives/bashx/src/ast/parser.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/crypto.js +1954 -0
- package/dist/primitives/bashx/src/do/commands/crypto.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/data-processing.js +1812 -0
- package/dist/primitives/bashx/src/do/commands/data-processing.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/extended-utils.js +804 -0
- package/dist/primitives/bashx/src/do/commands/extended-utils.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/math-control.js +1122 -0
- package/dist/primitives/bashx/src/do/commands/math-control.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/posix-utils.js +1015 -0
- package/dist/primitives/bashx/src/do/commands/posix-utils.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/system-utils.js +687 -0
- package/dist/primitives/bashx/src/do/commands/system-utils.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/test-command.js +523 -0
- package/dist/primitives/bashx/src/do/commands/test-command.js.map +1 -0
- package/dist/primitives/bashx/src/do/commands/text-processing.js +1550 -0
- package/dist/primitives/bashx/src/do/commands/text-processing.js.map +1 -0
- package/dist/primitives/bashx/src/do/container-executor.js +429 -0
- package/dist/primitives/bashx/src/do/container-executor.js.map +1 -0
- package/dist/primitives/bashx/src/do/index.js +668 -0
- package/dist/primitives/bashx/src/do/index.js.map +1 -0
- package/dist/primitives/bashx/src/do/tiered-executor.js +2647 -0
- package/dist/primitives/bashx/src/do/tiered-executor.js.map +1 -0
- package/dist/primitives/bashx/src/do/worker.js +352 -0
- package/dist/primitives/bashx/src/do/worker.js.map +1 -0
- package/dist/primitives/bashx/src/types.js +10 -0
- package/dist/primitives/bashx/src/types.js.map +1 -0
- package/dist/primitives/fsx/core/backend.js +480 -0
- package/dist/primitives/fsx/core/backend.js.map +1 -0
- package/dist/primitives/fsx/core/constants.js +140 -0
- package/dist/primitives/fsx/core/constants.js.map +1 -0
- package/dist/primitives/fsx/core/fsx.js +1184 -0
- package/dist/primitives/fsx/core/fsx.js.map +1 -0
- package/dist/primitives/fsx/core/glob/glob.js +438 -0
- package/dist/primitives/fsx/core/glob/glob.js.map +1 -0
- package/dist/primitives/fsx/core/glob/index.js +8 -0
- package/dist/primitives/fsx/core/glob/index.js.map +1 -0
- package/dist/primitives/fsx/core/glob/match.js +392 -0
- package/dist/primitives/fsx/core/glob/match.js.map +1 -0
- package/dist/primitives/fsx/core/types.js +307 -0
- package/dist/primitives/fsx/core/types.js.map +1 -0
- package/dist/sandbox/index.js +258 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sdk/capnweb-compat.js +42 -0
- package/dist/sdk/capnweb-compat.js.map +1 -0
- package/dist/sdk/client.js +20 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/index.js +17 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/snippets/artifacts-config.js +241 -0
- package/dist/snippets/artifacts-config.js.map +1 -0
- package/dist/snippets/artifacts-ingest.js +832 -0
- package/dist/snippets/artifacts-ingest.js.map +1 -0
- package/dist/snippets/artifacts-serve.js +1035 -0
- package/dist/snippets/artifacts-serve.js.map +1 -0
- package/dist/snippets/artifacts-types.js +161 -0
- package/dist/snippets/artifacts-types.js.map +1 -0
- package/dist/snippets/cache-probe.js +376 -0
- package/dist/snippets/cache-probe.js.map +1 -0
- package/dist/snippets/cache.js +10 -0
- package/dist/snippets/cache.js.map +1 -0
- package/dist/snippets/events.js +469 -0
- package/dist/snippets/events.js.map +1 -0
- package/dist/snippets/index.js +7 -0
- package/dist/snippets/index.js.map +1 -0
- package/dist/snippets/proxy.js +495 -0
- package/dist/snippets/proxy.js.map +1 -0
- package/dist/snippets/search.js +1759 -0
- package/dist/snippets/search.js.map +1 -0
- package/dist/streams/index.js +30 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams/observability.js +68 -0
- package/dist/streams/observability.js.map +1 -0
- package/dist/types/AI.js +92 -0
- package/dist/types/AI.js.map +1 -0
- package/dist/types/AIFunction.js +171 -0
- package/dist/types/AIFunction.js.map +1 -0
- package/dist/types/BrowseVerb.js +89 -0
- package/dist/types/BrowseVerb.js.map +1 -0
- package/dist/types/Browser.js +31 -0
- package/dist/types/Browser.js.map +1 -0
- package/dist/types/Chaos.js +15 -0
- package/dist/types/Chaos.js.map +1 -0
- package/dist/types/CloudflareBindings.js +109 -0
- package/dist/types/CloudflareBindings.js.map +1 -0
- package/dist/types/Collection.js +50 -0
- package/dist/types/Collection.js.map +1 -0
- package/dist/types/DO.js +2 -0
- package/dist/types/DO.js.map +1 -0
- package/dist/types/DOLocation.js +63 -0
- package/dist/types/DOLocation.js.map +1 -0
- package/dist/types/EventHandler.js +57 -0
- package/dist/types/EventHandler.js.map +1 -0
- package/dist/types/Experiment.js +33 -0
- package/dist/types/Experiment.js.map +1 -0
- package/dist/types/Flag.js +57 -0
- package/dist/types/Flag.js.map +1 -0
- package/dist/types/Lifecycle.js +13 -0
- package/dist/types/Lifecycle.js.map +1 -0
- package/dist/types/Location.js +169 -0
- package/dist/types/Location.js.map +1 -0
- package/dist/types/Noun.js +66 -0
- package/dist/types/Noun.js.map +1 -0
- package/dist/types/SessionEvent.js +194 -0
- package/dist/types/SessionEvent.js.map +1 -0
- package/dist/types/Thing.js +55 -0
- package/dist/types/Thing.js.map +1 -0
- package/dist/types/ThingDO.js +153 -0
- package/dist/types/ThingDO.js.map +1 -0
- package/dist/types/Things.js +2 -0
- package/dist/types/Things.js.map +1 -0
- package/dist/types/Verb.js +119 -0
- package/dist/types/Verb.js.map +1 -0
- package/dist/types/WorkflowContext.js +70 -0
- package/dist/types/WorkflowContext.js.map +1 -0
- package/dist/types/analytics-api.js +13 -0
- package/dist/types/analytics-api.js.map +1 -0
- package/dist/types/capabilities.js +135 -0
- package/dist/types/capabilities.js.map +1 -0
- package/dist/types/drizzle.js +12 -0
- package/dist/types/drizzle.js.map +1 -0
- package/dist/types/event.js +201 -0
- package/dist/types/event.js.map +1 -0
- package/dist/types/fn.js +12 -0
- package/dist/types/fn.js.map +1 -0
- package/dist/types/iceberg.js +48 -0
- package/dist/types/iceberg.js.map +1 -0
- package/dist/types/ids.js +170 -0
- package/dist/types/ids.js.map +1 -0
- package/dist/types/index.js +41 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/introspect.js +54 -0
- package/dist/types/introspect.js.map +1 -0
- package/dist/types/observability.js +124 -0
- package/dist/types/observability.js.map +1 -0
- package/dist/types/sync-protocol.js +175 -0
- package/dist/types/sync-protocol.js.map +1 -0
- package/dist/types/vector.js +13 -0
- package/dist/types/vector.js.map +1 -0
- package/dist/workflows/ScheduleManager.js +473 -0
- package/dist/workflows/ScheduleManager.js.map +1 -0
- package/dist/workflows/StepDOBridge.js +149 -0
- package/dist/workflows/StepDOBridge.js.map +1 -0
- package/dist/workflows/StepResultStorage.js +232 -0
- package/dist/workflows/StepResultStorage.js.map +1 -0
- package/dist/workflows/WaitForEventManager.js +461 -0
- package/dist/workflows/WaitForEventManager.js.map +1 -0
- package/dist/workflows/analyzer.js +332 -0
- package/dist/workflows/analyzer.js.map +1 -0
- package/dist/workflows/compat/activity-router.js +484 -0
- package/dist/workflows/compat/activity-router.js.map +1 -0
- package/dist/workflows/compat/backends/cloudflare-workflows.js +431 -0
- package/dist/workflows/compat/backends/cloudflare-workflows.js.map +1 -0
- package/dist/workflows/compat/backends/index.js +14 -0
- package/dist/workflows/compat/backends/index.js.map +1 -0
- package/dist/workflows/compat/errors/index.js +375 -0
- package/dist/workflows/compat/errors/index.js.map +1 -0
- package/dist/workflows/compat/index.js +79 -0
- package/dist/workflows/compat/index.js.map +1 -0
- package/dist/workflows/compat/inngest/index.js +989 -0
- package/dist/workflows/compat/inngest/index.js.map +1 -0
- package/dist/workflows/compat/qstash/index.js +1263 -0
- package/dist/workflows/compat/qstash/index.js.map +1 -0
- package/dist/workflows/compat/temporal/activities.js +739 -0
- package/dist/workflows/compat/temporal/activities.js.map +1 -0
- package/dist/workflows/compat/temporal/child-workflows.js +154 -0
- package/dist/workflows/compat/temporal/child-workflows.js.map +1 -0
- package/dist/workflows/compat/temporal/client.js +381 -0
- package/dist/workflows/compat/temporal/client.js.map +1 -0
- package/dist/workflows/compat/temporal/context.js +309 -0
- package/dist/workflows/compat/temporal/context.js.map +1 -0
- package/dist/workflows/compat/temporal/determinism.js +216 -0
- package/dist/workflows/compat/temporal/determinism.js.map +1 -0
- package/dist/workflows/compat/temporal/errors.js +128 -0
- package/dist/workflows/compat/temporal/errors.js.map +1 -0
- package/dist/workflows/compat/temporal/index.js +2464 -0
- package/dist/workflows/compat/temporal/index.js.map +1 -0
- package/dist/workflows/compat/temporal/saga.js +504 -0
- package/dist/workflows/compat/temporal/saga.js.map +1 -0
- package/dist/workflows/compat/temporal/signals.js +364 -0
- package/dist/workflows/compat/temporal/signals.js.map +1 -0
- package/dist/workflows/compat/temporal/storage.js +271 -0
- package/dist/workflows/compat/temporal/storage.js.map +1 -0
- package/dist/workflows/compat/temporal/timers.js +347 -0
- package/dist/workflows/compat/temporal/timers.js.map +1 -0
- package/dist/workflows/compat/temporal/types.js +7 -0
- package/dist/workflows/compat/temporal/types.js.map +1 -0
- package/dist/workflows/compat/temporal/unified-primitives.js +339 -0
- package/dist/workflows/compat/temporal/unified-primitives.js.map +1 -0
- package/dist/workflows/compat/trigger/index.js +468 -0
- package/dist/workflows/compat/trigger/index.js.map +1 -0
- package/dist/workflows/compat/utils/index.js +69 -0
- package/dist/workflows/compat/utils/index.js.map +1 -0
- package/dist/workflows/context/correlation-capability.js +266 -0
- package/dist/workflows/context/correlation-capability.js.map +1 -0
- package/dist/workflows/context/correlation.js +484 -0
- package/dist/workflows/context/correlation.js.map +1 -0
- package/dist/workflows/context/experiment.js +289 -0
- package/dist/workflows/context/experiment.js.map +1 -0
- package/dist/workflows/context/flag.js +244 -0
- package/dist/workflows/context/flag.js.map +1 -0
- package/dist/workflows/context/foundation.js +648 -0
- package/dist/workflows/context/foundation.js.map +1 -0
- package/dist/workflows/context/human-base.js +106 -0
- package/dist/workflows/context/human-base.js.map +1 -0
- package/dist/workflows/context/human.js +368 -0
- package/dist/workflows/context/human.js.map +1 -0
- package/dist/workflows/context/measure.js +354 -0
- package/dist/workflows/context/measure.js.map +1 -0
- package/dist/workflows/context/rate-limit.js +358 -0
- package/dist/workflows/context/rate-limit.js.map +1 -0
- package/dist/workflows/context/user.js +117 -0
- package/dist/workflows/context/user.js.map +1 -0
- package/dist/workflows/context/vault.js +360 -0
- package/dist/workflows/context/vault.js.map +1 -0
- package/dist/workflows/data/entity-events/entity-events.js +489 -0
- package/dist/workflows/data/entity-events/entity-events.js.map +1 -0
- package/dist/workflows/data/experiment/index.js +599 -0
- package/dist/workflows/data/experiment/index.js.map +1 -0
- package/dist/workflows/data/goal/context.js +558 -0
- package/dist/workflows/data/goal/context.js.map +1 -0
- package/dist/workflows/data/goal/index.js +32 -0
- package/dist/workflows/data/goal/index.js.map +1 -0
- package/dist/workflows/data/measure/index.js +840 -0
- package/dist/workflows/data/measure/index.js.map +1 -0
- package/dist/workflows/data/stream/index.js +1215 -0
- package/dist/workflows/data/stream/index.js.map +1 -0
- package/dist/workflows/data/track/context.js +883 -0
- package/dist/workflows/data/track/context.js.map +1 -0
- package/dist/workflows/data/track/index.js +15 -0
- package/dist/workflows/data/track/index.js.map +1 -0
- package/dist/workflows/data/view/context.js +864 -0
- package/dist/workflows/data/view/context.js.map +1 -0
- package/dist/workflows/domain.js +93 -0
- package/dist/workflows/domain.js.map +1 -0
- package/dist/workflows/flag.js +176 -0
- package/dist/workflows/flag.js.map +1 -0
- package/dist/workflows/flags.js +217 -0
- package/dist/workflows/flags.js.map +1 -0
- package/dist/workflows/hash.js +209 -0
- package/dist/workflows/hash.js.map +1 -0
- package/dist/workflows/index.js +50 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/on.js +378 -0
- package/dist/workflows/on.js.map +1 -0
- package/dist/workflows/pipeline-promise.js +481 -0
- package/dist/workflows/pipeline-promise.js.map +1 -0
- package/dist/workflows/pipeline-types.js +20 -0
- package/dist/workflows/pipeline-types.js.map +1 -0
- package/dist/workflows/proxy.js +76 -0
- package/dist/workflows/proxy.js.map +1 -0
- package/dist/workflows/runtime.js +310 -0
- package/dist/workflows/runtime.js.map +1 -0
- package/dist/workflows/schedule-builder.js +327 -0
- package/dist/workflows/schedule-builder.js.map +1 -0
- package/dist/workflows/visibility/index.js +148 -0
- package/dist/workflows/visibility/index.js.map +1 -0
- package/dist/workflows/visibility/query-parser.js +150 -0
- package/dist/workflows/visibility/query-parser.js.map +1 -0
- package/dist/workflows/visibility/store.js +223 -0
- package/dist/workflows/visibility/store.js.map +1 -0
- package/dist/workflows/visibility/types.js +30 -0
- package/dist/workflows/visibility/types.js.map +1 -0
- package/dist/workflows/workflow.js +53 -0
- package/dist/workflows/workflow.js.map +1 -0
- package/package.json +279 -46
|
@@ -0,0 +1,1184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FSx - Main filesystem class
|
|
3
|
+
*
|
|
4
|
+
* Provides POSIX-like filesystem operations backed by a pluggable FsBackend.
|
|
5
|
+
* This is the primary interface for interacting with the virtual filesystem.
|
|
6
|
+
*
|
|
7
|
+
* The core FSx class is runtime-agnostic and has zero Cloudflare dependencies.
|
|
8
|
+
* For Durable Object integration, use the DOBackend from fsx/do.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { FSx, MemoryBackend } from '@dotdo/fsx'
|
|
13
|
+
*
|
|
14
|
+
* // Use with in-memory backend for testing
|
|
15
|
+
* const backend = new MemoryBackend()
|
|
16
|
+
* const fsx = new FSx(backend)
|
|
17
|
+
*
|
|
18
|
+
* // Write and read files
|
|
19
|
+
* await fsx.writeFile('/hello.txt', 'Hello, World!')
|
|
20
|
+
* const content = await fsx.readFile('/hello.txt')
|
|
21
|
+
*
|
|
22
|
+
* // Directory operations
|
|
23
|
+
* await fsx.mkdir('/mydir', { recursive: true })
|
|
24
|
+
* const files = await fsx.readdir('/mydir')
|
|
25
|
+
*
|
|
26
|
+
* // Check file stats
|
|
27
|
+
* const stats = await fsx.stat('/hello.txt')
|
|
28
|
+
* console.log(stats.size, stats.isFile())
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @module
|
|
32
|
+
*/
|
|
33
|
+
// Note: constants is referenced in JSDoc examples only
|
|
34
|
+
export { constants } from './constants.js';
|
|
35
|
+
/**
|
|
36
|
+
* Manages file watchers for a single FSx instance.
|
|
37
|
+
*
|
|
38
|
+
* Optimized for handling many concurrent watchers by using:
|
|
39
|
+
* - Path prefix indexing for O(log n) lookup of potentially matching watchers
|
|
40
|
+
* - Batch notification to avoid callback storms
|
|
41
|
+
* - Proper cancellation support via AbortController
|
|
42
|
+
*
|
|
43
|
+
* Since FSx runs in a Durable Object environment without native fs.watch,
|
|
44
|
+
* this class implements watching by hooking into FSx operations directly.
|
|
45
|
+
* When a file operation occurs, the WatchManager emits events to all
|
|
46
|
+
* registered watchers that match the affected path.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const manager = new WatchManager()
|
|
51
|
+
* const entry = manager.addWatcher('/home/user', true, (event, filename) => {
|
|
52
|
+
* console.log(`${event}: ${filename}`)
|
|
53
|
+
* })
|
|
54
|
+
*
|
|
55
|
+
* // Emit an event
|
|
56
|
+
* manager.emit('change', '/home/user/file.txt')
|
|
57
|
+
*
|
|
58
|
+
* // Later, remove the watcher
|
|
59
|
+
* manager.removeWatcher(entry)
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
class WatchManager {
|
|
63
|
+
/** Counter for generating unique watcher IDs */
|
|
64
|
+
nextId = 0;
|
|
65
|
+
/** All registered watchers indexed by ID for fast removal */
|
|
66
|
+
watchersById = new Map();
|
|
67
|
+
/**
|
|
68
|
+
* Index of watchers by their normalized path.
|
|
69
|
+
* Multiple watchers can watch the same path.
|
|
70
|
+
*/
|
|
71
|
+
watchersByPath = new Map();
|
|
72
|
+
// Reserved for future optimization - sorted watched paths for efficient prefix matching
|
|
73
|
+
// @ts-expect-error Reserved for future use
|
|
74
|
+
_sortedPaths = [];
|
|
75
|
+
// @ts-expect-error Reserved for future use
|
|
76
|
+
_pathsNeedSort = false;
|
|
77
|
+
/**
|
|
78
|
+
* Register a new file system watcher.
|
|
79
|
+
*
|
|
80
|
+
* @param path - The path to watch (file or directory)
|
|
81
|
+
* @param recursive - Whether to watch subdirectories recursively
|
|
82
|
+
* @param listener - Callback function to invoke on events
|
|
83
|
+
* @returns The watch entry for later removal
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const entry = manager.addWatcher('/home/user', true, (event, filename) => {
|
|
88
|
+
* console.log(`${event}: ${filename}`)
|
|
89
|
+
* })
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
addWatcher(path, recursive, listener) {
|
|
93
|
+
const normalizedPath = this.normalizePath(path);
|
|
94
|
+
const entry = {
|
|
95
|
+
id: this.nextId++,
|
|
96
|
+
path: normalizedPath,
|
|
97
|
+
recursive,
|
|
98
|
+
listener,
|
|
99
|
+
closed: false,
|
|
100
|
+
abortController: new AbortController(),
|
|
101
|
+
};
|
|
102
|
+
// Add to ID index
|
|
103
|
+
this.watchersById.set(entry.id, entry);
|
|
104
|
+
// Add to path index
|
|
105
|
+
let pathWatchers = this.watchersByPath.get(normalizedPath);
|
|
106
|
+
if (!pathWatchers) {
|
|
107
|
+
pathWatchers = new Set();
|
|
108
|
+
this.watchersByPath.set(normalizedPath, pathWatchers);
|
|
109
|
+
this._pathsNeedSort = true;
|
|
110
|
+
}
|
|
111
|
+
pathWatchers.add(entry);
|
|
112
|
+
return entry;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Remove a watcher and clean up its resources.
|
|
116
|
+
*
|
|
117
|
+
* @param entry - The watch entry to remove
|
|
118
|
+
*/
|
|
119
|
+
removeWatcher(entry) {
|
|
120
|
+
entry.closed = true;
|
|
121
|
+
entry.abortController.abort();
|
|
122
|
+
// Remove from ID index
|
|
123
|
+
this.watchersById.delete(entry.id);
|
|
124
|
+
// Remove from path index
|
|
125
|
+
const pathWatchers = this.watchersByPath.get(entry.path);
|
|
126
|
+
if (pathWatchers) {
|
|
127
|
+
pathWatchers.delete(entry);
|
|
128
|
+
if (pathWatchers.size === 0) {
|
|
129
|
+
this.watchersByPath.delete(entry.path);
|
|
130
|
+
this._pathsNeedSort = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get the AbortSignal for a watcher, useful for cancellation.
|
|
136
|
+
*
|
|
137
|
+
* @param entry - The watch entry
|
|
138
|
+
* @returns The AbortSignal that will be triggered when the watcher is closed
|
|
139
|
+
*/
|
|
140
|
+
getAbortSignal(entry) {
|
|
141
|
+
return entry.abortController.signal;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get the total number of active watchers.
|
|
145
|
+
*
|
|
146
|
+
* @returns Number of registered watchers
|
|
147
|
+
*/
|
|
148
|
+
get watcherCount() {
|
|
149
|
+
return this.watchersById.size;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Emit a file system event to all matching watchers.
|
|
153
|
+
*
|
|
154
|
+
* Uses optimized path matching to minimize iteration:
|
|
155
|
+
* 1. Direct path watchers (exact match)
|
|
156
|
+
* 2. Parent directory watchers (non-recursive, direct children only)
|
|
157
|
+
* 3. Ancestor directory watchers (recursive)
|
|
158
|
+
*
|
|
159
|
+
* @param eventType - 'change' for content modifications, 'rename' for create/delete/rename
|
|
160
|
+
* @param affectedPath - The full normalized path that was affected
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* // Emit a change event for a modified file
|
|
165
|
+
* manager.emit('change', '/home/user/file.txt')
|
|
166
|
+
*
|
|
167
|
+
* // Emit a rename event for a created file
|
|
168
|
+
* manager.emit('rename', '/home/user/new-file.txt')
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
emit(eventType, affectedPath) {
|
|
172
|
+
const normalizedAffected = this.normalizePath(affectedPath);
|
|
173
|
+
const matchingWatchers = [];
|
|
174
|
+
// 1. Check for exact path watchers (watching this specific file/dir)
|
|
175
|
+
const exactWatchers = this.watchersByPath.get(normalizedAffected);
|
|
176
|
+
if (exactWatchers) {
|
|
177
|
+
for (const watcher of exactWatchers) {
|
|
178
|
+
if (!watcher.closed) {
|
|
179
|
+
const filename = this.getBasename(normalizedAffected);
|
|
180
|
+
matchingWatchers.push({ watcher, filename });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// 2. Check for parent directory watchers
|
|
185
|
+
let currentPath = this.getParentPath(normalizedAffected);
|
|
186
|
+
if (currentPath !== normalizedAffected) {
|
|
187
|
+
// Direct parent watchers (both recursive and non-recursive)
|
|
188
|
+
const parentWatchers = this.watchersByPath.get(currentPath);
|
|
189
|
+
if (parentWatchers) {
|
|
190
|
+
for (const watcher of parentWatchers) {
|
|
191
|
+
if (!watcher.closed) {
|
|
192
|
+
const filename = this.getBasename(normalizedAffected);
|
|
193
|
+
matchingWatchers.push({ watcher, filename });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// 3. Check for ancestor directory watchers (recursive only)
|
|
198
|
+
let ancestorPath = this.getParentPath(currentPath);
|
|
199
|
+
while (ancestorPath !== currentPath) {
|
|
200
|
+
const ancestorWatchers = this.watchersByPath.get(ancestorPath);
|
|
201
|
+
if (ancestorWatchers) {
|
|
202
|
+
for (const watcher of ancestorWatchers) {
|
|
203
|
+
if (!watcher.closed && watcher.recursive) {
|
|
204
|
+
const filename = this.getRelativePath(ancestorPath, normalizedAffected);
|
|
205
|
+
matchingWatchers.push({ watcher, filename });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
currentPath = ancestorPath;
|
|
210
|
+
ancestorPath = this.getParentPath(ancestorPath);
|
|
211
|
+
}
|
|
212
|
+
// Check root watchers if we're not at root
|
|
213
|
+
if (currentPath !== '/') {
|
|
214
|
+
const rootWatchers = this.watchersByPath.get('/');
|
|
215
|
+
if (rootWatchers) {
|
|
216
|
+
for (const watcher of rootWatchers) {
|
|
217
|
+
if (!watcher.closed && watcher.recursive) {
|
|
218
|
+
const filename = normalizedAffected.slice(1); // Remove leading /
|
|
219
|
+
matchingWatchers.push({ watcher, filename });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Fire all callbacks asynchronously using queueMicrotask for batching
|
|
226
|
+
for (const { watcher, filename } of matchingWatchers) {
|
|
227
|
+
queueMicrotask(() => {
|
|
228
|
+
if (!watcher.closed) {
|
|
229
|
+
try {
|
|
230
|
+
watcher.listener(eventType, filename);
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// Swallow listener errors to prevent breaking other watchers
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Normalize a path by removing trailing slashes (except for root).
|
|
241
|
+
* @param path - Path to normalize
|
|
242
|
+
* @returns Normalized path
|
|
243
|
+
*/
|
|
244
|
+
normalizePath(path) {
|
|
245
|
+
if (path === '/' || path === '')
|
|
246
|
+
return '/';
|
|
247
|
+
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get the parent directory path.
|
|
251
|
+
* @param path - Path to get parent of
|
|
252
|
+
* @returns Parent path, or the same path if at root
|
|
253
|
+
*/
|
|
254
|
+
getParentPath(path) {
|
|
255
|
+
if (path === '/')
|
|
256
|
+
return '/';
|
|
257
|
+
const lastSlash = path.lastIndexOf('/');
|
|
258
|
+
if (lastSlash <= 0)
|
|
259
|
+
return '/';
|
|
260
|
+
return path.slice(0, lastSlash);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get the basename (final component) of a path.
|
|
264
|
+
* @param path - Path to get basename of
|
|
265
|
+
* @returns Basename
|
|
266
|
+
*/
|
|
267
|
+
getBasename(path) {
|
|
268
|
+
const lastSlash = path.lastIndexOf('/');
|
|
269
|
+
return lastSlash >= 0 ? path.slice(lastSlash + 1) : path;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get the relative path from a base to a target.
|
|
273
|
+
* @param basePath - Base path
|
|
274
|
+
* @param targetPath - Target path
|
|
275
|
+
* @returns Relative path from base to target
|
|
276
|
+
*/
|
|
277
|
+
getRelativePath(basePath, targetPath) {
|
|
278
|
+
const normalizedBase = this.normalizePath(basePath);
|
|
279
|
+
const normalizedTarget = this.normalizePath(targetPath);
|
|
280
|
+
if (normalizedBase === '/') {
|
|
281
|
+
return normalizedTarget.slice(1);
|
|
282
|
+
}
|
|
283
|
+
if (normalizedTarget.startsWith(normalizedBase + '/')) {
|
|
284
|
+
return normalizedTarget.slice(normalizedBase.length + 1);
|
|
285
|
+
}
|
|
286
|
+
return this.getBasename(normalizedTarget);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const DEFAULT_OPTIONS = {
|
|
290
|
+
tiers: {
|
|
291
|
+
hotMaxSize: 1024 * 1024, // 1MB
|
|
292
|
+
warmEnabled: true,
|
|
293
|
+
coldEnabled: false,
|
|
294
|
+
},
|
|
295
|
+
defaultMode: 0o644,
|
|
296
|
+
defaultDirMode: 0o755,
|
|
297
|
+
tmpMaxAge: 24 * 60 * 60 * 1000, // 24 hours
|
|
298
|
+
maxFileSize: 100 * 1024 * 1024, // 100MB
|
|
299
|
+
maxPathLength: 4096,
|
|
300
|
+
uid: 0,
|
|
301
|
+
gid: 0,
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* FSx - Virtual filesystem with pluggable backend
|
|
305
|
+
*
|
|
306
|
+
* The core FSx class is runtime-agnostic and works with any FsBackend implementation.
|
|
307
|
+
* For Cloudflare Durable Objects, use the DOBackend from fsx/do.
|
|
308
|
+
*/
|
|
309
|
+
export class FSx {
|
|
310
|
+
backend;
|
|
311
|
+
options;
|
|
312
|
+
watchManager = new WatchManager();
|
|
313
|
+
constructor(backend, options = {}) {
|
|
314
|
+
this.backend = backend;
|
|
315
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Normalize a path
|
|
319
|
+
*/
|
|
320
|
+
normalizePath(path) {
|
|
321
|
+
// Remove trailing slashes (except root)
|
|
322
|
+
if (path !== '/' && path.endsWith('/')) {
|
|
323
|
+
path = path.slice(0, -1);
|
|
324
|
+
}
|
|
325
|
+
// Ensure starts with /
|
|
326
|
+
if (!path.startsWith('/')) {
|
|
327
|
+
path = '/' + path;
|
|
328
|
+
}
|
|
329
|
+
// Resolve . and ..
|
|
330
|
+
const parts = path.split('/').filter(Boolean);
|
|
331
|
+
const resolved = [];
|
|
332
|
+
for (const part of parts) {
|
|
333
|
+
if (part === '.')
|
|
334
|
+
continue;
|
|
335
|
+
if (part === '..') {
|
|
336
|
+
resolved.pop();
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
resolved.push(part);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return '/' + resolved.join('/');
|
|
343
|
+
}
|
|
344
|
+
// ==================== File Operations ====================
|
|
345
|
+
/**
|
|
346
|
+
* Read a file's contents
|
|
347
|
+
*
|
|
348
|
+
* Reads the entire contents of a file. By default, returns a UTF-8 decoded string.
|
|
349
|
+
* Use the encoding parameter to control the output format.
|
|
350
|
+
*
|
|
351
|
+
* @param path - Path to the file to read
|
|
352
|
+
* @param encoding - Output encoding: 'utf-8'/'utf8' (default), 'base64', or undefined for raw bytes
|
|
353
|
+
* @returns File contents as a string or Uint8Array depending on encoding
|
|
354
|
+
* @throws {ENOENT} If the file does not exist
|
|
355
|
+
* @throws {EISDIR} If the path is a directory
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```typescript
|
|
359
|
+
* // Read as UTF-8 string (default)
|
|
360
|
+
* const text = await fsx.readFile('/hello.txt')
|
|
361
|
+
*
|
|
362
|
+
* // Read as raw bytes
|
|
363
|
+
* const bytes = await fsx.readFile('/image.png', undefined)
|
|
364
|
+
*
|
|
365
|
+
* // Read as base64
|
|
366
|
+
* const base64 = await fsx.readFile('/image.png', 'base64')
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
async readFile(path, encoding) {
|
|
370
|
+
path = this.normalizePath(path);
|
|
371
|
+
const bytes = await this.backend.readFile(path);
|
|
372
|
+
// If utf-8 encoding requested (or default), decode bytes to string
|
|
373
|
+
if (!encoding || encoding === 'utf-8' || encoding === 'utf8') {
|
|
374
|
+
return new TextDecoder().decode(bytes);
|
|
375
|
+
}
|
|
376
|
+
// If base64 encoding requested, encode bytes to base64
|
|
377
|
+
if (encoding === 'base64') {
|
|
378
|
+
let binary = '';
|
|
379
|
+
for (const byte of bytes) {
|
|
380
|
+
binary += String.fromCharCode(byte);
|
|
381
|
+
}
|
|
382
|
+
return btoa(binary);
|
|
383
|
+
}
|
|
384
|
+
// For other encodings or no encoding, return bytes
|
|
385
|
+
return bytes;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Write data to a file
|
|
389
|
+
*
|
|
390
|
+
* Writes data to a file, replacing the file if it already exists.
|
|
391
|
+
* Creates any necessary parent directories.
|
|
392
|
+
*
|
|
393
|
+
* @param path - Path to the file to write
|
|
394
|
+
* @param data - Content to write (string or bytes)
|
|
395
|
+
* @param options - Write options
|
|
396
|
+
* @param options.mode - File permissions (default: 0o644)
|
|
397
|
+
* @param options.flag - File system flag: 'w' (write/create), 'a' (append), 'wx' (exclusive create)
|
|
398
|
+
* @throws {EISDIR} If the path is a directory
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* // Write a string
|
|
403
|
+
* await fsx.writeFile('/hello.txt', 'Hello, World!')
|
|
404
|
+
*
|
|
405
|
+
* // Write binary data
|
|
406
|
+
* await fsx.writeFile('/data.bin', new Uint8Array([1, 2, 3]))
|
|
407
|
+
*
|
|
408
|
+
* // Write with specific permissions
|
|
409
|
+
* await fsx.writeFile('/script.sh', '#!/bin/bash', { mode: 0o755 })
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
async writeFile(path, data, options) {
|
|
413
|
+
path = this.normalizePath(path);
|
|
414
|
+
// Check if file exists before writing (to determine event type)
|
|
415
|
+
const fileExisted = await this.backend.exists(path);
|
|
416
|
+
// Convert string to bytes
|
|
417
|
+
const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;
|
|
418
|
+
await this.backend.writeFile(path, bytes, {
|
|
419
|
+
mode: options?.mode ?? this.options.defaultMode,
|
|
420
|
+
flag: options?.flag,
|
|
421
|
+
});
|
|
422
|
+
// Emit watch event - 'rename' for new file, 'change' for existing
|
|
423
|
+
this.watchManager.emit(fileExisted ? 'change' : 'rename', path);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Append data to a file
|
|
427
|
+
*
|
|
428
|
+
* Appends data to the end of a file. Creates the file if it doesn't exist.
|
|
429
|
+
*
|
|
430
|
+
* @param path - Path to the file
|
|
431
|
+
* @param data - Content to append
|
|
432
|
+
* @throws {EISDIR} If the path is a directory
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```typescript
|
|
436
|
+
* await fsx.appendFile('/log.txt', 'New log entry\n')
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
async appendFile(path, data) {
|
|
440
|
+
return this.writeFile(path, data, { flag: 'a' });
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Delete a file
|
|
444
|
+
*
|
|
445
|
+
* Removes a file from the filesystem. Does not work on directories.
|
|
446
|
+
*
|
|
447
|
+
* @param path - Path to the file to delete
|
|
448
|
+
* @throws {ENOENT} If the file does not exist
|
|
449
|
+
* @throws {EISDIR} If the path is a directory (use rmdir or rm instead)
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```typescript
|
|
453
|
+
* await fsx.unlink('/old-file.txt')
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
async unlink(path) {
|
|
457
|
+
path = this.normalizePath(path);
|
|
458
|
+
await this.backend.unlink(path);
|
|
459
|
+
// Emit watch event - 'rename' for file deletion
|
|
460
|
+
this.watchManager.emit('rename', path);
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Rename or move a file or directory
|
|
464
|
+
*
|
|
465
|
+
* Atomically renames or moves a file/directory from oldPath to newPath.
|
|
466
|
+
* Can be used to move files between directories.
|
|
467
|
+
*
|
|
468
|
+
* @param oldPath - Current path
|
|
469
|
+
* @param newPath - New path
|
|
470
|
+
* @throws {ENOENT} If oldPath does not exist
|
|
471
|
+
* @throws {EEXIST} If newPath already exists (for some filesystem configurations)
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* // Rename a file
|
|
476
|
+
* await fsx.rename('/old-name.txt', '/new-name.txt')
|
|
477
|
+
*
|
|
478
|
+
* // Move to another directory
|
|
479
|
+
* await fsx.rename('/file.txt', '/archive/file.txt')
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
async rename(oldPath, newPath) {
|
|
483
|
+
oldPath = this.normalizePath(oldPath);
|
|
484
|
+
newPath = this.normalizePath(newPath);
|
|
485
|
+
await this.backend.rename(oldPath, newPath);
|
|
486
|
+
// Emit watch events - 'rename' for both old and new paths
|
|
487
|
+
this.watchManager.emit('rename', oldPath);
|
|
488
|
+
this.watchManager.emit('rename', newPath);
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Copy a file
|
|
492
|
+
*
|
|
493
|
+
* Creates a copy of a file at the destination path.
|
|
494
|
+
*
|
|
495
|
+
* @param src - Source file path
|
|
496
|
+
* @param dest - Destination file path
|
|
497
|
+
* @param flags - Copy flags (e.g., constants.COPYFILE_EXCL to fail if dest exists)
|
|
498
|
+
* @throws {ENOENT} If the source file does not exist
|
|
499
|
+
* @throws {EEXIST} If dest exists and COPYFILE_EXCL flag is set
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* ```typescript
|
|
503
|
+
* // Simple copy
|
|
504
|
+
* await fsx.copyFile('/original.txt', '/backup.txt')
|
|
505
|
+
*
|
|
506
|
+
* // Fail if destination exists
|
|
507
|
+
* await fsx.copyFile('/src.txt', '/dst.txt', constants.COPYFILE_EXCL)
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
async copyFile(src, dest, _flags) {
|
|
511
|
+
src = this.normalizePath(src);
|
|
512
|
+
dest = this.normalizePath(dest);
|
|
513
|
+
await this.backend.copyFile(src, dest);
|
|
514
|
+
}
|
|
515
|
+
// ==================== Directory Operations ====================
|
|
516
|
+
/**
|
|
517
|
+
* Create a directory
|
|
518
|
+
*
|
|
519
|
+
* Creates a new directory. With recursive option, creates parent directories
|
|
520
|
+
* as needed (like `mkdir -p`).
|
|
521
|
+
*
|
|
522
|
+
* @param path - Path for the new directory
|
|
523
|
+
* @param options - Creation options
|
|
524
|
+
* @param options.recursive - Create parent directories if needed (default: false)
|
|
525
|
+
* @param options.mode - Directory permissions (default: 0o755)
|
|
526
|
+
* @throws {EEXIST} If directory already exists (unless recursive is true)
|
|
527
|
+
* @throws {ENOENT} If parent doesn't exist and recursive is false
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```typescript
|
|
531
|
+
* // Create a single directory
|
|
532
|
+
* await fsx.mkdir('/mydir')
|
|
533
|
+
*
|
|
534
|
+
* // Create nested directories
|
|
535
|
+
* await fsx.mkdir('/a/b/c', { recursive: true })
|
|
536
|
+
* ```
|
|
537
|
+
*/
|
|
538
|
+
async mkdir(path, options) {
|
|
539
|
+
path = this.normalizePath(path);
|
|
540
|
+
await this.backend.mkdir(path, {
|
|
541
|
+
recursive: options?.recursive ?? false,
|
|
542
|
+
mode: options?.mode ?? this.options.defaultDirMode,
|
|
543
|
+
});
|
|
544
|
+
// Emit watch event - 'rename' for new directory
|
|
545
|
+
this.watchManager.emit('rename', path);
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Remove a directory
|
|
549
|
+
*
|
|
550
|
+
* Removes an empty directory. With recursive option, removes directory
|
|
551
|
+
* and all contents (like `rm -r`).
|
|
552
|
+
*
|
|
553
|
+
* @param path - Path to the directory
|
|
554
|
+
* @param options - Removal options
|
|
555
|
+
* @param options.recursive - Remove contents recursively (default: false)
|
|
556
|
+
* @throws {ENOENT} If directory does not exist
|
|
557
|
+
* @throws {ENOTDIR} If path is not a directory
|
|
558
|
+
* @throws {ENOTEMPTY} If directory is not empty and recursive is false
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```typescript
|
|
562
|
+
* // Remove empty directory
|
|
563
|
+
* await fsx.rmdir('/empty-dir')
|
|
564
|
+
*
|
|
565
|
+
* // Remove directory and all contents
|
|
566
|
+
* await fsx.rmdir('/full-dir', { recursive: true })
|
|
567
|
+
* ```
|
|
568
|
+
*/
|
|
569
|
+
async rmdir(path, options) {
|
|
570
|
+
path = this.normalizePath(path);
|
|
571
|
+
await this.backend.rmdir(path, {
|
|
572
|
+
recursive: options?.recursive ?? false,
|
|
573
|
+
});
|
|
574
|
+
// Emit watch event - 'rename' for directory deletion
|
|
575
|
+
this.watchManager.emit('rename', path);
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Remove a file or directory
|
|
579
|
+
*
|
|
580
|
+
* Removes files or directories. With recursive option, removes directories
|
|
581
|
+
* and their contents. With force option, ignores non-existent paths.
|
|
582
|
+
*
|
|
583
|
+
* @param path - Path to remove
|
|
584
|
+
* @param options - Removal options
|
|
585
|
+
* @param options.recursive - Remove directories and contents (default: false)
|
|
586
|
+
* @param options.force - Ignore if path doesn't exist (default: false)
|
|
587
|
+
* @throws {ENOENT} If path doesn't exist and force is false
|
|
588
|
+
* @throws {EISDIR} If path is a directory and recursive is false
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* ```typescript
|
|
592
|
+
* // Remove a file
|
|
593
|
+
* await fsx.rm('/file.txt')
|
|
594
|
+
*
|
|
595
|
+
* // Remove directory tree (like rm -rf)
|
|
596
|
+
* await fsx.rm('/directory', { recursive: true, force: true })
|
|
597
|
+
* ```
|
|
598
|
+
*/
|
|
599
|
+
async rm(path, options) {
|
|
600
|
+
path = this.normalizePath(path);
|
|
601
|
+
// rm is implemented as unlink or rmdir depending on path type
|
|
602
|
+
try {
|
|
603
|
+
const stats = await this.backend.stat(path);
|
|
604
|
+
if (stats.isDirectory()) {
|
|
605
|
+
await this.backend.rmdir(path, { recursive: options?.recursive ?? false });
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
await this.backend.unlink(path);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
// If force is true, ignore ENOENT
|
|
613
|
+
if (options?.force && error.message.includes('ENOENT')) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
throw error;
|
|
617
|
+
}
|
|
618
|
+
// Emit watch event - 'rename' for deletion
|
|
619
|
+
this.watchManager.emit('rename', path);
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Read directory contents
|
|
623
|
+
*
|
|
624
|
+
* Returns the contents of a directory. With withFileTypes option, returns
|
|
625
|
+
* Dirent objects with type information. With recursive option, includes
|
|
626
|
+
* contents of subdirectories.
|
|
627
|
+
*
|
|
628
|
+
* @param path - Path to the directory
|
|
629
|
+
* @param options - Read options
|
|
630
|
+
* @param options.withFileTypes - Return Dirent objects instead of strings (default: false)
|
|
631
|
+
* @param options.recursive - Include subdirectory contents (default: false)
|
|
632
|
+
* @returns Array of filenames or Dirent objects
|
|
633
|
+
* @throws {ENOENT} If directory does not exist
|
|
634
|
+
* @throws {ENOTDIR} If path is not a directory
|
|
635
|
+
*
|
|
636
|
+
* @example
|
|
637
|
+
* ```typescript
|
|
638
|
+
* // List filenames
|
|
639
|
+
* const files = await fsx.readdir('/mydir')
|
|
640
|
+
* // ['file1.txt', 'file2.txt', 'subdir']
|
|
641
|
+
*
|
|
642
|
+
* // List with file types
|
|
643
|
+
* const entries = await fsx.readdir('/mydir', { withFileTypes: true })
|
|
644
|
+
* entries.forEach(e => console.log(e.name, e.isDirectory()))
|
|
645
|
+
* ```
|
|
646
|
+
*/
|
|
647
|
+
async readdir(path, options) {
|
|
648
|
+
path = this.normalizePath(path);
|
|
649
|
+
return this.backend.readdir(path, {
|
|
650
|
+
withFileTypes: options?.withFileTypes ?? false,
|
|
651
|
+
recursive: options?.recursive ?? false,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
// ==================== Metadata Operations ====================
|
|
655
|
+
/**
|
|
656
|
+
* Get file or directory stats
|
|
657
|
+
*
|
|
658
|
+
* Returns metadata about a file or directory including size, permissions,
|
|
659
|
+
* timestamps, and type-checking methods. Follows symbolic links.
|
|
660
|
+
*
|
|
661
|
+
* @param path - Path to the file or directory
|
|
662
|
+
* @returns Stats object with file metadata and type-checking methods
|
|
663
|
+
* @throws {ENOENT} If the path does not exist
|
|
664
|
+
*
|
|
665
|
+
* @example
|
|
666
|
+
* ```typescript
|
|
667
|
+
* const stats = await fsx.stat('/myfile.txt')
|
|
668
|
+
* console.log(stats.size) // File size in bytes
|
|
669
|
+
* console.log(stats.isFile()) // true
|
|
670
|
+
* console.log(stats.mtime) // Last modification time
|
|
671
|
+
* ```
|
|
672
|
+
*/
|
|
673
|
+
async stat(path) {
|
|
674
|
+
path = this.normalizePath(path);
|
|
675
|
+
return this.backend.stat(path);
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Get file or directory stats without following symbolic links
|
|
679
|
+
*
|
|
680
|
+
* Like {@link stat}, but does not follow symbolic links. If the path is
|
|
681
|
+
* a symlink, returns information about the link itself rather than its target.
|
|
682
|
+
*
|
|
683
|
+
* @param path - Path to the file, directory, or symbolic link
|
|
684
|
+
* @returns Stats object with file metadata and type-checking methods
|
|
685
|
+
* @throws {ENOENT} If the path does not exist
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```typescript
|
|
689
|
+
* // Check if something is a symlink
|
|
690
|
+
* const stats = await fsx.lstat('/link')
|
|
691
|
+
* if (stats.isSymbolicLink()) {
|
|
692
|
+
* const target = await fsx.readlink('/link')
|
|
693
|
+
* }
|
|
694
|
+
* ```
|
|
695
|
+
*/
|
|
696
|
+
async lstat(path) {
|
|
697
|
+
path = this.normalizePath(path);
|
|
698
|
+
return this.backend.lstat(path);
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Check file access permissions
|
|
702
|
+
*
|
|
703
|
+
* Tests whether the calling process can access the file at path.
|
|
704
|
+
* Throws an error if access is not permitted.
|
|
705
|
+
*
|
|
706
|
+
* @param path - Path to check
|
|
707
|
+
* @param mode - Access mode to check (default: F_OK for existence)
|
|
708
|
+
* - constants.F_OK: Check existence
|
|
709
|
+
* - constants.R_OK: Check read permission
|
|
710
|
+
* - constants.W_OK: Check write permission
|
|
711
|
+
* - constants.X_OK: Check execute permission
|
|
712
|
+
* @throws {ENOENT} If the path does not exist
|
|
713
|
+
* @throws {EACCES} If access is not permitted
|
|
714
|
+
*
|
|
715
|
+
* @example
|
|
716
|
+
* ```typescript
|
|
717
|
+
* // Check if file exists
|
|
718
|
+
* await fsx.access('/myfile.txt')
|
|
719
|
+
*
|
|
720
|
+
* // Check if file is readable and writable
|
|
721
|
+
* await fsx.access('/myfile.txt', constants.R_OK | constants.W_OK)
|
|
722
|
+
* ```
|
|
723
|
+
*/
|
|
724
|
+
async access(path, _mode) {
|
|
725
|
+
path = this.normalizePath(path);
|
|
726
|
+
// Check existence via stat - full permission checks would need backend support
|
|
727
|
+
await this.backend.stat(path);
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Check if a path exists
|
|
731
|
+
*
|
|
732
|
+
* A convenience method that returns a boolean instead of throwing.
|
|
733
|
+
* Prefer {@link access} when you need to check specific permissions.
|
|
734
|
+
*
|
|
735
|
+
* @param path - Path to check
|
|
736
|
+
* @returns true if the path exists, false otherwise
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```typescript
|
|
740
|
+
* if (await fsx.exists('/config.json')) {
|
|
741
|
+
* const config = await fsx.readFile('/config.json')
|
|
742
|
+
* }
|
|
743
|
+
* ```
|
|
744
|
+
*/
|
|
745
|
+
async exists(path) {
|
|
746
|
+
path = this.normalizePath(path);
|
|
747
|
+
return this.backend.exists(path);
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Change file permissions
|
|
751
|
+
*
|
|
752
|
+
* Changes the permissions of a file or directory.
|
|
753
|
+
*
|
|
754
|
+
* @param path - Path to the file or directory
|
|
755
|
+
* @param mode - New permissions (octal, e.g., 0o755)
|
|
756
|
+
* @throws {ENOENT} If the path does not exist
|
|
757
|
+
*
|
|
758
|
+
* @example
|
|
759
|
+
* ```typescript
|
|
760
|
+
* // Make a script executable
|
|
761
|
+
* await fsx.chmod('/script.sh', 0o755)
|
|
762
|
+
*
|
|
763
|
+
* // Read-only for owner only
|
|
764
|
+
* await fsx.chmod('/secret.txt', 0o400)
|
|
765
|
+
* ```
|
|
766
|
+
*/
|
|
767
|
+
async chmod(path, mode) {
|
|
768
|
+
path = this.normalizePath(path);
|
|
769
|
+
await this.backend.chmod(path, mode);
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Change file ownership
|
|
773
|
+
*
|
|
774
|
+
* Changes the owner and group of a file or directory.
|
|
775
|
+
*
|
|
776
|
+
* @param path - Path to the file or directory
|
|
777
|
+
* @param uid - User ID of the new owner
|
|
778
|
+
* @param gid - Group ID of the new group
|
|
779
|
+
* @throws {ENOENT} If the path does not exist
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```typescript
|
|
783
|
+
* await fsx.chown('/myfile.txt', 1000, 1000)
|
|
784
|
+
* ```
|
|
785
|
+
*/
|
|
786
|
+
async chown(path, uid, gid) {
|
|
787
|
+
path = this.normalizePath(path);
|
|
788
|
+
await this.backend.chown(path, uid, gid);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Update file access and modification timestamps
|
|
792
|
+
*
|
|
793
|
+
* Sets the access time (atime) and modification time (mtime) of a file.
|
|
794
|
+
*
|
|
795
|
+
* @param path - Path to the file
|
|
796
|
+
* @param atime - New access time (Date or Unix timestamp in ms)
|
|
797
|
+
* @param mtime - New modification time (Date or Unix timestamp in ms)
|
|
798
|
+
* @throws {ENOENT} If the file does not exist
|
|
799
|
+
*
|
|
800
|
+
* @example
|
|
801
|
+
* ```typescript
|
|
802
|
+
* // Set timestamps to current time
|
|
803
|
+
* const now = new Date()
|
|
804
|
+
* await fsx.utimes('/myfile.txt', now, now)
|
|
805
|
+
*
|
|
806
|
+
* // Set to specific Unix timestamp
|
|
807
|
+
* await fsx.utimes('/myfile.txt', 1704067200000, 1704067200000)
|
|
808
|
+
* ```
|
|
809
|
+
*/
|
|
810
|
+
async utimes(path, atime, mtime) {
|
|
811
|
+
path = this.normalizePath(path);
|
|
812
|
+
await this.backend.utimes(path, atime, mtime);
|
|
813
|
+
}
|
|
814
|
+
// ==================== Symbolic Links ====================
|
|
815
|
+
/**
|
|
816
|
+
* Create a symbolic link
|
|
817
|
+
*
|
|
818
|
+
* Creates a symbolic link at path pointing to target.
|
|
819
|
+
* The target can be a relative or absolute path.
|
|
820
|
+
*
|
|
821
|
+
* @param target - The path the symlink should point to
|
|
822
|
+
* @param path - Where to create the symlink
|
|
823
|
+
* @throws {EEXIST} If a file already exists at path
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* ```typescript
|
|
827
|
+
* // Create symlink to a file
|
|
828
|
+
* await fsx.symlink('/data/config.json', '/config.json')
|
|
829
|
+
*
|
|
830
|
+
* // Create symlink with relative target
|
|
831
|
+
* await fsx.symlink('../shared/lib', '/app/lib')
|
|
832
|
+
* ```
|
|
833
|
+
*/
|
|
834
|
+
async symlink(target, path) {
|
|
835
|
+
path = this.normalizePath(path);
|
|
836
|
+
await this.backend.symlink(target, path);
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Create a hard link
|
|
840
|
+
*
|
|
841
|
+
* Creates a new directory entry (hard link) at newPath that references
|
|
842
|
+
* the same file as existingPath. Both paths will point to the same
|
|
843
|
+
* underlying file content.
|
|
844
|
+
*
|
|
845
|
+
* @param existingPath - Path to the existing file
|
|
846
|
+
* @param newPath - Path for the new hard link
|
|
847
|
+
* @throws {ENOENT} If existingPath does not exist
|
|
848
|
+
* @throws {EEXIST} If newPath already exists
|
|
849
|
+
*
|
|
850
|
+
* @example
|
|
851
|
+
* ```typescript
|
|
852
|
+
* await fsx.link('/original.txt', '/hardlink.txt')
|
|
853
|
+
* // Both paths now reference the same file
|
|
854
|
+
* ```
|
|
855
|
+
*/
|
|
856
|
+
async link(existingPath, newPath) {
|
|
857
|
+
existingPath = this.normalizePath(existingPath);
|
|
858
|
+
newPath = this.normalizePath(newPath);
|
|
859
|
+
await this.backend.link(existingPath, newPath);
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Read the target of a symbolic link
|
|
863
|
+
*
|
|
864
|
+
* Returns the path that a symbolic link points to.
|
|
865
|
+
*
|
|
866
|
+
* @param path - Path to the symbolic link
|
|
867
|
+
* @returns The target path (may be relative or absolute)
|
|
868
|
+
* @throws {ENOENT} If the path does not exist
|
|
869
|
+
* @throws {EINVAL} If the path is not a symbolic link
|
|
870
|
+
*
|
|
871
|
+
* @example
|
|
872
|
+
* ```typescript
|
|
873
|
+
* // Get symlink target
|
|
874
|
+
* const target = await fsx.readlink('/mylink')
|
|
875
|
+
* console.log(target) // '/actual/file/path'
|
|
876
|
+
* ```
|
|
877
|
+
*/
|
|
878
|
+
async readlink(path) {
|
|
879
|
+
path = this.normalizePath(path);
|
|
880
|
+
return this.backend.readlink(path);
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Resolve the absolute path, following symbolic links
|
|
884
|
+
*
|
|
885
|
+
* Returns the canonical absolute pathname by resolving `.`, `..`,
|
|
886
|
+
* and symbolic links.
|
|
887
|
+
*
|
|
888
|
+
* @param path - Path to resolve
|
|
889
|
+
* @returns The resolved absolute path
|
|
890
|
+
* @throws {ENOENT} If the path does not exist
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* ```typescript
|
|
894
|
+
* // Resolve symlinks and relative components
|
|
895
|
+
* const real = await fsx.realpath('/app/../data/./link')
|
|
896
|
+
* console.log(real) // '/data/actual-file'
|
|
897
|
+
* ```
|
|
898
|
+
*/
|
|
899
|
+
async realpath(path) {
|
|
900
|
+
path = this.normalizePath(path);
|
|
901
|
+
// Realpath resolves symlinks - for now just return the normalized path
|
|
902
|
+
// as full symlink resolution would need backend support
|
|
903
|
+
return path;
|
|
904
|
+
}
|
|
905
|
+
// ==================== Streams ====================
|
|
906
|
+
/**
|
|
907
|
+
* Create a readable stream for a file
|
|
908
|
+
*
|
|
909
|
+
* Returns a ReadableStream that can be used to read file contents
|
|
910
|
+
* in chunks. Useful for large files or when streaming to responses.
|
|
911
|
+
*
|
|
912
|
+
* @param path - Path to the file to read
|
|
913
|
+
* @param options - Stream options (start, end positions, highWaterMark)
|
|
914
|
+
* @returns A ReadableStream of Uint8Array chunks
|
|
915
|
+
* @throws {ENOENT} If the file does not exist
|
|
916
|
+
* @throws {EISDIR} If the path is a directory
|
|
917
|
+
*
|
|
918
|
+
* @example
|
|
919
|
+
* ```typescript
|
|
920
|
+
* const stream = await fsx.createReadStream('/large-file.bin')
|
|
921
|
+
* for await (const chunk of stream) {
|
|
922
|
+
* process.write(chunk)
|
|
923
|
+
* }
|
|
924
|
+
* ```
|
|
925
|
+
*/
|
|
926
|
+
async createReadStream(path, options) {
|
|
927
|
+
path = this.normalizePath(path);
|
|
928
|
+
// Read entire file and wrap in a stream
|
|
929
|
+
const data = await this.backend.readFile(path);
|
|
930
|
+
// Apply start/end options if specified
|
|
931
|
+
let bytes = data;
|
|
932
|
+
if (options?.start !== undefined || options?.end !== undefined) {
|
|
933
|
+
const start = options?.start ?? 0;
|
|
934
|
+
const end = options?.end !== undefined ? options.end + 1 : data.length;
|
|
935
|
+
bytes = data.slice(start, end);
|
|
936
|
+
}
|
|
937
|
+
return new ReadableStream({
|
|
938
|
+
start(controller) {
|
|
939
|
+
controller.enqueue(bytes);
|
|
940
|
+
controller.close();
|
|
941
|
+
},
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Create a writable stream for a file
|
|
946
|
+
*
|
|
947
|
+
* Returns a WritableStream that can be used to write file contents
|
|
948
|
+
* in chunks. The file is created if it doesn't exist.
|
|
949
|
+
*
|
|
950
|
+
* @param path - Path to the file to write
|
|
951
|
+
* @param options - Stream options (flags, mode, start position)
|
|
952
|
+
* @returns A WritableStream accepting Uint8Array chunks
|
|
953
|
+
*
|
|
954
|
+
* @example
|
|
955
|
+
* ```typescript
|
|
956
|
+
* const stream = await fsx.createWriteStream('/output.bin')
|
|
957
|
+
* const writer = stream.getWriter()
|
|
958
|
+
* await writer.write(new Uint8Array([1, 2, 3]))
|
|
959
|
+
* await writer.close()
|
|
960
|
+
* ```
|
|
961
|
+
*/
|
|
962
|
+
async createWriteStream(path, options) {
|
|
963
|
+
const normalizedPath = this.normalizePath(path);
|
|
964
|
+
const backend = this.backend;
|
|
965
|
+
const defaultMode = this.options.defaultMode;
|
|
966
|
+
// Collect chunks and write on close
|
|
967
|
+
const chunks = [];
|
|
968
|
+
return new WritableStream({
|
|
969
|
+
write(chunk) {
|
|
970
|
+
chunks.push(chunk);
|
|
971
|
+
},
|
|
972
|
+
async close() {
|
|
973
|
+
// Concatenate all chunks
|
|
974
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
975
|
+
const data = new Uint8Array(totalLength);
|
|
976
|
+
let offset = 0;
|
|
977
|
+
for (const chunk of chunks) {
|
|
978
|
+
data.set(chunk, offset);
|
|
979
|
+
offset += chunk.length;
|
|
980
|
+
}
|
|
981
|
+
// Write to backend
|
|
982
|
+
await backend.writeFile(normalizedPath, data, {
|
|
983
|
+
mode: options?.mode ?? defaultMode,
|
|
984
|
+
flag: options?.flags,
|
|
985
|
+
});
|
|
986
|
+
},
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
// ==================== File Watching ====================
|
|
990
|
+
/**
|
|
991
|
+
* Watch a file or directory for changes
|
|
992
|
+
*
|
|
993
|
+
* Returns an FSWatcher that emits events when the file or directory changes.
|
|
994
|
+
* Events are emitted synchronously when filesystem operations occur on this
|
|
995
|
+
* FSx instance.
|
|
996
|
+
*
|
|
997
|
+
* Event types:
|
|
998
|
+
* - 'change': Fired when file content is modified
|
|
999
|
+
* - 'rename': Fired when a file/directory is created, deleted, or renamed
|
|
1000
|
+
*
|
|
1001
|
+
* @param path - Path to watch (file or directory)
|
|
1002
|
+
* @param options - Watch options
|
|
1003
|
+
* @param options.recursive - Watch subdirectories recursively (default: false)
|
|
1004
|
+
* @param options.persistent - Keep process alive while watching (default: true)
|
|
1005
|
+
* @param options.encoding - Encoding for filenames (default: 'utf-8')
|
|
1006
|
+
* @param listener - Callback for change events (eventType: 'change'|'rename', filename)
|
|
1007
|
+
* @returns An FSWatcher object with close(), ref(), and unref() methods
|
|
1008
|
+
*
|
|
1009
|
+
* @example
|
|
1010
|
+
* ```typescript
|
|
1011
|
+
* // Watch a directory for changes
|
|
1012
|
+
* const watcher = fsx.watch('/mydir', {}, (event, filename) => {
|
|
1013
|
+
* console.log(`${event}: ${filename}`)
|
|
1014
|
+
* })
|
|
1015
|
+
*
|
|
1016
|
+
* // Watch recursively
|
|
1017
|
+
* const deepWatcher = fsx.watch('/root', { recursive: true }, (event, filename) => {
|
|
1018
|
+
* console.log(`${event}: ${filename}`)
|
|
1019
|
+
* })
|
|
1020
|
+
*
|
|
1021
|
+
* // Later, stop watching
|
|
1022
|
+
* watcher.close()
|
|
1023
|
+
* ```
|
|
1024
|
+
*/
|
|
1025
|
+
watch(path, options, listener) {
|
|
1026
|
+
path = this.normalizePath(path);
|
|
1027
|
+
// Register the watcher with the WatchManager
|
|
1028
|
+
const recursive = options?.recursive ?? false;
|
|
1029
|
+
const watchEntry = listener
|
|
1030
|
+
? this.watchManager.addWatcher(path, recursive, listener)
|
|
1031
|
+
: null;
|
|
1032
|
+
// Create the FSWatcher object
|
|
1033
|
+
const watcher = {
|
|
1034
|
+
close: () => {
|
|
1035
|
+
if (watchEntry) {
|
|
1036
|
+
this.watchManager.removeWatcher(watchEntry);
|
|
1037
|
+
}
|
|
1038
|
+
},
|
|
1039
|
+
ref: () => watcher,
|
|
1040
|
+
unref: () => watcher,
|
|
1041
|
+
};
|
|
1042
|
+
return watcher;
|
|
1043
|
+
}
|
|
1044
|
+
// ==================== Utility ====================
|
|
1045
|
+
/**
|
|
1046
|
+
* Truncate a file to a specified length
|
|
1047
|
+
*
|
|
1048
|
+
* If the file is larger than the specified length, the extra data is discarded.
|
|
1049
|
+
* If smaller, the file is extended with null bytes.
|
|
1050
|
+
*
|
|
1051
|
+
* @param path - Path to the file
|
|
1052
|
+
* @param length - New file length in bytes (default: 0)
|
|
1053
|
+
* @throws {ENOENT} If the file does not exist
|
|
1054
|
+
* @throws {EISDIR} If the path is a directory
|
|
1055
|
+
*
|
|
1056
|
+
* @example
|
|
1057
|
+
* ```typescript
|
|
1058
|
+
* // Clear a file
|
|
1059
|
+
* await fsx.truncate('/myfile.txt')
|
|
1060
|
+
*
|
|
1061
|
+
* // Truncate to first 100 bytes
|
|
1062
|
+
* await fsx.truncate('/myfile.txt', 100)
|
|
1063
|
+
* ```
|
|
1064
|
+
*/
|
|
1065
|
+
async truncate(path, length) {
|
|
1066
|
+
path = this.normalizePath(path);
|
|
1067
|
+
const targetLength = length ?? 0;
|
|
1068
|
+
// Read current content
|
|
1069
|
+
const data = await this.backend.readFile(path);
|
|
1070
|
+
// Truncate or extend
|
|
1071
|
+
let newData;
|
|
1072
|
+
if (data.length > targetLength) {
|
|
1073
|
+
newData = data.slice(0, targetLength);
|
|
1074
|
+
}
|
|
1075
|
+
else if (data.length < targetLength) {
|
|
1076
|
+
newData = new Uint8Array(targetLength);
|
|
1077
|
+
newData.set(data);
|
|
1078
|
+
// Rest is already zero-filled
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
return; // No change needed
|
|
1082
|
+
}
|
|
1083
|
+
await this.backend.writeFile(path, newData);
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Open a file and get a file handle for low-level operations
|
|
1087
|
+
*
|
|
1088
|
+
* Returns a FileHandle that provides fine-grained control over file I/O,
|
|
1089
|
+
* including positioned reads/writes and file synchronization.
|
|
1090
|
+
*
|
|
1091
|
+
* @param path - Path to the file
|
|
1092
|
+
* @param flags - Open mode: 'r' (read), 'w' (write), 'a' (append), etc.
|
|
1093
|
+
* @param mode - File permissions for newly created files (default: 0o644)
|
|
1094
|
+
* @returns A FileHandle for low-level file operations
|
|
1095
|
+
* @throws {ENOENT} If file doesn't exist and flags don't include create
|
|
1096
|
+
*
|
|
1097
|
+
* @example
|
|
1098
|
+
* ```typescript
|
|
1099
|
+
* const handle = await fsx.open('/data.bin', 'r+')
|
|
1100
|
+
* try {
|
|
1101
|
+
* const buffer = new Uint8Array(1024)
|
|
1102
|
+
* const { bytesRead } = await handle.read(buffer, 0, 1024, 0)
|
|
1103
|
+
* await handle.write(new Uint8Array([1, 2, 3]), 0)
|
|
1104
|
+
* await handle.sync()
|
|
1105
|
+
* } finally {
|
|
1106
|
+
* await handle.close()
|
|
1107
|
+
* }
|
|
1108
|
+
* ```
|
|
1109
|
+
*/
|
|
1110
|
+
async open(path, flags, _mode) {
|
|
1111
|
+
path = this.normalizePath(path);
|
|
1112
|
+
return this.createFileHandle(path, flags);
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Create a FileHandle object for low-level file operations
|
|
1116
|
+
*
|
|
1117
|
+
* @param path - The file path
|
|
1118
|
+
* @param flags - Open flags
|
|
1119
|
+
* @returns A FileHandle with read, write, stat, sync, and close methods
|
|
1120
|
+
*/
|
|
1121
|
+
createFileHandle(path, _flags) {
|
|
1122
|
+
const backend = this.backend;
|
|
1123
|
+
let fileData = null;
|
|
1124
|
+
let modified = false;
|
|
1125
|
+
return {
|
|
1126
|
+
fd: 0, // Placeholder - we use path-based operations
|
|
1127
|
+
read: async (buffer, offset, length, position) => {
|
|
1128
|
+
if (!fileData) {
|
|
1129
|
+
fileData = await backend.readFile(path);
|
|
1130
|
+
}
|
|
1131
|
+
const readLength = length ?? buffer.length;
|
|
1132
|
+
const readPosition = position ?? 0;
|
|
1133
|
+
const targetOffset = offset ?? 0;
|
|
1134
|
+
const bytesToRead = Math.min(readLength, fileData.length - readPosition);
|
|
1135
|
+
for (let i = 0; i < bytesToRead; i++) {
|
|
1136
|
+
buffer[targetOffset + i] = fileData[readPosition + i];
|
|
1137
|
+
}
|
|
1138
|
+
return { bytesRead: bytesToRead, buffer };
|
|
1139
|
+
},
|
|
1140
|
+
write: async (data, _position) => {
|
|
1141
|
+
const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;
|
|
1142
|
+
fileData = bytes;
|
|
1143
|
+
modified = true;
|
|
1144
|
+
return { bytesWritten: bytes.length };
|
|
1145
|
+
},
|
|
1146
|
+
stat: () => backend.stat(path),
|
|
1147
|
+
truncate: async (length) => {
|
|
1148
|
+
if (!fileData) {
|
|
1149
|
+
fileData = await backend.readFile(path);
|
|
1150
|
+
}
|
|
1151
|
+
const targetLength = length ?? 0;
|
|
1152
|
+
if (fileData.length > targetLength) {
|
|
1153
|
+
fileData = fileData.slice(0, targetLength);
|
|
1154
|
+
}
|
|
1155
|
+
else if (fileData.length < targetLength) {
|
|
1156
|
+
const newData = new Uint8Array(targetLength);
|
|
1157
|
+
newData.set(fileData);
|
|
1158
|
+
fileData = newData;
|
|
1159
|
+
}
|
|
1160
|
+
modified = true;
|
|
1161
|
+
},
|
|
1162
|
+
sync: async () => {
|
|
1163
|
+
if (modified && fileData) {
|
|
1164
|
+
await backend.writeFile(path, fileData);
|
|
1165
|
+
modified = false;
|
|
1166
|
+
}
|
|
1167
|
+
},
|
|
1168
|
+
close: async () => {
|
|
1169
|
+
if (modified && fileData) {
|
|
1170
|
+
await backend.writeFile(path, fileData);
|
|
1171
|
+
}
|
|
1172
|
+
fileData = null;
|
|
1173
|
+
modified = false;
|
|
1174
|
+
},
|
|
1175
|
+
createReadStream: (_options) => {
|
|
1176
|
+
throw new Error('FileHandle.createReadStream is not implemented');
|
|
1177
|
+
},
|
|
1178
|
+
createWriteStream: (_options) => {
|
|
1179
|
+
throw new Error('FileHandle.createWriteStream is not implemented');
|
|
1180
|
+
},
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
//# sourceMappingURL=fsx.js.map
|