workerstack 0.1.0-beta.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/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +138 -0
- package/dist/access.d.ts +131 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +333 -0
- package/dist/access.js.map +1 -0
- package/dist/api/builder.d.ts +457 -0
- package/dist/api/builder.d.ts.map +1 -0
- package/dist/api/builder.js +45 -0
- package/dist/api/builder.js.map +1 -0
- package/dist/api/catalog.d.ts +8 -0
- package/dist/api/catalog.d.ts.map +1 -0
- package/dist/api/catalog.js +56 -0
- package/dist/api/catalog.js.map +1 -0
- package/dist/api/context.d.ts +47 -0
- package/dist/api/context.d.ts.map +1 -0
- package/dist/api/context.js +40 -0
- package/dist/api/context.js.map +1 -0
- package/dist/api/crud-router.d.ts +476 -0
- package/dist/api/crud-router.d.ts.map +1 -0
- package/dist/api/crud-router.js +354 -0
- package/dist/api/crud-router.js.map +1 -0
- package/dist/api/list-input-schema.d.ts +37 -0
- package/dist/api/list-input-schema.d.ts.map +1 -0
- package/dist/api/list-input-schema.js +63 -0
- package/dist/api/list-input-schema.js.map +1 -0
- package/dist/api/list-spec.d.ts +44 -0
- package/dist/api/list-spec.d.ts.map +1 -0
- package/dist/api/list-spec.js +49 -0
- package/dist/api/list-spec.js.map +1 -0
- package/dist/api/live-view.d.ts +14 -0
- package/dist/api/live-view.d.ts.map +1 -0
- package/dist/api/live-view.js +42 -0
- package/dist/api/live-view.js.map +1 -0
- package/dist/api/live-window.d.ts +40 -0
- package/dist/api/live-window.d.ts.map +1 -0
- package/dist/api/live-window.js +93 -0
- package/dist/api/live-window.js.map +1 -0
- package/dist/api/openapi.d.ts +6 -0
- package/dist/api/openapi.d.ts.map +1 -0
- package/dist/api/openapi.js +146 -0
- package/dist/api/openapi.js.map +1 -0
- package/dist/api/realtime-router.d.ts +94 -0
- package/dist/api/realtime-router.d.ts.map +1 -0
- package/dist/api/realtime-router.js +45 -0
- package/dist/api/realtime-router.js.map +1 -0
- package/dist/api/registry.d.ts +30 -0
- package/dist/api/registry.d.ts.map +1 -0
- package/dist/api/registry.js +236 -0
- package/dist/api/registry.js.map +1 -0
- package/dist/api/router.d.ts +15 -0
- package/dist/api/router.d.ts.map +1 -0
- package/dist/api/router.js +32 -0
- package/dist/api/router.js.map +1 -0
- package/dist/api/storage-router.d.ts +349 -0
- package/dist/api/storage-router.d.ts.map +1 -0
- package/dist/api/storage-router.js +155 -0
- package/dist/api/storage-router.js.map +1 -0
- package/dist/api/types.d.ts +55 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/auth-password.d.ts +27 -0
- package/dist/auth-password.d.ts.map +1 -0
- package/dist/auth-password.js +60 -0
- package/dist/auth-password.js.map +1 -0
- package/dist/auth.d.ts +59 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +146 -0
- package/dist/auth.js.map +1 -0
- package/dist/backend-internals.d.ts +35 -0
- package/dist/backend-internals.d.ts.map +1 -0
- package/dist/backend-internals.js +2 -0
- package/dist/backend-internals.js.map +1 -0
- package/dist/backend.d.ts +63 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +59 -0
- package/dist/backend.js.map +1 -0
- package/dist/blueprint-generator.d.ts +21 -0
- package/dist/blueprint-generator.d.ts.map +1 -0
- package/dist/blueprint-generator.js +199 -0
- package/dist/blueprint-generator.js.map +1 -0
- package/dist/blueprint.d.ts +99 -0
- package/dist/blueprint.d.ts.map +1 -0
- package/dist/blueprint.js +257 -0
- package/dist/blueprint.js.map +1 -0
- package/dist/cli-skills.d.ts +16 -0
- package/dist/cli-skills.d.ts.map +1 -0
- package/dist/cli-skills.js +108 -0
- package/dist/cli-skills.js.map +1 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +205 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/client.d.ts +53 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +111 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +4 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/live-view.d.ts +49 -0
- package/dist/client/live-view.d.ts.map +1 -0
- package/dist/client/live-view.js +184 -0
- package/dist/client/live-view.js.map +1 -0
- package/dist/client/react.d.ts +4 -0
- package/dist/client/react.d.ts.map +1 -0
- package/dist/client/react.js +6 -0
- package/dist/client/react.js.map +1 -0
- package/dist/client/realtime.d.ts +54 -0
- package/dist/client/realtime.d.ts.map +1 -0
- package/dist/client/realtime.js +105 -0
- package/dist/client/realtime.js.map +1 -0
- package/dist/client/rest.d.ts +2 -0
- package/dist/client/rest.d.ts.map +1 -0
- package/dist/client/rest.js +2 -0
- package/dist/client/rest.js.map +1 -0
- package/dist/client/rpc-client.d.ts +38 -0
- package/dist/client/rpc-client.d.ts.map +1 -0
- package/dist/client/rpc-client.js +59 -0
- package/dist/client/rpc-client.js.map +1 -0
- package/dist/client/solid.d.ts +7 -0
- package/dist/client/solid.d.ts.map +1 -0
- package/dist/client/solid.js +21 -0
- package/dist/client/solid.js.map +1 -0
- package/dist/client/svelte.d.ts +6 -0
- package/dist/client/svelte.d.ts.map +1 -0
- package/dist/client/svelte.js +9 -0
- package/dist/client/svelte.js.map +1 -0
- package/dist/client/vue.d.ts +5 -0
- package/dist/client/vue.d.ts.map +1 -0
- package/dist/client/vue.js +11 -0
- package/dist/client/vue.js.map +1 -0
- package/dist/codegen.d.ts +36 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +235 -0
- package/dist/codegen.js.map +1 -0
- package/dist/config.d.ts +129 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +75 -0
- package/dist/config.js.map +1 -0
- package/dist/cron.d.ts +4 -0
- package/dist/cron.d.ts.map +1 -0
- package/dist/cron.js +3 -0
- package/dist/cron.js.map +1 -0
- package/dist/crud-operations.d.ts +43 -0
- package/dist/crud-operations.d.ts.map +1 -0
- package/dist/crud-operations.js +249 -0
- package/dist/crud-operations.js.map +1 -0
- package/dist/database/adapter.d.ts +26 -0
- package/dist/database/adapter.d.ts.map +1 -0
- package/dist/database/adapter.js +2 -0
- package/dist/database/adapter.js.map +1 -0
- package/dist/database/libsql.d.ts +3 -0
- package/dist/database/libsql.d.ts.map +1 -0
- package/dist/database/libsql.js +39 -0
- package/dist/database/libsql.js.map +1 -0
- package/dist/db.d.ts +15 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +8 -0
- package/dist/db.js.map +1 -0
- package/dist/dev/binaries.d.ts +22 -0
- package/dist/dev/binaries.d.ts.map +1 -0
- package/dist/dev/binaries.js +119 -0
- package/dist/dev/binaries.js.map +1 -0
- package/dist/dev/env.d.ts +11 -0
- package/dist/dev/env.d.ts.map +1 -0
- package/dist/dev/env.js +71 -0
- package/dist/dev/env.js.map +1 -0
- package/dist/dev/index.d.ts +39 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +273 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/processes.d.ts +37 -0
- package/dist/dev/processes.d.ts.map +1 -0
- package/dist/dev/processes.js +104 -0
- package/dist/dev/processes.js.map +1 -0
- package/dist/dev/push.d.ts +2 -0
- package/dist/dev/push.d.ts.map +1 -0
- package/dist/dev/push.js +30 -0
- package/dist/dev/push.js.map +1 -0
- package/dist/dialect.d.ts +17 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialect.js +12 -0
- package/dist/dialect.js.map +1 -0
- package/dist/email/smtp.d.ts +20 -0
- package/dist/email/smtp.d.ts.map +1 -0
- package/dist/email/smtp.js +31 -0
- package/dist/email/smtp.js.map +1 -0
- package/dist/email.d.ts +26 -0
- package/dist/email.d.ts.map +1 -0
- package/dist/email.js +2 -0
- package/dist/email.js.map +1 -0
- package/dist/env-probe.d.ts +9 -0
- package/dist/env-probe.d.ts.map +1 -0
- package/dist/env-probe.js +72 -0
- package/dist/env-probe.js.map +1 -0
- package/dist/env.d.ts +62 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +86 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +153 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +106 -0
- package/dist/errors.js.map +1 -0
- package/dist/handler.d.ts +11 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +21 -0
- package/dist/handler.js.map +1 -0
- package/dist/hosted-contract.d.ts +11 -0
- package/dist/hosted-contract.d.ts.map +1 -0
- package/dist/hosted-contract.js +50 -0
- package/dist/hosted-contract.js.map +1 -0
- package/dist/idempotency.d.ts +17 -0
- package/dist/idempotency.d.ts.map +1 -0
- package/dist/idempotency.js +65 -0
- package/dist/idempotency.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect.d.ts +15 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +44 -0
- package/dist/inspect.js.map +1 -0
- package/dist/internal-tables.d.ts +1767 -0
- package/dist/internal-tables.d.ts.map +1 -0
- package/dist/internal-tables.js +114 -0
- package/dist/internal-tables.js.map +1 -0
- package/dist/jobs/cron.d.ts +15 -0
- package/dist/jobs/cron.d.ts.map +1 -0
- package/dist/jobs/cron.js +74 -0
- package/dist/jobs/cron.js.map +1 -0
- package/dist/jobs/define.d.ts +186 -0
- package/dist/jobs/define.d.ts.map +1 -0
- package/dist/jobs/define.js +99 -0
- package/dist/jobs/define.js.map +1 -0
- package/dist/jobs/index.d.ts +9 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +7 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/queue.d.ts +13 -0
- package/dist/jobs/queue.d.ts.map +1 -0
- package/dist/jobs/queue.js +71 -0
- package/dist/jobs/queue.js.map +1 -0
- package/dist/jobs/slots.d.ts +30 -0
- package/dist/jobs/slots.d.ts.map +1 -0
- package/dist/jobs/slots.js +53 -0
- package/dist/jobs/slots.js.map +1 -0
- package/dist/jobs/worker.d.ts +45 -0
- package/dist/jobs/worker.d.ts.map +1 -0
- package/dist/jobs/worker.js +605 -0
- package/dist/jobs/worker.js.map +1 -0
- package/dist/lifecycle.d.ts +10 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +37 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/list-query.d.ts +62 -0
- package/dist/list-query.d.ts.map +1 -0
- package/dist/list-query.js +190 -0
- package/dist/list-query.js.map +1 -0
- package/dist/live/apply.d.ts +13 -0
- package/dist/live/apply.d.ts.map +1 -0
- package/dist/live/apply.js +32 -0
- package/dist/live/apply.js.map +1 -0
- package/dist/live/index.d.ts +48 -0
- package/dist/live/index.d.ts.map +1 -0
- package/dist/live/index.js +108 -0
- package/dist/live/index.js.map +1 -0
- package/dist/live/protocol.d.ts +47 -0
- package/dist/live/protocol.d.ts.map +1 -0
- package/dist/live/protocol.js +12 -0
- package/dist/live/protocol.js.map +1 -0
- package/dist/live/sse.d.ts +22 -0
- package/dist/live/sse.d.ts.map +1 -0
- package/dist/live/sse.js +41 -0
- package/dist/live/sse.js.map +1 -0
- package/dist/logging.d.ts +8 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +6 -0
- package/dist/logging.js.map +1 -0
- package/dist/manifest-diff.d.ts +6 -0
- package/dist/manifest-diff.d.ts.map +1 -0
- package/dist/manifest-diff.js +42 -0
- package/dist/manifest-diff.js.map +1 -0
- package/dist/manifest.d.ts +87 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +237 -0
- package/dist/manifest.js.map +1 -0
- package/dist/messaging/email.d.ts +16 -0
- package/dist/messaging/email.d.ts.map +1 -0
- package/dist/messaging/email.js +8 -0
- package/dist/messaging/email.js.map +1 -0
- package/dist/messaging/index.d.ts +8 -0
- package/dist/messaging/index.d.ts.map +1 -0
- package/dist/messaging/index.js +4 -0
- package/dist/messaging/index.js.map +1 -0
- package/dist/messaging/journal.d.ts +4 -0
- package/dist/messaging/journal.d.ts.map +1 -0
- package/dist/messaging/journal.js +19 -0
- package/dist/messaging/journal.js.map +1 -0
- package/dist/messaging/runtime.d.ts +16 -0
- package/dist/messaging/runtime.d.ts.map +1 -0
- package/dist/messaging/runtime.js +213 -0
- package/dist/messaging/runtime.js.map +1 -0
- package/dist/messaging/standalone.d.ts +27 -0
- package/dist/messaging/standalone.d.ts.map +1 -0
- package/dist/messaging/standalone.js +18 -0
- package/dist/messaging/standalone.js.map +1 -0
- package/dist/messaging/telegram.d.ts +16 -0
- package/dist/messaging/telegram.d.ts.map +1 -0
- package/dist/messaging/telegram.js +5 -0
- package/dist/messaging/telegram.js.map +1 -0
- package/dist/messaging/types.d.ts +22 -0
- package/dist/messaging/types.d.ts.map +1 -0
- package/dist/messaging/types.js +17 -0
- package/dist/messaging/types.js.map +1 -0
- package/dist/platform.d.ts +25 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +24 -0
- package/dist/platform.js.map +1 -0
- package/dist/provision-internals.d.ts +23 -0
- package/dist/provision-internals.d.ts.map +1 -0
- package/dist/provision-internals.js +7 -0
- package/dist/provision-internals.js.map +1 -0
- package/dist/provision-runtime.d.ts +6 -0
- package/dist/provision-runtime.d.ts.map +1 -0
- package/dist/provision-runtime.js +41 -0
- package/dist/provision-runtime.js.map +1 -0
- package/dist/provision-schema.d.ts +16 -0
- package/dist/provision-schema.d.ts.map +1 -0
- package/dist/provision-schema.js +59 -0
- package/dist/provision-schema.js.map +1 -0
- package/dist/provision.d.ts +8 -0
- package/dist/provision.d.ts.map +1 -0
- package/dist/provision.js +16 -0
- package/dist/provision.js.map +1 -0
- package/dist/query/api.d.ts +10 -0
- package/dist/query/api.d.ts.map +1 -0
- package/dist/query/api.js +7 -0
- package/dist/query/api.js.map +1 -0
- package/dist/query/client.d.ts +28 -0
- package/dist/query/client.d.ts.map +1 -0
- package/dist/query/client.js +87 -0
- package/dist/query/client.js.map +1 -0
- package/dist/query/fetch.d.ts +3 -0
- package/dist/query/fetch.d.ts.map +1 -0
- package/dist/query/fetch.js +4 -0
- package/dist/query/fetch.js.map +1 -0
- package/dist/query/index.d.ts +8 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +4 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/infer.d.ts +25 -0
- package/dist/query/infer.d.ts.map +1 -0
- package/dist/query/infer.js +2 -0
- package/dist/query/infer.js.map +1 -0
- package/dist/query/react.d.ts +3 -0
- package/dist/query/react.d.ts.map +1 -0
- package/dist/query/react.js +2 -0
- package/dist/query/react.js.map +1 -0
- package/dist/query/realtime-flush.d.ts +26 -0
- package/dist/query/realtime-flush.d.ts.map +1 -0
- package/dist/query/realtime-flush.js +45 -0
- package/dist/query/realtime-flush.js.map +1 -0
- package/dist/query/realtime-stream.d.ts +3 -0
- package/dist/query/realtime-stream.d.ts.map +1 -0
- package/dist/query/realtime-stream.js +3 -0
- package/dist/query/realtime-stream.js.map +1 -0
- package/dist/query/realtime.d.ts +88 -0
- package/dist/query/realtime.d.ts.map +1 -0
- package/dist/query/realtime.js +255 -0
- package/dist/query/realtime.js.map +1 -0
- package/dist/rate-limit.d.ts +8 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +46 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/readiness-probes.d.ts +4 -0
- package/dist/readiness-probes.d.ts.map +1 -0
- package/dist/readiness-probes.js +23 -0
- package/dist/readiness-probes.js.map +1 -0
- package/dist/readiness.d.ts +39 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +72 -0
- package/dist/readiness.js.map +1 -0
- package/dist/realtime/facade.d.ts +20 -0
- package/dist/realtime/facade.d.ts.map +1 -0
- package/dist/realtime/facade.js +37 -0
- package/dist/realtime/facade.js.map +1 -0
- package/dist/realtime/filter.d.ts +42 -0
- package/dist/realtime/filter.d.ts.map +1 -0
- package/dist/realtime/filter.js +86 -0
- package/dist/realtime/filter.js.map +1 -0
- package/dist/realtime/heartbeat.d.ts +20 -0
- package/dist/realtime/heartbeat.d.ts.map +1 -0
- package/dist/realtime/heartbeat.js +64 -0
- package/dist/realtime/heartbeat.js.map +1 -0
- package/dist/realtime/publisher.d.ts +19 -0
- package/dist/realtime/publisher.d.ts.map +1 -0
- package/dist/realtime/publisher.js +8 -0
- package/dist/realtime/publisher.js.map +1 -0
- package/dist/runtime.d.ts +141 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +463 -0
- package/dist/runtime.js.map +1 -0
- package/dist/schema-export.d.ts +2 -0
- package/dist/schema-export.d.ts.map +1 -0
- package/dist/schema-export.js +2 -0
- package/dist/schema-export.js.map +1 -0
- package/dist/scope.d.ts +4 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +13 -0
- package/dist/scope.js.map +1 -0
- package/dist/standard-schema.d.ts +13 -0
- package/dist/standard-schema.d.ts.map +1 -0
- package/dist/standard-schema.js +34 -0
- package/dist/standard-schema.js.map +1 -0
- package/dist/start/auth-client.d.ts +11 -0
- package/dist/start/auth-client.d.ts.map +1 -0
- package/dist/start/auth-client.js +15 -0
- package/dist/start/auth-client.js.map +1 -0
- package/dist/start/create-start-worker.d.ts +16 -0
- package/dist/start/create-start-worker.d.ts.map +1 -0
- package/dist/start/create-start-worker.js +24 -0
- package/dist/start/create-start-worker.js.map +1 -0
- package/dist/start/index.d.ts +66 -0
- package/dist/start/index.d.ts.map +1 -0
- package/dist/start/index.js +49 -0
- package/dist/start/index.js.map +1 -0
- package/dist/start/isomorphic-fetch.d.ts +22 -0
- package/dist/start/isomorphic-fetch.d.ts.map +1 -0
- package/dist/start/isomorphic-fetch.js +49 -0
- package/dist/start/isomorphic-fetch.js.map +1 -0
- package/dist/start/server-entry.d.ts +7 -0
- package/dist/start/server-entry.d.ts.map +1 -0
- package/dist/start/server-entry.js +8 -0
- package/dist/start/server-entry.js.map +1 -0
- package/dist/storage/background.d.ts +3 -0
- package/dist/storage/background.d.ts.map +1 -0
- package/dist/storage/background.js +3 -0
- package/dist/storage/background.js.map +1 -0
- package/dist/storage/buckets.d.ts +78 -0
- package/dist/storage/buckets.d.ts.map +1 -0
- package/dist/storage/buckets.js +177 -0
- package/dist/storage/buckets.js.map +1 -0
- package/dist/storage/delete.d.ts +8 -0
- package/dist/storage/delete.d.ts.map +1 -0
- package/dist/storage/delete.js +10 -0
- package/dist/storage/delete.js.map +1 -0
- package/dist/storage/file-meta.d.ts +37 -0
- package/dist/storage/file-meta.d.ts.map +1 -0
- package/dist/storage/file-meta.js +166 -0
- package/dist/storage/file-meta.js.map +1 -0
- package/dist/storage/index.d.ts +28 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +5 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local.d.ts +15 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +56 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/storage/operations.d.ts +54 -0
- package/dist/storage/operations.d.ts.map +1 -0
- package/dist/storage/operations.js +212 -0
- package/dist/storage/operations.js.map +1 -0
- package/dist/storage/registry.d.ts +11 -0
- package/dist/storage/registry.d.ts.map +1 -0
- package/dist/storage/registry.js +23 -0
- package/dist/storage/registry.js.map +1 -0
- package/dist/storage/s3.d.ts +35 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +125 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/storage/sigv4.d.ts +24 -0
- package/dist/storage/sigv4.d.ts.map +1 -0
- package/dist/storage/sigv4.js +107 -0
- package/dist/storage/sigv4.js.map +1 -0
- package/dist/storage/sweep.d.ts +9 -0
- package/dist/storage/sweep.d.ts.map +1 -0
- package/dist/storage/sweep.js +17 -0
- package/dist/storage/sweep.js.map +1 -0
- package/dist/sync/collection.d.ts +106 -0
- package/dist/sync/collection.d.ts.map +1 -0
- package/dist/sync/collection.js +258 -0
- package/dist/sync/collection.js.map +1 -0
- package/dist/sync/index.d.ts +8 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +4 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/realtime-sync.d.ts +31 -0
- package/dist/sync/realtime-sync.d.ts.map +1 -0
- package/dist/sync/realtime-sync.js +46 -0
- package/dist/sync/realtime-sync.js.map +1 -0
- package/dist/sync/sync-client.d.ts +28 -0
- package/dist/sync/sync-client.d.ts.map +1 -0
- package/dist/sync/sync-client.js +52 -0
- package/dist/sync/sync-client.js.map +1 -0
- package/dist/sync/update-queue.d.ts +27 -0
- package/dist/sync/update-queue.d.ts.map +1 -0
- package/dist/sync/update-queue.js +66 -0
- package/dist/sync/update-queue.js.map +1 -0
- package/dist/testing/auth.d.ts +85 -0
- package/dist/testing/auth.d.ts.map +1 -0
- package/dist/testing/auth.js +140 -0
- package/dist/testing/auth.js.map +1 -0
- package/dist/testing/client.d.ts +6 -0
- package/dist/testing/client.d.ts.map +1 -0
- package/dist/testing/client.js +9 -0
- package/dist/testing/client.js.map +1 -0
- package/dist/testing/database.d.ts +6 -0
- package/dist/testing/database.d.ts.map +1 -0
- package/dist/testing/database.js +8 -0
- package/dist/testing/database.js.map +1 -0
- package/dist/testing/email.d.ts +15 -0
- package/dist/testing/email.d.ts.map +1 -0
- package/dist/testing/email.js +32 -0
- package/dist/testing/email.js.map +1 -0
- package/dist/testing/fixture.d.ts +51 -0
- package/dist/testing/fixture.d.ts.map +1 -0
- package/dist/testing/fixture.js +177 -0
- package/dist/testing/fixture.js.map +1 -0
- package/dist/testing/jobs.d.ts +40 -0
- package/dist/testing/jobs.d.ts.map +1 -0
- package/dist/testing/jobs.js +76 -0
- package/dist/testing/jobs.js.map +1 -0
- package/dist/testing/logs.d.ts +18 -0
- package/dist/testing/logs.d.ts.map +1 -0
- package/dist/testing/logs.js +53 -0
- package/dist/testing/logs.js.map +1 -0
- package/dist/testing/messaging.d.ts +21 -0
- package/dist/testing/messaging.d.ts.map +1 -0
- package/dist/testing/messaging.js +34 -0
- package/dist/testing/messaging.js.map +1 -0
- package/dist/testing/storage.d.ts +7 -0
- package/dist/testing/storage.d.ts.map +1 -0
- package/dist/testing/storage.js +29 -0
- package/dist/testing/storage.js.map +1 -0
- package/dist/testing/workers-fakes.d.ts +17 -0
- package/dist/testing/workers-fakes.d.ts.map +1 -0
- package/dist/testing/workers-fakes.js +98 -0
- package/dist/testing/workers-fakes.js.map +1 -0
- package/dist/testing.d.ts +12 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +5 -0
- package/dist/testing.js.map +1 -0
- package/dist/typeid.d.ts +46 -0
- package/dist/typeid.d.ts.map +1 -0
- package/dist/typeid.js +130 -0
- package/dist/typeid.js.map +1 -0
- package/dist/vite.d.ts +26 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +100 -0
- package/dist/vite.js.map +1 -0
- package/dist/worker-plan.d.ts +37 -0
- package/dist/worker-plan.d.ts.map +1 -0
- package/dist/worker-plan.js +63 -0
- package/dist/worker-plan.js.map +1 -0
- package/dist/workers/app.d.ts +19 -0
- package/dist/workers/app.d.ts.map +1 -0
- package/dist/workers/app.js +56 -0
- package/dist/workers/app.js.map +1 -0
- package/dist/workers/entry.d.ts +7 -0
- package/dist/workers/entry.d.ts.map +1 -0
- package/dist/workers/entry.js +8 -0
- package/dist/workers/entry.js.map +1 -0
- package/dist/workers/index.d.ts +23 -0
- package/dist/workers/index.d.ts.map +1 -0
- package/dist/workers/index.js +39 -0
- package/dist/workers/index.js.map +1 -0
- package/dist/workers/r2.d.ts +24 -0
- package/dist/workers/r2.d.ts.map +1 -0
- package/dist/workers/r2.js +76 -0
- package/dist/workers/r2.js.map +1 -0
- package/dist/workers/rate-limiter.d.ts +9 -0
- package/dist/workers/rate-limiter.d.ts.map +1 -0
- package/dist/workers/rate-limiter.js +22 -0
- package/dist/workers/rate-limiter.js.map +1 -0
- package/dist/workers/realtime-hub.d.ts +20 -0
- package/dist/workers/realtime-hub.d.ts.map +1 -0
- package/dist/workers/realtime-hub.js +117 -0
- package/dist/workers/realtime-hub.js.map +1 -0
- package/dist/workers/registry.d.ts +13 -0
- package/dist/workers/registry.d.ts.map +1 -0
- package/dist/workers/registry.js +19 -0
- package/dist/workers/registry.js.map +1 -0
- package/dist/workers/scheduler.d.ts +13 -0
- package/dist/workers/scheduler.d.ts.map +1 -0
- package/dist/workers/scheduler.js +71 -0
- package/dist/workers/scheduler.js.map +1 -0
- package/dist/workers/types.d.ts +55 -0
- package/dist/workers/types.d.ts.map +1 -0
- package/dist/workers/types.js +4 -0
- package/dist/workers/types.js.map +1 -0
- package/dist/workers/wrangler.d.ts +62 -0
- package/dist/workers/wrangler.d.ts.map +1 -0
- package/dist/workers/wrangler.js +90 -0
- package/dist/workers/wrangler.js.map +1 -0
- package/package.json +286 -0
- package/skills/creating-workerstack-apps/SKILL.md +45 -0
- package/skills/creating-workerstack-apps/agents/openai.yaml +4 -0
- package/skills/creating-workerstack-apps/references/application-structure.md +219 -0
- package/skills/creating-workerstack-apps/references/runtime-integrations.md +96 -0
- package/skills/creating-workerstack-apps/references/verification.md +29 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const encoder = new TextEncoder();
|
|
2
|
+
function hex(bytes) {
|
|
3
|
+
return Array.from(new Uint8Array(bytes), (b) => b.toString(16).padStart(2, '0')).join('');
|
|
4
|
+
}
|
|
5
|
+
async function sha256Hex(data) {
|
|
6
|
+
const bytes = typeof data === 'string' ? encoder.encode(data) : data;
|
|
7
|
+
return hex(await crypto.subtle.digest('SHA-256', bytes));
|
|
8
|
+
}
|
|
9
|
+
async function hmac(key, data) {
|
|
10
|
+
const cryptoKey = await crypto.subtle.importKey('raw', key, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
11
|
+
return crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(data));
|
|
12
|
+
}
|
|
13
|
+
async function signingKey(secret, day, region, service) {
|
|
14
|
+
const kDate = await hmac(encoder.encode(`AWS4${secret}`), day);
|
|
15
|
+
const kRegion = await hmac(kDate, region);
|
|
16
|
+
const kService = await hmac(kRegion, service);
|
|
17
|
+
return hmac(kService, 'aws4_request');
|
|
18
|
+
}
|
|
19
|
+
/** RFC 3986 encoding, as SigV4 requires: only A-Z a-z 0-9 - _ . ~ stay. */
|
|
20
|
+
function uriEncode(value) {
|
|
21
|
+
return encodeURIComponent(value).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
22
|
+
}
|
|
23
|
+
/** S3 does not normalize paths: encode each segment once, keep the slashes. */
|
|
24
|
+
function canonicalPath(pathname) {
|
|
25
|
+
return pathname
|
|
26
|
+
.split('/')
|
|
27
|
+
.map((segment) => uriEncode(decodeURIComponent(segment)))
|
|
28
|
+
.join('/');
|
|
29
|
+
}
|
|
30
|
+
function canonicalQuery(params) {
|
|
31
|
+
return [...params.entries()]
|
|
32
|
+
.map(([k, v]) => [uriEncode(k), uriEncode(v)])
|
|
33
|
+
.sort(([a, av], [b, bv]) => a < b ? -1 : a > b ? 1 : av < bv ? -1 : av > bv ? 1 : 0)
|
|
34
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
35
|
+
.join('&');
|
|
36
|
+
}
|
|
37
|
+
function amzDates(date) {
|
|
38
|
+
const amzDate = date.toISOString().replace(/[:-]|\.\d{3}/g, '');
|
|
39
|
+
return { amzDate, day: amzDate.slice(0, 8) };
|
|
40
|
+
}
|
|
41
|
+
async function signature(credentials, day, amzDate, canonicalRequest) {
|
|
42
|
+
const service = credentials.service ?? 's3';
|
|
43
|
+
const scope = `${day}/${credentials.region}/${service}/aws4_request`;
|
|
44
|
+
const stringToSign = [
|
|
45
|
+
'AWS4-HMAC-SHA256',
|
|
46
|
+
amzDate,
|
|
47
|
+
scope,
|
|
48
|
+
await sha256Hex(canonicalRequest),
|
|
49
|
+
].join('\n');
|
|
50
|
+
const key = await signingKey(credentials.secretAccessKey, day, credentials.region, service);
|
|
51
|
+
return { scope, signature: hex(await hmac(key, stringToSign)) };
|
|
52
|
+
}
|
|
53
|
+
export async function signRequest(input) {
|
|
54
|
+
const url = new URL(input.url);
|
|
55
|
+
const { amzDate, day } = amzDates(input.date ?? new Date());
|
|
56
|
+
const payloadHash = input.unsignedPayload
|
|
57
|
+
? 'UNSIGNED-PAYLOAD'
|
|
58
|
+
: await sha256Hex(input.body ?? '');
|
|
59
|
+
const headers = {};
|
|
60
|
+
for (const [k, v] of Object.entries(input.headers ?? {})) {
|
|
61
|
+
headers[k.toLowerCase()] = v.trim();
|
|
62
|
+
}
|
|
63
|
+
headers['host'] = url.host;
|
|
64
|
+
headers['x-amz-date'] = amzDate;
|
|
65
|
+
headers['x-amz-content-sha256'] = payloadHash;
|
|
66
|
+
const names = Object.keys(headers).sort();
|
|
67
|
+
const canonicalRequest = [
|
|
68
|
+
input.method.toUpperCase(),
|
|
69
|
+
canonicalPath(url.pathname),
|
|
70
|
+
canonicalQuery(url.searchParams),
|
|
71
|
+
names.map((n) => `${n}:${headers[n]}\n`).join(''),
|
|
72
|
+
names.join(';'),
|
|
73
|
+
payloadHash,
|
|
74
|
+
].join('\n');
|
|
75
|
+
const signed = await signature(input.credentials, day, amzDate, canonicalRequest);
|
|
76
|
+
headers['authorization'] =
|
|
77
|
+
`AWS4-HMAC-SHA256 Credential=${input.credentials.accessKeyId}/${signed.scope}, ` +
|
|
78
|
+
`SignedHeaders=${names.join(';')}, Signature=${signed.signature}`;
|
|
79
|
+
return headers;
|
|
80
|
+
}
|
|
81
|
+
export async function presignUrl(input) {
|
|
82
|
+
const url = new URL(input.url);
|
|
83
|
+
const { amzDate, day } = amzDates(input.date ?? new Date());
|
|
84
|
+
const service = input.credentials.service ?? 's3';
|
|
85
|
+
const headers = { host: url.host };
|
|
86
|
+
for (const [k, v] of Object.entries(input.signedHeaders ?? {})) {
|
|
87
|
+
headers[k.toLowerCase()] = v.trim();
|
|
88
|
+
}
|
|
89
|
+
const names = Object.keys(headers).sort();
|
|
90
|
+
url.searchParams.set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256');
|
|
91
|
+
url.searchParams.set('X-Amz-Credential', `${input.credentials.accessKeyId}/${day}/${input.credentials.region}/${service}/aws4_request`);
|
|
92
|
+
url.searchParams.set('X-Amz-Date', amzDate);
|
|
93
|
+
url.searchParams.set('X-Amz-Expires', String(input.expiresIn));
|
|
94
|
+
url.searchParams.set('X-Amz-SignedHeaders', names.join(';'));
|
|
95
|
+
const canonicalRequest = [
|
|
96
|
+
input.method.toUpperCase(),
|
|
97
|
+
canonicalPath(url.pathname),
|
|
98
|
+
canonicalQuery(url.searchParams),
|
|
99
|
+
names.map((n) => `${n}:${headers[n]}\n`).join(''),
|
|
100
|
+
names.join(';'),
|
|
101
|
+
'UNSIGNED-PAYLOAD',
|
|
102
|
+
].join('\n');
|
|
103
|
+
const signed = await signature(input.credentials, day, amzDate, canonicalRequest);
|
|
104
|
+
url.searchParams.set('X-Amz-Signature', signed.signature);
|
|
105
|
+
return url.toString();
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=sigv4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sigv4.js","sourceRoot":"","sources":["../../src/storage/sigv4.ts"],"names":[],"mappings":"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","sourcesContent":["// src/storage/sigv4.ts — AWS Signature Version 4 on WebCrypto, so S3 access\n// and presigned URLs work in Bun, workerd, and celld without Bun.S3Client.\nexport type SigV4Credentials = {\n accessKeyId: string\n secretAccessKey: string\n region: string\n service?: string\n}\n\nconst encoder = new TextEncoder()\n\nfunction hex(bytes: ArrayBuffer): string {\n return Array.from(new Uint8Array(bytes), (b) =>\n b.toString(16).padStart(2, '0'),\n ).join('')\n}\n\nasync function sha256Hex(\n data: ArrayBuffer | Uint8Array<ArrayBuffer> | string,\n): Promise<string> {\n const bytes = typeof data === 'string' ? encoder.encode(data) : data\n return hex(await crypto.subtle.digest('SHA-256', bytes))\n}\n\nasync function hmac(\n key: ArrayBuffer | Uint8Array<ArrayBuffer>,\n data: string,\n): Promise<ArrayBuffer> {\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n key,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n return crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(data))\n}\n\nasync function signingKey(\n secret: string,\n day: string,\n region: string,\n service: string,\n): Promise<ArrayBuffer> {\n const kDate = await hmac(encoder.encode(`AWS4${secret}`), day)\n const kRegion = await hmac(kDate, region)\n const kService = await hmac(kRegion, service)\n return hmac(kService, 'aws4_request')\n}\n\n/** RFC 3986 encoding, as SigV4 requires: only A-Z a-z 0-9 - _ . ~ stay. */\nfunction uriEncode(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,\n )\n}\n\n/** S3 does not normalize paths: encode each segment once, keep the slashes. */\nfunction canonicalPath(pathname: string): string {\n return pathname\n .split('/')\n .map((segment) => uriEncode(decodeURIComponent(segment)))\n .join('/')\n}\n\nfunction canonicalQuery(params: URLSearchParams): string {\n return [...params.entries()]\n .map(([k, v]) => [uriEncode(k), uriEncode(v)] as const)\n .sort(([a, av], [b, bv]) =>\n a < b ? -1 : a > b ? 1 : av < bv ? -1 : av > bv ? 1 : 0,\n )\n .map(([k, v]) => `${k}=${v}`)\n .join('&')\n}\n\nfunction amzDates(date: Date): { amzDate: string; day: string } {\n const amzDate = date.toISOString().replace(/[:-]|\\.\\d{3}/g, '')\n return { amzDate, day: amzDate.slice(0, 8) }\n}\n\nasync function signature(\n credentials: SigV4Credentials,\n day: string,\n amzDate: string,\n canonicalRequest: string,\n): Promise<{ scope: string; signature: string }> {\n const service = credentials.service ?? 's3'\n const scope = `${day}/${credentials.region}/${service}/aws4_request`\n const stringToSign = [\n 'AWS4-HMAC-SHA256',\n amzDate,\n scope,\n await sha256Hex(canonicalRequest),\n ].join('\\n')\n const key = await signingKey(\n credentials.secretAccessKey,\n day,\n credentials.region,\n service,\n )\n return { scope, signature: hex(await hmac(key, stringToSign)) }\n}\n\nexport async function signRequest(input: {\n method: string\n url: string\n headers?: Record<string, string>\n body?: ArrayBuffer | Uint8Array<ArrayBuffer> | string | null\n credentials: SigV4Credentials\n date?: Date\n unsignedPayload?: boolean\n}): Promise<Record<string, string>> {\n const url = new URL(input.url)\n const { amzDate, day } = amzDates(input.date ?? new Date())\n const payloadHash = input.unsignedPayload\n ? 'UNSIGNED-PAYLOAD'\n : await sha256Hex(input.body ?? '')\n const headers: Record<string, string> = {}\n for (const [k, v] of Object.entries(input.headers ?? {})) {\n headers[k.toLowerCase()] = v.trim()\n }\n headers['host'] = url.host\n headers['x-amz-date'] = amzDate\n headers['x-amz-content-sha256'] = payloadHash\n const names = Object.keys(headers).sort()\n const canonicalRequest = [\n input.method.toUpperCase(),\n canonicalPath(url.pathname),\n canonicalQuery(url.searchParams),\n names.map((n) => `${n}:${headers[n]}\\n`).join(''),\n names.join(';'),\n payloadHash,\n ].join('\\n')\n const signed = await signature(\n input.credentials,\n day,\n amzDate,\n canonicalRequest,\n )\n headers['authorization'] =\n `AWS4-HMAC-SHA256 Credential=${input.credentials.accessKeyId}/${signed.scope}, ` +\n `SignedHeaders=${names.join(';')}, Signature=${signed.signature}`\n return headers\n}\n\nexport async function presignUrl(input: {\n method: string\n url: string\n credentials: SigV4Credentials\n expiresIn: number\n date?: Date\n signedHeaders?: Record<string, string>\n}): Promise<string> {\n const url = new URL(input.url)\n const { amzDate, day } = amzDates(input.date ?? new Date())\n const service = input.credentials.service ?? 's3'\n const headers: Record<string, string> = { host: url.host }\n for (const [k, v] of Object.entries(input.signedHeaders ?? {})) {\n headers[k.toLowerCase()] = v.trim()\n }\n const names = Object.keys(headers).sort()\n url.searchParams.set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256')\n url.searchParams.set(\n 'X-Amz-Credential',\n `${input.credentials.accessKeyId}/${day}/${input.credentials.region}/${service}/aws4_request`,\n )\n url.searchParams.set('X-Amz-Date', amzDate)\n url.searchParams.set('X-Amz-Expires', String(input.expiresIn))\n url.searchParams.set('X-Amz-SignedHeaders', names.join(';'))\n const canonicalRequest = [\n input.method.toUpperCase(),\n canonicalPath(url.pathname),\n canonicalQuery(url.searchParams),\n names.map((n) => `${n}:${headers[n]}\\n`).join(''),\n names.join(';'),\n 'UNSIGNED-PAYLOAD',\n ].join('\\n')\n const signed = await signature(\n input.credentials,\n day,\n amzDate,\n canonicalRequest,\n )\n url.searchParams.set('X-Amz-Signature', signed.signature)\n return url.toString()\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AnyDb } from '../dialect.js';
|
|
2
|
+
import type { BucketStorageRegistry } from './registry.js';
|
|
3
|
+
/**
|
|
4
|
+
* Reap stale `pending` file-meta rows (two-phase uploads that never confirmed)
|
|
5
|
+
* older than `olderThanMs`. For each, best-effort delete the object (it may
|
|
6
|
+
* never have landed) then remove the meta row. Returns the number reaped.
|
|
7
|
+
*/
|
|
8
|
+
export declare function sweepOrphans(registry: BucketStorageRegistry, db: AnyDb, olderThanMs: number): Promise<number>;
|
|
9
|
+
//# sourceMappingURL=sweep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweep.d.ts","sourceRoot":"","sources":["../../src/storage/sweep.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,qBAAqB,EAC/B,EAAE,EAAE,KAAK,EACT,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CASjB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { deleteFileMetaRow, listStalePendingFiles } from './file-meta.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reap stale `pending` file-meta rows (two-phase uploads that never confirmed)
|
|
4
|
+
* older than `olderThanMs`. For each, best-effort delete the object (it may
|
|
5
|
+
* never have landed) then remove the meta row. Returns the number reaped.
|
|
6
|
+
*/
|
|
7
|
+
export async function sweepOrphans(registry, db, olderThanMs) {
|
|
8
|
+
const cutoff = Date.now() - olderThanMs;
|
|
9
|
+
const rows = await listStalePendingFiles(db, cutoff);
|
|
10
|
+
for (const row of rows) {
|
|
11
|
+
const adapter = registry.get(row.bucket)?.adapter;
|
|
12
|
+
await adapter?.delete(row.fileId).catch(() => { });
|
|
13
|
+
await deleteFileMetaRow(db, row.fileId);
|
|
14
|
+
}
|
|
15
|
+
return rows.length;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=sweep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweep.js","sourceRoot":"","sources":["../../src/storage/sweep.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEtE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAA+B,EAC/B,EAAS,EACT,WAAmB;IAEnB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAA;IACvC,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACpD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;QACjD,MAAM,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACjD,MAAM,iBAAiB,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACzC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAA;AACpB,CAAC","sourcesContent":["// src/storage/sweep.ts\nimport type { AnyDb } from '../dialect'\nimport type { BucketStorageRegistry } from './registry'\n\nimport { deleteFileMetaRow, listStalePendingFiles } from './file-meta'\n\n/**\n * Reap stale `pending` file-meta rows (two-phase uploads that never confirmed)\n * older than `olderThanMs`. For each, best-effort delete the object (it may\n * never have landed) then remove the meta row. Returns the number reaped.\n */\nexport async function sweepOrphans(\n registry: BucketStorageRegistry,\n db: AnyDb,\n olderThanMs: number,\n): Promise<number> {\n const cutoff = Date.now() - olderThanMs\n const rows = await listStalePendingFiles(db, cutoff)\n for (const row of rows) {\n const adapter = registry.get(row.bucket)?.adapter\n await adapter?.delete(row.fileId).catch(() => {})\n await deleteFileMetaRow(db, row.fileId)\n }\n return rows.length\n}\n"]}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { type Collection, type StandardSchema } from '@tanstack/db';
|
|
3
|
+
type TableListResult<TRow> = {
|
|
4
|
+
items: TRow[];
|
|
5
|
+
hasMore: boolean;
|
|
6
|
+
nextCursor?: string;
|
|
7
|
+
total?: number;
|
|
8
|
+
limit?: number;
|
|
9
|
+
offset?: number;
|
|
10
|
+
};
|
|
11
|
+
type TableProcedures<TRow, TCreate, TUpdate> = {
|
|
12
|
+
list: {
|
|
13
|
+
call(input?: Record<string, unknown>): Promise<TableListResult<TRow>>;
|
|
14
|
+
};
|
|
15
|
+
get: {
|
|
16
|
+
call(input: {
|
|
17
|
+
id: string;
|
|
18
|
+
}): Promise<TRow>;
|
|
19
|
+
};
|
|
20
|
+
create: {
|
|
21
|
+
call(input: TCreate): Promise<TRow>;
|
|
22
|
+
};
|
|
23
|
+
update: {
|
|
24
|
+
call(input: {
|
|
25
|
+
params: {
|
|
26
|
+
id: string;
|
|
27
|
+
};
|
|
28
|
+
query: {};
|
|
29
|
+
headers: {};
|
|
30
|
+
body: TUpdate;
|
|
31
|
+
}): Promise<TRow>;
|
|
32
|
+
};
|
|
33
|
+
delete: {
|
|
34
|
+
call(input: {
|
|
35
|
+
id: string;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export type DirectTableApi<TRow, TCreate, TUpdate> = {
|
|
40
|
+
list(input?: Record<string, unknown>): Promise<TableListResult<TRow>>;
|
|
41
|
+
get(id: string | number): Promise<TRow>;
|
|
42
|
+
create(input: TCreate): Promise<TRow>;
|
|
43
|
+
update(id: string | number, input: TUpdate): Promise<TRow>;
|
|
44
|
+
delete(id: string | number): Promise<void>;
|
|
45
|
+
};
|
|
46
|
+
export type TableCollectionConfig = {
|
|
47
|
+
tableName: string;
|
|
48
|
+
procedures: TableProcedures<any, any, any>;
|
|
49
|
+
queryClient: QueryClient;
|
|
50
|
+
/** Rows the default `.collection` syncs per fetch. Defaults to 100. For
|
|
51
|
+
* feed-shaped tables that need real pagination use `scopedCollection`. */
|
|
52
|
+
limit?: number;
|
|
53
|
+
};
|
|
54
|
+
export type ScopedFilterValue = string | number | boolean | null | readonly (string | number)[];
|
|
55
|
+
export type ScopedCollectionOptions = {
|
|
56
|
+
/** Equality filters, e.g. `{ replyToId: null }` — columns must be in the
|
|
57
|
+
* table's `filterableColumns` server-side. Same name and shape as the
|
|
58
|
+
* `filters` accepted by the list procedure. */
|
|
59
|
+
filters?: Record<string, ScopedFilterValue>;
|
|
60
|
+
sort?: string;
|
|
61
|
+
order?: 'asc' | 'desc';
|
|
62
|
+
/** Rows per underlying request; clamped to the server cap (200). */
|
|
63
|
+
pageSize?: number;
|
|
64
|
+
/** Window size on first load and default `loadMore` step. Defaults to 20. */
|
|
65
|
+
initialCount?: number;
|
|
66
|
+
};
|
|
67
|
+
export declare function createTableCollection<TRow extends {
|
|
68
|
+
id: string | number;
|
|
69
|
+
}, TCreate = Partial<TRow>, TUpdate = Partial<TRow>>(config: TableCollectionConfig): {
|
|
70
|
+
collection: import("@tanstack/db").Collection<TRow, string | number, import("@tanstack/query-db-collection").QueryCollectionUtils<TRow, string | number, TRow, unknown>, never, TRow> & import("@tanstack/db").NonSingleResult;
|
|
71
|
+
table: DirectTableApi<TRow, TCreate, TUpdate>;
|
|
72
|
+
scopedCollection: (options?: ScopedCollectionOptions) => {
|
|
73
|
+
collection: import("@tanstack/db").Collection<TRow, string | number, import("@tanstack/query-db-collection").QueryCollectionUtils<TRow, string | number, TRow, unknown>, never, TRow> & import("@tanstack/db").NonSingleResult;
|
|
74
|
+
/** Grow the window by `count` (default initialCount) and refetch in place. */
|
|
75
|
+
loadMore: (count?: number) => Promise<void>;
|
|
76
|
+
/** Whether the server reported rows beyond the window (as of last fetch). */
|
|
77
|
+
hasMore: () => boolean;
|
|
78
|
+
/** Current desired window size. */
|
|
79
|
+
size: () => number;
|
|
80
|
+
};
|
|
81
|
+
collectionByIds: (ids: readonly TRow["id"][], options?: {
|
|
82
|
+
column?: string;
|
|
83
|
+
}) => import("@tanstack/db").Collection<TRow, string | number, import("@tanstack/query-db-collection").QueryCollectionUtils<TRow, string | number, TRow, unknown>, never, TRow> & import("@tanstack/db").NonSingleResult;
|
|
84
|
+
applyRealtimeEvent: (action: "create" | "update" | "delete", record: Record<string, unknown>) => void;
|
|
85
|
+
refetchAll: () => Promise<void>;
|
|
86
|
+
};
|
|
87
|
+
export type ScopedCollection<TRow extends {
|
|
88
|
+
id: string | number;
|
|
89
|
+
}> = {
|
|
90
|
+
collection: Collection<TRow, string | number, Record<string, any>, StandardSchema<TRow>>;
|
|
91
|
+
loadMore: (count?: number) => Promise<void>;
|
|
92
|
+
hasMore: () => boolean;
|
|
93
|
+
size: () => number;
|
|
94
|
+
};
|
|
95
|
+
export type TableCollection<TRow extends {
|
|
96
|
+
id: string | number;
|
|
97
|
+
}, TCreate = Partial<TRow>, TUpdate = Partial<TRow>> = {
|
|
98
|
+
collection: Collection<TRow, string | number, Record<string, any>, StandardSchema<TRow>>;
|
|
99
|
+
table: DirectTableApi<TRow, TCreate, TUpdate>;
|
|
100
|
+
scopedCollection: (options?: ScopedCollectionOptions) => ScopedCollection<TRow>;
|
|
101
|
+
collectionByIds: (ids: readonly (string | number)[], column?: string) => Collection<TRow, string | number, Record<string, any>, StandardSchema<TRow>>;
|
|
102
|
+
applyRealtimeEvent: (action: 'create' | 'update' | 'delete', record: Record<string, unknown>) => void;
|
|
103
|
+
refetchAll: () => Promise<void>;
|
|
104
|
+
};
|
|
105
|
+
export {};
|
|
106
|
+
//# sourceMappingURL=collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/sync/collection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { createCollection, } from '@tanstack/db';
|
|
2
|
+
import { queryCollectionOptions } from '@tanstack/query-db-collection';
|
|
3
|
+
import { createUpdateQueue } from './update-queue.js';
|
|
4
|
+
const MAX_LIST_LIMIT = 200;
|
|
5
|
+
function matchesFilter(record, filters) {
|
|
6
|
+
for (const [col, expected] of Object.entries(filters)) {
|
|
7
|
+
const actual = record[col];
|
|
8
|
+
if (expected === null) {
|
|
9
|
+
if (actual != null)
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
else if (Array.isArray(expected)) {
|
|
13
|
+
if (!expected.map(String).includes(String(actual)))
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
else if (String(actual) !== String(expected))
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
/** Deterministic serialization for cache keys (object key order ignored). */
|
|
22
|
+
function stableKey(value) {
|
|
23
|
+
if (value === null || typeof value !== 'object')
|
|
24
|
+
return JSON.stringify(value);
|
|
25
|
+
if (Array.isArray(value))
|
|
26
|
+
return `[${value.map(stableKey).join(',')}]`;
|
|
27
|
+
const entries = Object.entries(value)
|
|
28
|
+
.sort(([a], [b]) => (a < b ? -1 : 1))
|
|
29
|
+
.map(([k, v]) => `${JSON.stringify(k)}:${stableKey(v)}`);
|
|
30
|
+
return `{${entries.join(',')}}`;
|
|
31
|
+
}
|
|
32
|
+
export function createTableCollection(config) {
|
|
33
|
+
const table = config.procedures;
|
|
34
|
+
const direct = {
|
|
35
|
+
list: (input = {}) => table.list.call(input),
|
|
36
|
+
get: (id) => table.get.call({ id: String(id) }),
|
|
37
|
+
create: (input) => table.create.call(input),
|
|
38
|
+
update: (id, input) => table.update.call({
|
|
39
|
+
params: { id: String(id) },
|
|
40
|
+
query: {},
|
|
41
|
+
headers: {},
|
|
42
|
+
body: input,
|
|
43
|
+
}),
|
|
44
|
+
delete: (id) => table.delete.call({ id: String(id) }),
|
|
45
|
+
};
|
|
46
|
+
const collection = createCollection(queryCollectionOptions({
|
|
47
|
+
queryKey: [config.tableName, 'collection'],
|
|
48
|
+
queryFn: async () => {
|
|
49
|
+
const page = await table.list.call({ limit: config.limit ?? 100 });
|
|
50
|
+
return page.items;
|
|
51
|
+
},
|
|
52
|
+
queryClient: config.queryClient,
|
|
53
|
+
getKey: (item) => item.id,
|
|
54
|
+
onInsert: async ({ transaction }) => {
|
|
55
|
+
let reconciled = true;
|
|
56
|
+
for (const mutation of transaction.mutations) {
|
|
57
|
+
// Pass the client-generated `id` through as-is. TanStack DB's
|
|
58
|
+
// optimistic insert keys the local row by this `id` (via `getKey`),
|
|
59
|
+
// and this matches `sanitizeWriteBody`'s default on the server: a
|
|
60
|
+
// client-supplied `id` on create is accepted unless the table's
|
|
61
|
+
// access config sets an explicit `writableColumns` allowlist that
|
|
62
|
+
// excludes `id`.
|
|
63
|
+
const row = (await table.create.call(mutation.modified));
|
|
64
|
+
// Apps that DO restrict it that way get a server-assigned id, which
|
|
65
|
+
// leaves the optimistic row keyed under the client's — `writeUpsert`
|
|
66
|
+
// can't retire that, so those fall back to the refetch.
|
|
67
|
+
if (row?.id != null && String(row.id) === String(mutation.key)) {
|
|
68
|
+
applyRealtimeEvent('create', row);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
reconciled = false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return reconciled ? { refetch: false } : {};
|
|
75
|
+
},
|
|
76
|
+
onUpdate: async ({ transaction }) => {
|
|
77
|
+
await Promise.all(transaction.mutations.map((mutation) => updateQueue.enqueue(mutation.key, mutation.changes)));
|
|
78
|
+
return { refetch: false };
|
|
79
|
+
},
|
|
80
|
+
onDelete: async ({ transaction }) => {
|
|
81
|
+
for (const mutation of transaction.mutations) {
|
|
82
|
+
// Updates still queued for this row are superseded, and one already
|
|
83
|
+
// in flight must land before the DELETE or the two race.
|
|
84
|
+
await updateQueue.settle(mutation.key);
|
|
85
|
+
await table.delete.call({ id: String(mutation.key) });
|
|
86
|
+
applyRealtimeEvent('delete', { id: mutation.key });
|
|
87
|
+
}
|
|
88
|
+
return { refetch: false };
|
|
89
|
+
},
|
|
90
|
+
}));
|
|
91
|
+
// Cursor-shaped workloads call `update()` many times a second. The queue
|
|
92
|
+
// merges everything that piles up for a row while its request is in flight
|
|
93
|
+
// into a single follow-up, so request rate tracks RTT, not call rate.
|
|
94
|
+
const updateQueue = createUpdateQueue({
|
|
95
|
+
send: (id, changes) => table.update.call({
|
|
96
|
+
params: { id: String(id) },
|
|
97
|
+
query: {},
|
|
98
|
+
headers: {},
|
|
99
|
+
body: changes,
|
|
100
|
+
}),
|
|
101
|
+
onResult: async (row) => {
|
|
102
|
+
// `writeUpsert` writes the response whole, so mutation endpoints are
|
|
103
|
+
// contracted to return a full row. A body without an id can't be
|
|
104
|
+
// reconciled locally — fall back to the refetch rather than write it.
|
|
105
|
+
if (row?.id == null)
|
|
106
|
+
await collection.utils.refetch();
|
|
107
|
+
else
|
|
108
|
+
applyRealtimeEvent('update', row);
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
// Scoped/byIds collections register here so realtime events fan out to
|
|
112
|
+
// every live view of this table, filtered client-side by each view's
|
|
113
|
+
// own predicate (broadcasts are already access-filtered server-side).
|
|
114
|
+
const registry = [];
|
|
115
|
+
const scopedCache = new Map();
|
|
116
|
+
function scopedCollection(options = {}) {
|
|
117
|
+
const pageSize = Math.min(options.pageSize ?? MAX_LIST_LIMIT, MAX_LIST_LIMIT);
|
|
118
|
+
const initialCount = options.initialCount ?? 20;
|
|
119
|
+
const filters = options.filters ?? {};
|
|
120
|
+
const cacheKey = stableKey({
|
|
121
|
+
filters,
|
|
122
|
+
sort: options.sort ?? null,
|
|
123
|
+
order: options.order ?? null,
|
|
124
|
+
pageSize,
|
|
125
|
+
initialCount,
|
|
126
|
+
});
|
|
127
|
+
const cached = scopedCache.get(cacheKey);
|
|
128
|
+
if (cached)
|
|
129
|
+
return cached;
|
|
130
|
+
let desiredCount = initialCount;
|
|
131
|
+
let serverHasMore = false;
|
|
132
|
+
const scoped = createCollection(queryCollectionOptions({
|
|
133
|
+
queryKey: [config.tableName, 'scoped', cacheKey],
|
|
134
|
+
queryFn: async () => {
|
|
135
|
+
// Growing window: walk cursor pages (each ≤ the server cap) until
|
|
136
|
+
// the current desired count is collected or the table runs out.
|
|
137
|
+
// The collection stays stable across loadMore — refetching in
|
|
138
|
+
// place only ever adds rows, so already-rendered items never
|
|
139
|
+
// unmount (no scroll jumps or zero-item flashes).
|
|
140
|
+
const items = [];
|
|
141
|
+
let cursor;
|
|
142
|
+
let more = false;
|
|
143
|
+
while (items.length < desiredCount) {
|
|
144
|
+
const remaining = Math.min(pageSize, desiredCount - items.length);
|
|
145
|
+
const page = await table.list.call({
|
|
146
|
+
...(Object.keys(filters).length ? { filters } : {}),
|
|
147
|
+
...(options.sort ? { sort: options.sort } : {}),
|
|
148
|
+
...(options.order ? { order: options.order } : {}),
|
|
149
|
+
limit: remaining,
|
|
150
|
+
...(cursor ? { cursor } : {}),
|
|
151
|
+
});
|
|
152
|
+
items.push(...page.items);
|
|
153
|
+
more = Boolean(page.hasMore && page.nextCursor);
|
|
154
|
+
if (!more)
|
|
155
|
+
break;
|
|
156
|
+
cursor = page.nextCursor;
|
|
157
|
+
}
|
|
158
|
+
serverHasMore = more;
|
|
159
|
+
return items.slice(0, desiredCount);
|
|
160
|
+
},
|
|
161
|
+
queryClient: config.queryClient,
|
|
162
|
+
getKey: (item) => item.id,
|
|
163
|
+
}));
|
|
164
|
+
const entry = {
|
|
165
|
+
collection: scoped,
|
|
166
|
+
loadMore: async (count) => {
|
|
167
|
+
desiredCount += count ?? initialCount;
|
|
168
|
+
await scoped.utils.refetch();
|
|
169
|
+
},
|
|
170
|
+
hasMore: () => serverHasMore,
|
|
171
|
+
size: () => desiredCount,
|
|
172
|
+
};
|
|
173
|
+
registry.push({
|
|
174
|
+
collection: scoped,
|
|
175
|
+
matches: (record) => matchesFilter(record, filters),
|
|
176
|
+
refetch: async () => {
|
|
177
|
+
await scoped.utils.refetch();
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
scopedCache.set(cacheKey, entry);
|
|
181
|
+
return entry;
|
|
182
|
+
}
|
|
183
|
+
const byIdsCache = new Map();
|
|
184
|
+
function collectionByIds(ids, options = {}) {
|
|
185
|
+
const column = options.column ?? 'id';
|
|
186
|
+
const unique = Array.from(new Set(ids.map(String))).sort();
|
|
187
|
+
const cacheKey = `${column}:${unique.join(',')}`;
|
|
188
|
+
const cached = byIdsCache.get(cacheKey);
|
|
189
|
+
if (cached)
|
|
190
|
+
return cached;
|
|
191
|
+
const idSet = new Set(unique);
|
|
192
|
+
const byIds = createCollection(queryCollectionOptions({
|
|
193
|
+
queryKey: [config.tableName, 'byIds', column, unique],
|
|
194
|
+
queryFn: async () => {
|
|
195
|
+
if (unique.length === 0)
|
|
196
|
+
return [];
|
|
197
|
+
const items = [];
|
|
198
|
+
// Chunked at the server's IN-filter cap so any id set works.
|
|
199
|
+
for (let i = 0; i < unique.length; i += MAX_LIST_LIMIT) {
|
|
200
|
+
const chunk = unique.slice(i, i + MAX_LIST_LIMIT);
|
|
201
|
+
const page = await table.list.call({
|
|
202
|
+
filters: { [column]: chunk },
|
|
203
|
+
limit: chunk.length,
|
|
204
|
+
});
|
|
205
|
+
items.push(...page.items);
|
|
206
|
+
}
|
|
207
|
+
return items;
|
|
208
|
+
},
|
|
209
|
+
queryClient: config.queryClient,
|
|
210
|
+
getKey: (item) => item.id,
|
|
211
|
+
}));
|
|
212
|
+
registry.push({
|
|
213
|
+
collection: byIds,
|
|
214
|
+
matches: (record) => idSet.has(String(record[column])),
|
|
215
|
+
refetch: async () => {
|
|
216
|
+
await byIds.utils.refetch();
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
byIdsCache.set(cacheKey, byIds);
|
|
220
|
+
return byIds;
|
|
221
|
+
}
|
|
222
|
+
function applyRealtimeEvent(action, record) {
|
|
223
|
+
const id = record['id'];
|
|
224
|
+
const apply = (target, matches) => {
|
|
225
|
+
// Collections that never started syncing (no subscribers yet) can't
|
|
226
|
+
// accept manual writes — and don't need to: their first sync fetches
|
|
227
|
+
// fresh data anyway.
|
|
228
|
+
if (target.status !== 'ready')
|
|
229
|
+
return;
|
|
230
|
+
if (action === 'delete' || !matches(record)) {
|
|
231
|
+
if (target.get(id) !== undefined)
|
|
232
|
+
target.utils.writeDelete(id);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
target.utils.writeUpsert(record);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
apply(collection, () => true);
|
|
239
|
+
for (const entry of registry)
|
|
240
|
+
apply(entry.collection, entry.matches);
|
|
241
|
+
}
|
|
242
|
+
/** Refetch the base collection plus every scoped/byIds view after reconnect. */
|
|
243
|
+
async function refetchAll() {
|
|
244
|
+
await Promise.all([
|
|
245
|
+
collection.utils.refetch(),
|
|
246
|
+
...registry.map((entry) => entry.refetch()),
|
|
247
|
+
]);
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
collection,
|
|
251
|
+
table: direct,
|
|
252
|
+
scopedCollection,
|
|
253
|
+
collectionByIds,
|
|
254
|
+
applyRealtimeEvent,
|
|
255
|
+
refetchAll,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/sync/collection.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gBAAgB,GAGjB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AAEtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAClD,MAAM,cAAc,GAAG,GAAG,CAAA;AAiE1B,SAAS,aAAa,CACpB,MAA+B,EAC/B,OAA0C;IAE1C,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtD,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QAC1B,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,IAAI,MAAM,IAAI,IAAI;gBAAE,OAAO,KAAK,CAAA;QAClC,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAA;QAClE,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAA;IAC9D,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,6EAA6E;AAC7E,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC7E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;IACtE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;SAC7D,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACpC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC1D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;AACjC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAInC,MAA6B;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,UAAqD,CAAA;IAC1E,MAAM,MAAM,GAA2C;QACrD,IAAI,EAAE,CAAC,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAC5C,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QAC/C,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QAC3C,MAAM,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,CACpB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE;YAC1B,KAAK,EAAE,EAAE;YACT,OAAO,EAAE,EAAE;YACX,IAAI,EAAE,KAAK;SACZ,CAAC;QACJ,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;KACtD,CAAA;IAED,MAAM,UAAU,GAAG,gBAAgB,CACjC,sBAAsB,CAAO;QAC3B,QAAQ,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC;QAC1C,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC,CAAA;YAClE,OAAO,IAAI,CAAC,KAAK,CAAA;QACnB,CAAC;QACD,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE;QACzB,QAAQ,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;YAClC,IAAI,UAAU,GAAG,IAAI,CAAA;YACrB,KAAK,MAAM,QAAQ,IAAI,WAAW,CAAC,SAAS,EAAE,CAAC;gBAC7C,8DAA8D;gBAC9D,oEAAoE;gBACpE,kEAAkE;gBAClE,gEAAgE;gBAChE,kEAAkE;gBAClE,iBAAiB;gBACjB,MAAM,GAAG,GAAG,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAClC,QAAQ,CAAC,QAA8B,CACxC,CAAqB,CAAA;gBACtB,oEAAoE;gBACpE,qEAAqE;gBACrE,wDAAwD;gBACxD,IAAI,GAAG,EAAE,EAAE,IAAI,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC/D,kBAAkB,CAAC,QAAQ,EAAE,GAA8B,CAAC,CAAA;gBAC9D,CAAC;qBAAM,CAAC;oBACN,UAAU,GAAG,KAAK,CAAA;gBACpB,CAAC;YACH,CAAC;YACD,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAC7C,CAAC;QACD,QAAQ,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;YAClC,MAAM,OAAO,CAAC,GAAG,CACf,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CACrC,WAAW,CAAC,OAAO,CACjB,QAAQ,CAAC,GAAiB,EAC1B,QAAQ,CAAC,OAAkC,CAC5C,CACF,CACF,CAAA;YACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;QAC3B,CAAC;QACD,QAAQ,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;YAClC,KAAK,MAAM,QAAQ,IAAI,WAAW,CAAC,SAAS,EAAE,CAAC;gBAC7C,oEAAoE;gBACpE,yDAAyD;gBACzD,MAAM,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAiB,CAAC,CAAA;gBACpD,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBACrD,kBAAkB,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAA;YACpD,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;QAC3B,CAAC;KACF,CAAC,CACH,CAAA;IAED,yEAAyE;IACzE,2EAA2E;IAC3E,sEAAsE;IACtE,MAAM,WAAW,GAAG,iBAAiB,CAAmB;QACtD,IAAI,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,CACpB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;YAChB,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE;YAC1B,KAAK,EAAE,EAAE;YACT,OAAO,EAAE,EAAE;YACX,IAAI,EAAE,OAA6B;SACpC,CAAC;QACJ,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACtB,qEAAqE;YACrE,iEAAiE;YACjE,sEAAsE;YACtE,IAAK,GAAwB,EAAE,EAAE,IAAI,IAAI;gBACvC,MAAM,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,CAAA;;gBAC7B,kBAAkB,CAAC,QAAQ,EAAE,GAA8B,CAAC,CAAA;QACnE,CAAC;KACF,CAAC,CAAA;IAIF,uEAAuE;IACvE,qEAAqE;IACrE,sEAAsE;IACtE,MAAM,QAAQ,GAIR,EAAE,CAAA;IAER,MAAM,WAAW,GAAG,IAAI,GAAG,EAA4B,CAAA;IAYvD,SAAS,gBAAgB,CACvB,UAAmC,EAAE;QAErC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CACvB,OAAO,CAAC,QAAQ,IAAI,cAAc,EAClC,cAAc,CACf,CAAA;QACD,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAA;QAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAA;QACrC,MAAM,QAAQ,GAAG,SAAS,CAAC;YACzB,OAAO;YACP,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;YAC5B,QAAQ;YACR,YAAY;SACb,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACxC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAA;QAEzB,IAAI,YAAY,GAAG,YAAY,CAAA;QAC/B,IAAI,aAAa,GAAG,KAAK,CAAA;QAEzB,MAAM,MAAM,GAAG,gBAAgB,CAC7B,sBAAsB,CAAO;YAC3B,QAAQ,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC;YAChD,OAAO,EAAE,KAAK,IAAI,EAAE;gBAClB,kEAAkE;gBAClE,gEAAgE;gBAChE,8DAA8D;gBAC9D,6DAA6D;gBAC7D,kDAAkD;gBAClD,MAAM,KAAK,GAAW,EAAE,CAAA;gBACxB,IAAI,MAA0B,CAAA;gBAC9B,IAAI,IAAI,GAAG,KAAK,CAAA;gBAChB,OAAO,KAAK,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC;oBACnC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAA;oBACjE,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;wBACjC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;wBACnD,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;wBAC/C,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;wBAClD,KAAK,EAAE,SAAS;wBAChB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC9B,CAAC,CAAA;oBACF,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;oBACzB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,CAAA;oBAC/C,IAAI,CAAC,IAAI;wBAAE,MAAK;oBAChB,MAAM,GAAG,IAAI,CAAC,UAAU,CAAA;gBAC1B,CAAC;gBACD,aAAa,GAAG,IAAI,CAAA;gBACpB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAA;YACrC,CAAC;YACD,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE;SAC1B,CAAC,CACH,CAAA;QAED,MAAM,KAAK,GAAqB;YAC9B,UAAU,EAAE,MAAM;YAClB,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;gBACxB,YAAY,IAAI,KAAK,IAAI,YAAY,CAAA;gBACrC,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAA;YAC9B,CAAC;YACD,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa;YAC5B,IAAI,EAAE,GAAG,EAAE,CAAC,YAAY;SACzB,CAAA;QACD,QAAQ,CAAC,IAAI,CAAC;YACZ,UAAU,EAAE,MAAM;YAClB,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC;YACnD,OAAO,EAAE,KAAK,IAAI,EAAE;gBAClB,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAA;YAC9B,CAAC;SACF,CAAC,CAAA;QACF,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QAChC,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAA;IAEhD,SAAS,eAAe,CACtB,GAA0B,EAC1B,UAA+B,EAAE;QAEjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAA;QACrC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAC1D,MAAM,QAAQ,GAAG,GAAG,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;QAChD,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACvC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAA;QAEzB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAA;QAC7B,MAAM,KAAK,GAAG,gBAAgB,CAC5B,sBAAsB,CAAO;YAC3B,QAAQ,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC;YACrD,OAAO,EAAE,KAAK,IAAI,EAAE;gBAClB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO,EAAE,CAAA;gBAClC,MAAM,KAAK,GAAW,EAAE,CAAA;gBACxB,6DAA6D;gBAC7D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,cAAc,EAAE,CAAC;oBACvD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,CAAA;oBACjD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;wBACjC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE;wBAC5B,KAAK,EAAE,KAAK,CAAC,MAAM;qBACpB,CAAC,CAAA;oBACF,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;gBAC3B,CAAC;gBACD,OAAO,KAAK,CAAA;YACd,CAAC;YACD,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE;SAC1B,CAAC,CACH,CAAA;QACD,QAAQ,CAAC,IAAI,CAAC;YACZ,UAAU,EAAE,KAAK;YACjB,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YACtD,OAAO,EAAE,KAAK,IAAI,EAAE;gBAClB,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAA;YAC7B,CAAC;SACF,CAAC,CAAA;QACF,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QAC/B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,SAAS,kBAAkB,CACzB,MAAsC,EACtC,MAA+B;QAE/B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAoB,CAAA;QAC1C,MAAM,KAAK,GAAG,CACZ,MAAkB,EAClB,OAAqD,EACrD,EAAE;YACF,oEAAoE;YACpE,qEAAqE;YACrE,qBAAqB;YACrB,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO;gBAAE,OAAM;YACrC,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5C,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,SAAS;oBAAE,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;YAChE,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,MAAyB,CAAC,CAAA;YACrD,CAAC;QACH,CAAC,CAAA;QACD,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;QAC7B,KAAK,MAAM,KAAK,IAAI,QAAQ;YAAE,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IACtE,CAAC;IAED,gFAAgF;IAChF,KAAK,UAAU,UAAU;QACvB,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE;YAC1B,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;SAC5C,CAAC,CAAA;IACJ,CAAC;IAED,OAAO;QACL,UAAU;QACV,KAAK,EAAE,MAAM;QACb,gBAAgB;QAChB,eAAe;QACf,kBAAkB;QAClB,UAAU;KACX,CAAA;AACH,CAAC","sourcesContent":["import type { QueryClient } from '@tanstack/react-query'\n\nimport {\n createCollection,\n type Collection,\n type StandardSchema,\n} from '@tanstack/db'\nimport { queryCollectionOptions } from '@tanstack/query-db-collection'\n\nimport { createUpdateQueue } from './update-queue'\nconst MAX_LIST_LIMIT = 200\n\ntype TableListResult<TRow> = {\n items: TRow[]\n hasMore: boolean\n nextCursor?: string\n total?: number\n limit?: number\n offset?: number\n}\n\ntype TableProcedures<TRow, TCreate, TUpdate> = {\n list: {\n call(input?: Record<string, unknown>): Promise<TableListResult<TRow>>\n }\n get: { call(input: { id: string }): Promise<TRow> }\n create: { call(input: TCreate): Promise<TRow> }\n update: {\n call(input: {\n params: { id: string }\n query: {}\n headers: {}\n body: TUpdate\n }): Promise<TRow>\n }\n delete: { call(input: { id: string }): Promise<void> }\n}\n\nexport type DirectTableApi<TRow, TCreate, TUpdate> = {\n list(input?: Record<string, unknown>): Promise<TableListResult<TRow>>\n get(id: string | number): Promise<TRow>\n create(input: TCreate): Promise<TRow>\n update(id: string | number, input: TUpdate): Promise<TRow>\n delete(id: string | number): Promise<void>\n}\n\nexport type TableCollectionConfig = {\n tableName: string\n procedures: TableProcedures<any, any, any>\n queryClient: QueryClient\n /** Rows the default `.collection` syncs per fetch. Defaults to 100. For\n * feed-shaped tables that need real pagination use `scopedCollection`. */\n limit?: number\n}\n\nexport type ScopedFilterValue =\n | string\n | number\n | boolean\n | null\n | readonly (string | number)[]\n\nexport type ScopedCollectionOptions = {\n /** Equality filters, e.g. `{ replyToId: null }` — columns must be in the\n * table's `filterableColumns` server-side. Same name and shape as the\n * `filters` accepted by the list procedure. */\n filters?: Record<string, ScopedFilterValue>\n sort?: string\n order?: 'asc' | 'desc'\n /** Rows per underlying request; clamped to the server cap (200). */\n pageSize?: number\n /** Window size on first load and default `loadMore` step. Defaults to 20. */\n initialCount?: number\n}\n\nfunction matchesFilter(\n record: Record<string, unknown>,\n filters: Record<string, ScopedFilterValue>,\n): boolean {\n for (const [col, expected] of Object.entries(filters)) {\n const actual = record[col]\n if (expected === null) {\n if (actual != null) return false\n } else if (Array.isArray(expected)) {\n if (!expected.map(String).includes(String(actual))) return false\n } else if (String(actual) !== String(expected)) return false\n }\n return true\n}\n\n/** Deterministic serialization for cache keys (object key order ignored). */\nfunction stableKey(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value)\n if (Array.isArray(value)) return `[${value.map(stableKey).join(',')}]`\n const entries = Object.entries(value as Record<string, unknown>)\n .sort(([a], [b]) => (a < b ? -1 : 1))\n .map(([k, v]) => `${JSON.stringify(k)}:${stableKey(v)}`)\n return `{${entries.join(',')}}`\n}\n\nexport function createTableCollection<\n TRow extends { id: string | number },\n TCreate = Partial<TRow>,\n TUpdate = Partial<TRow>,\n>(config: TableCollectionConfig) {\n const table = config.procedures as TableProcedures<TRow, TCreate, TUpdate>\n const direct: DirectTableApi<TRow, TCreate, TUpdate> = {\n list: (input = {}) => table.list.call(input),\n get: (id) => table.get.call({ id: String(id) }),\n create: (input) => table.create.call(input),\n update: (id, input) =>\n table.update.call({\n params: { id: String(id) },\n query: {},\n headers: {},\n body: input,\n }),\n delete: (id) => table.delete.call({ id: String(id) }),\n }\n\n const collection = createCollection(\n queryCollectionOptions<TRow>({\n queryKey: [config.tableName, 'collection'],\n queryFn: async () => {\n const page = await table.list.call({ limit: config.limit ?? 100 })\n return page.items\n },\n queryClient: config.queryClient,\n getKey: (item) => item.id,\n onInsert: async ({ transaction }) => {\n let reconciled = true\n for (const mutation of transaction.mutations) {\n // Pass the client-generated `id` through as-is. TanStack DB's\n // optimistic insert keys the local row by this `id` (via `getKey`),\n // and this matches `sanitizeWriteBody`'s default on the server: a\n // client-supplied `id` on create is accepted unless the table's\n // access config sets an explicit `writableColumns` allowlist that\n // excludes `id`.\n const row = (await table.create.call(\n mutation.modified as unknown as TCreate,\n )) as TRow | undefined\n // Apps that DO restrict it that way get a server-assigned id, which\n // leaves the optimistic row keyed under the client's — `writeUpsert`\n // can't retire that, so those fall back to the refetch.\n if (row?.id != null && String(row.id) === String(mutation.key)) {\n applyRealtimeEvent('create', row as Record<string, unknown>)\n } else {\n reconciled = false\n }\n }\n return reconciled ? { refetch: false } : {}\n },\n onUpdate: async ({ transaction }) => {\n await Promise.all(\n transaction.mutations.map((mutation) =>\n updateQueue.enqueue(\n mutation.key as TRow['id'],\n mutation.changes as Record<string, unknown>,\n ),\n ),\n )\n return { refetch: false }\n },\n onDelete: async ({ transaction }) => {\n for (const mutation of transaction.mutations) {\n // Updates still queued for this row are superseded, and one already\n // in flight must land before the DELETE or the two race.\n await updateQueue.settle(mutation.key as TRow['id'])\n await table.delete.call({ id: String(mutation.key) })\n applyRealtimeEvent('delete', { id: mutation.key })\n }\n return { refetch: false }\n },\n }),\n )\n\n // Cursor-shaped workloads call `update()` many times a second. The queue\n // merges everything that piles up for a row while its request is in flight\n // into a single follow-up, so request rate tracks RTT, not call rate.\n const updateQueue = createUpdateQueue<TRow['id'], TRow>({\n send: (id, changes) =>\n table.update.call({\n params: { id: String(id) },\n query: {},\n headers: {},\n body: changes as unknown as TUpdate,\n }),\n onResult: async (row) => {\n // `writeUpsert` writes the response whole, so mutation endpoints are\n // contracted to return a full row. A body without an id can't be\n // reconciled locally — fall back to the refetch rather than write it.\n if ((row as TRow | undefined)?.id == null)\n await collection.utils.refetch()\n else applyRealtimeEvent('update', row as Record<string, unknown>)\n },\n })\n\n type Collection = typeof collection\n\n // Scoped/byIds collections register here so realtime events fan out to\n // every live view of this table, filtered client-side by each view's\n // own predicate (broadcasts are already access-filtered server-side).\n const registry: {\n collection: Collection\n matches: (record: Record<string, unknown>) => boolean\n refetch: () => Promise<void>\n }[] = []\n\n const scopedCache = new Map<string, ScopedCollection>()\n\n type ScopedCollection = {\n collection: Collection\n /** Grow the window by `count` (default initialCount) and refetch in place. */\n loadMore: (count?: number) => Promise<void>\n /** Whether the server reported rows beyond the window (as of last fetch). */\n hasMore: () => boolean\n /** Current desired window size. */\n size: () => number\n }\n\n function scopedCollection(\n options: ScopedCollectionOptions = {},\n ): ScopedCollection {\n const pageSize = Math.min(\n options.pageSize ?? MAX_LIST_LIMIT,\n MAX_LIST_LIMIT,\n )\n const initialCount = options.initialCount ?? 20\n const filters = options.filters ?? {}\n const cacheKey = stableKey({\n filters,\n sort: options.sort ?? null,\n order: options.order ?? null,\n pageSize,\n initialCount,\n })\n const cached = scopedCache.get(cacheKey)\n if (cached) return cached\n\n let desiredCount = initialCount\n let serverHasMore = false\n\n const scoped = createCollection(\n queryCollectionOptions<TRow>({\n queryKey: [config.tableName, 'scoped', cacheKey],\n queryFn: async () => {\n // Growing window: walk cursor pages (each ≤ the server cap) until\n // the current desired count is collected or the table runs out.\n // The collection stays stable across loadMore — refetching in\n // place only ever adds rows, so already-rendered items never\n // unmount (no scroll jumps or zero-item flashes).\n const items: TRow[] = []\n let cursor: string | undefined\n let more = false\n while (items.length < desiredCount) {\n const remaining = Math.min(pageSize, desiredCount - items.length)\n const page = await table.list.call({\n ...(Object.keys(filters).length ? { filters } : {}),\n ...(options.sort ? { sort: options.sort } : {}),\n ...(options.order ? { order: options.order } : {}),\n limit: remaining,\n ...(cursor ? { cursor } : {}),\n })\n items.push(...page.items)\n more = Boolean(page.hasMore && page.nextCursor)\n if (!more) break\n cursor = page.nextCursor\n }\n serverHasMore = more\n return items.slice(0, desiredCount)\n },\n queryClient: config.queryClient,\n getKey: (item) => item.id,\n }),\n )\n\n const entry: ScopedCollection = {\n collection: scoped,\n loadMore: async (count) => {\n desiredCount += count ?? initialCount\n await scoped.utils.refetch()\n },\n hasMore: () => serverHasMore,\n size: () => desiredCount,\n }\n registry.push({\n collection: scoped,\n matches: (record) => matchesFilter(record, filters),\n refetch: async () => {\n await scoped.utils.refetch()\n },\n })\n scopedCache.set(cacheKey, entry)\n return entry\n }\n\n const byIdsCache = new Map<string, Collection>()\n\n function collectionByIds(\n ids: readonly TRow['id'][],\n options: { column?: string } = {},\n ): Collection {\n const column = options.column ?? 'id'\n const unique = Array.from(new Set(ids.map(String))).sort()\n const cacheKey = `${column}:${unique.join(',')}`\n const cached = byIdsCache.get(cacheKey)\n if (cached) return cached\n\n const idSet = new Set(unique)\n const byIds = createCollection(\n queryCollectionOptions<TRow>({\n queryKey: [config.tableName, 'byIds', column, unique],\n queryFn: async () => {\n if (unique.length === 0) return []\n const items: TRow[] = []\n // Chunked at the server's IN-filter cap so any id set works.\n for (let i = 0; i < unique.length; i += MAX_LIST_LIMIT) {\n const chunk = unique.slice(i, i + MAX_LIST_LIMIT)\n const page = await table.list.call({\n filters: { [column]: chunk },\n limit: chunk.length,\n })\n items.push(...page.items)\n }\n return items\n },\n queryClient: config.queryClient,\n getKey: (item) => item.id,\n }),\n )\n registry.push({\n collection: byIds,\n matches: (record) => idSet.has(String(record[column])),\n refetch: async () => {\n await byIds.utils.refetch()\n },\n })\n byIdsCache.set(cacheKey, byIds)\n return byIds\n }\n\n function applyRealtimeEvent(\n action: 'create' | 'update' | 'delete',\n record: Record<string, unknown>,\n ) {\n const id = record['id'] as string | number\n const apply = (\n target: Collection,\n matches: (record: Record<string, unknown>) => boolean,\n ) => {\n // Collections that never started syncing (no subscribers yet) can't\n // accept manual writes — and don't need to: their first sync fetches\n // fresh data anyway.\n if (target.status !== 'ready') return\n if (action === 'delete' || !matches(record)) {\n if (target.get(id) !== undefined) target.utils.writeDelete(id)\n } else {\n target.utils.writeUpsert(record as unknown as TRow)\n }\n }\n apply(collection, () => true)\n for (const entry of registry) apply(entry.collection, entry.matches)\n }\n\n /** Refetch the base collection plus every scoped/byIds view after reconnect. */\n async function refetchAll() {\n await Promise.all([\n collection.utils.refetch(),\n ...registry.map((entry) => entry.refetch()),\n ])\n }\n\n return {\n collection,\n table: direct,\n scopedCollection,\n collectionByIds,\n applyRealtimeEvent,\n refetchAll,\n }\n}\n\nexport type ScopedCollection<TRow extends { id: string | number }> = {\n collection: Collection<\n TRow,\n string | number,\n Record<string, any>,\n StandardSchema<TRow>\n >\n loadMore: (count?: number) => Promise<void>\n hasMore: () => boolean\n size: () => number\n}\n\nexport type TableCollection<\n TRow extends { id: string | number },\n TCreate = Partial<TRow>,\n TUpdate = Partial<TRow>,\n> = {\n collection: Collection<\n TRow,\n string | number,\n Record<string, any>,\n StandardSchema<TRow>\n >\n table: DirectTableApi<TRow, TCreate, TUpdate>\n scopedCollection: (\n options?: ScopedCollectionOptions,\n ) => ScopedCollection<TRow>\n collectionByIds: (\n ids: readonly (string | number)[],\n column?: string,\n ) => Collection<\n TRow,\n string | number,\n Record<string, any>,\n StandardSchema<TRow>\n >\n applyRealtimeEvent: (\n action: 'create' | 'update' | 'delete',\n record: Record<string, unknown>,\n ) => void\n refetchAll: () => Promise<void>\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createSyncClient } from './sync-client.js';
|
|
2
|
+
export type { WorkerstackSyncClient, CreateFor, RowFor, SyncClientOptions, } from './sync-client.js';
|
|
3
|
+
export { createTableCollection } from './collection.js';
|
|
4
|
+
export type { ScopedCollectionOptions, ScopedFilterValue, TableCollection, TableCollectionConfig, } from './collection.js';
|
|
5
|
+
export { createSyncRealtimeClient } from './realtime-sync.js';
|
|
6
|
+
export type { SyncRealtimeConfig, SyncRealtimeTarget } from './realtime-sync.js';
|
|
7
|
+
export type { AnyWorkerstackApp, InferBuckets, InferInsert, InferSchema, InferSelect, InferTables, UploadedFile, } from '../query/index.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,YAAY,EACV,qBAAqB,EACrB,SAAS,EACT,MAAM,EACN,iBAAiB,GAClB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,YAAY,EACV,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAC7E,YAAY,EACV,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,GACb,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAOhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAOpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA","sourcesContent":["export { createSyncClient } from './sync-client'\nexport type {\n WorkerstackSyncClient,\n CreateFor,\n RowFor,\n SyncClientOptions,\n} from './sync-client'\nexport { createTableCollection } from './collection'\nexport type {\n ScopedCollectionOptions,\n ScopedFilterValue,\n TableCollection,\n TableCollectionConfig,\n} from './collection'\nexport { createSyncRealtimeClient } from './realtime-sync'\nexport type { SyncRealtimeConfig, SyncRealtimeTarget } from './realtime-sync'\nexport type {\n AnyWorkerstackApp,\n InferBuckets,\n InferInsert,\n InferSchema,\n InferSelect,\n InferTables,\n UploadedFile,\n} from '../query/index'\n"]}
|