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
package/dist/config.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import { betterAuth } from 'better-auth';
|
|
3
|
+
import * as v from 'valibot';
|
|
4
|
+
import { validateEnv, } from './env.js';
|
|
5
|
+
import { resolveBuckets, } from './storage/buckets.js';
|
|
6
|
+
export function defineAuth(schemaOrConfig, builder) {
|
|
7
|
+
return builder ?? schemaOrConfig;
|
|
8
|
+
}
|
|
9
|
+
// Only the union-shaped options need runtime validation: they are the ones a
|
|
10
|
+
// JavaScript caller can plausibly get wrong in a way that fails confusingly
|
|
11
|
+
// downstream. Everything else is either typed-only or read raw from `options`.
|
|
12
|
+
const RuntimeOptionsSchema = v.object({
|
|
13
|
+
rateLimit: v.optional(v.union([
|
|
14
|
+
v.boolean(),
|
|
15
|
+
v.object({
|
|
16
|
+
windowMs: v.optional(v.number()),
|
|
17
|
+
max: v.optional(v.number()),
|
|
18
|
+
}),
|
|
19
|
+
])),
|
|
20
|
+
idempotency: v.optional(v.union([v.boolean(), v.object({ ttlMs: v.optional(v.number()) })])),
|
|
21
|
+
realtime: v.optional(v.union([
|
|
22
|
+
v.boolean(),
|
|
23
|
+
v.object({
|
|
24
|
+
bufferSize: v.optional(v.number()),
|
|
25
|
+
resumeSeconds: v.optional(v.number()),
|
|
26
|
+
}),
|
|
27
|
+
])),
|
|
28
|
+
openapi: v.optional(v.boolean()),
|
|
29
|
+
});
|
|
30
|
+
export function resolveConfig(options, env,
|
|
31
|
+
// Platform-injected overrides (Bunderhost & co.) beat code-level config so
|
|
32
|
+
// apps with hardcoded local urls deploy unchanged.
|
|
33
|
+
platformSource = process.env) {
|
|
34
|
+
const parsed = v.parse(RuntimeOptionsSchema, options);
|
|
35
|
+
// Self-validate when the caller didn't pass a pre-validated env, so
|
|
36
|
+
// resolveConfig stays usable standalone.
|
|
37
|
+
const resolvedEnv = env ?? validateEnv(undefined);
|
|
38
|
+
const adapter = options.database?.adapter;
|
|
39
|
+
if (!adapter) {
|
|
40
|
+
throw new Error('[workerstack] database.adapter is required');
|
|
41
|
+
}
|
|
42
|
+
const defaultUrl = 'file:./data.db';
|
|
43
|
+
return {
|
|
44
|
+
database: {
|
|
45
|
+
adapter,
|
|
46
|
+
url: platformSource['WORKERSTACK_DATABASE_URL'] ??
|
|
47
|
+
options.database?.url ??
|
|
48
|
+
resolvedEnv.DATABASE_URL ??
|
|
49
|
+
defaultUrl,
|
|
50
|
+
authToken: platformSource['WORKERSTACK_DATABASE_AUTH_TOKEN'] ??
|
|
51
|
+
options.database?.authToken ??
|
|
52
|
+
resolvedEnv.DATABASE_AUTH_TOKEN,
|
|
53
|
+
migrations: options.database?.migrations ?? './migrations',
|
|
54
|
+
},
|
|
55
|
+
auth: (() => {
|
|
56
|
+
// The secret default has to survive the builder form too, and the builder
|
|
57
|
+
// can only run once the db exists — so wrap it and default afterwards.
|
|
58
|
+
const withSecret = (cfg) => ({
|
|
59
|
+
...cfg,
|
|
60
|
+
secret: cfg.secret ?? resolvedEnv.AUTH_SECRET,
|
|
61
|
+
});
|
|
62
|
+
const authInput = options.auth;
|
|
63
|
+
return typeof authInput === 'function'
|
|
64
|
+
? ((ctx) => withSecret(authInput(ctx)))
|
|
65
|
+
: withSecret(authInput ?? {});
|
|
66
|
+
})(),
|
|
67
|
+
storage: resolveBuckets(options.storage, platformSource),
|
|
68
|
+
realtime: parsed.realtime,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Collapse the object/builder union once the db is up. */
|
|
72
|
+
export function resolveAuthConfig(auth, ctx) {
|
|
73
|
+
return typeof auth === 'function' ? auth(ctx) : auth;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyMiddleware, AnyRouter } from '@orpc/server'\n\n// src/config.ts\nimport { betterAuth } from 'better-auth'\nimport * as v from 'valibot'\n\nimport type {\n AuthSessionResolver,\n SessionUserConfig,\n SessionUserExtraOf,\n TableAccessInput,\n} from './access'\nimport type { WorkerstackApiBuilder } from './api/builder'\nimport type { DatabaseAdapter } from './database/adapter'\nimport type { DbFor } from './db'\nimport type { AnyDb } from './dialect'\nimport type { IdempotencyConfig } from './idempotency'\nimport type { MessagingConfig } from './messaging'\nimport type { RateLimitConfig } from './rate-limit'\n\nimport {\n validateEnv,\n type BaseEnv,\n type EnvConfigInput,\n type ValidatedEnv,\n} from './env'\nimport {\n resolveBuckets,\n type ResolvedStorageBuckets,\n type StorageConfigInput,\n} from './storage/buckets'\n\nexport type BetterAuthConfig = Omit<\n NonNullable<Parameters<typeof betterAuth>[0]>,\n 'database'\n>\n\n/**\n * What an `auth` builder is handed. `db` is the app's own connection, typed\n * from `schema` alone — that is what keeps the builder cycle-free: better-auth\n * hooks living in another file get a db without importing the app whose type\n * they help produce.\n */\nexport type AuthConfigContext<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n> = {\n db: DbFor<TSchema>\n env: ValidatedEnv<TEnv>\n}\n\nexport type AuthConfigInput<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n TConfig extends BetterAuthConfig = BetterAuthConfig,\n> = TConfig | ((ctx: AuthConfigContext<TSchema, TEnv>) => TConfig)\n\n/**\n * Identity helper for defining an auth config in a separate file with full\n * type inference — no explicit generic annotations required.\n *\n * Follows the `defineConfig` / `defineComponent` ecosystem convention:\n * the function does nothing at runtime but gives TypeScript an inference\n * anchor through the `schema` parameter.\n *\n * @example\n * ```ts\n * // Static config (no db/env access needed)\n * export const authConfig = defineAuth({ ... })\n *\n * // Builder with schema-typed db\n * export const authConfig = defineAuth(schema, ({ db }) => ({\n * databaseHooks: {\n * user: { create: { after: async (user) => { await db.insert(...) } } }\n * }\n * }))\n * ```\n */\nexport function defineAuth<const TConfig extends BetterAuthConfig>(\n config: TConfig,\n): TConfig\nexport function defineAuth<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined,\n const TConfig extends BetterAuthConfig,\n>(\n context: { schema: TSchema; env: TEnv },\n builder: (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig,\n): (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig\nexport function defineAuth<\n TSchema extends Record<string, unknown>,\n const TConfig extends BetterAuthConfig,\n TEnv extends EnvConfigInput | undefined = undefined,\n>(\n schema: TSchema,\n builder: (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig,\n): (ctx: AuthConfigContext<TSchema, TEnv>) => TConfig\nexport function defineAuth(\n schemaOrConfig: Record<string, unknown>,\n builder?: (ctx: any) => BetterAuthConfig,\n) {\n return builder ?? schemaOrConfig\n}\n\n/**\n * The builder as {@link ResolvedConfig} carries it: schema-agnostic, because\n * ResolvedConfig is not generic. {@link resolveAuthConfig} is the only caller.\n */\nexport type AuthConfigFactory = (ctx: {\n db: AnyDb\n env: BaseEnv\n}) => BetterAuthConfig\n\n// Only the union-shaped options need runtime validation: they are the ones a\n// JavaScript caller can plausibly get wrong in a way that fails confusingly\n// downstream. Everything else is either typed-only or read raw from `options`.\nconst RuntimeOptionsSchema = v.object({\n rateLimit: v.optional(\n v.union([\n v.boolean(),\n v.object({\n windowMs: v.optional(v.number()),\n max: v.optional(v.number()),\n }),\n ]),\n ),\n idempotency: v.optional(\n v.union([v.boolean(), v.object({ ttlMs: v.optional(v.number()) })]),\n ),\n realtime: v.optional(\n v.union([\n v.boolean(),\n v.object({\n bufferSize: v.optional(v.number()),\n resumeSeconds: v.optional(v.number()),\n }),\n ]),\n ),\n openapi: v.optional(v.boolean()),\n})\n\n/** Realtime declaration. Independent of schema, storage, and env. */\nexport type RealtimeConfigInput =\n | boolean\n | {\n bufferSize?: number\n resumeSeconds?: number\n }\n\nexport type WorkerstackConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined =\n | Record<string, TableAccessInput>\n | undefined,\n TStorage extends StorageConfigInput | undefined =\n | StorageConfigInput\n | undefined,\n TEnv extends EnvConfigInput | undefined = EnvConfigInput | undefined,\n TCustomApiRouter extends AnyRouter | undefined = AnyRouter | undefined,\n TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n TSession extends SessionUserConfig<Record<string, unknown>> | undefined =\n undefined,\n> = {\n schema: TSchema\n access?: TAccess\n database: {\n adapter: DatabaseAdapter\n url?: string\n authToken?: string\n migrations?: string\n }\n /**\n * better-auth options, or a builder receiving `{ db, env }`. Use the builder\n * form when database hooks need to write: it hands out the app's own\n * connection, so the application never opens a second one just to satisfy a\n * config that is built before the app exists.\n */\n auth?: AuthConfigInput<NoInfer<TSchema>, NoInfer<TEnv>, TAuthConfig>\n /**\n * Reuse an application-owned session reader for the unified API while\n * keeping Workerstack's auth handler available.\n */\n authResolver?: AuthSessionResolver<SessionUserExtraOf<TSession>>\n /** Explicitly project application fields from Better Auth into API users. */\n session?: TSession\n storage?: TStorage\n messaging?: MessagingConfig\n /**\n * The application's oRPC router. Pass the finished router object, or a\n * callback when the router needs the framework builder at configuration\n * time. Declare the router at module scope with `defineApi` and the object\n * form keeps router modules free of factory wrappers.\n */\n api?:\n | TCustomApiRouter\n | ((\n builder: WorkerstackApiBuilder<\n TSchema,\n ValidatedEnv<TEnv>,\n MessagingConfig,\n SessionUserExtraOf<TSession>\n >,\n ) => TCustomApiRouter)\n /**\n * Middleware applied to every procedure in the graph: generated CRUD,\n * storage, realtime, health, and the application's own procedures. Declare\n * each one with `o.middleware(...)` from `defineApi`.\n *\n * It runs before authentication, so `context.user` is not available inside\n * it. Read an already-resolved caller with `context.peekSession()`.\n */\n middleware?: AnyMiddleware[]\n rateLimit?: boolean | RateLimitConfig\n idempotency?: boolean | IdempotencyConfig\n /** Generate and serve `/api/openapi.json`. Disabled by default. */\n openapi?: boolean\n realtime?: RealtimeConfigInput\n}\n\nexport type ResolvedConfig = {\n database: {\n adapter: DatabaseAdapter\n url: string\n authToken?: string\n migrations: string\n }\n auth: BetterAuthConfig | AuthConfigFactory\n storage: ResolvedStorageBuckets\n realtime?: RealtimeConfigInput\n}\n\nexport function resolveConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TCustomApiRouter extends AnyRouter | undefined = undefined,\n TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n TSession extends SessionUserConfig<Record<string, unknown>> | undefined =\n undefined,\n>(\n options: WorkerstackConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TCustomApiRouter,\n TAuthConfig,\n TSession\n >,\n env?: BaseEnv,\n // Platform-injected overrides (Bunderhost & co.) beat code-level config so\n // apps with hardcoded local urls deploy unchanged.\n platformSource: Record<string, string | undefined> = process.env as Record<\n string,\n string | undefined\n >,\n): ResolvedConfig {\n const parsed = v.parse(RuntimeOptionsSchema, options)\n // Self-validate when the caller didn't pass a pre-validated env, so\n // resolveConfig stays usable standalone.\n const resolvedEnv = env ?? validateEnv(undefined)\n\n const adapter = options.database?.adapter\n if (!adapter) {\n throw new Error('[workerstack] database.adapter is required')\n }\n\n const defaultUrl = 'file:./data.db'\n\n return {\n database: {\n adapter,\n url:\n platformSource['WORKERSTACK_DATABASE_URL'] ??\n options.database?.url ??\n resolvedEnv.DATABASE_URL ??\n defaultUrl,\n authToken:\n platformSource['WORKERSTACK_DATABASE_AUTH_TOKEN'] ??\n options.database?.authToken ??\n resolvedEnv.DATABASE_AUTH_TOKEN,\n migrations: options.database?.migrations ?? './migrations',\n },\n auth: (() => {\n // The secret default has to survive the builder form too, and the builder\n // can only run once the db exists — so wrap it and default afterwards.\n const withSecret = (cfg: BetterAuthConfig): BetterAuthConfig => ({\n ...cfg,\n secret: cfg.secret ?? resolvedEnv.AUTH_SECRET,\n })\n const authInput = options.auth\n return typeof authInput === 'function'\n ? (((ctx) =>\n withSecret(authInput(ctx as never))) satisfies AuthConfigFactory)\n : withSecret(authInput ?? {})\n })(),\n storage: resolveBuckets(options.storage, platformSource),\n realtime: parsed.realtime,\n }\n}\n\n/** Collapse the object/builder union once the db is up. */\nexport function resolveAuthConfig(\n auth: ResolvedConfig['auth'],\n ctx: { db: AnyDb; env: BaseEnv },\n): BetterAuthConfig {\n return typeof auth === 'function' ? auth(ctx) : auth\n}\n"]}
|
package/dist/cron.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../src/cron.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACxE,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/cron.js
ADDED
package/dist/cron.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.js","sourceRoot":"","sources":["../src/cron.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA","sourcesContent":["export { cronMatches, parseCron } from './jobs/cron'\nexport { floorSlot, slotsDue, CRON_PREFIX, SLOT_MS } from './jobs/slots'\nexport type { CatchUp } from './jobs/slots'\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AnyDb } from './dialect.js';
|
|
2
|
+
import type { RealtimeFacade } from './realtime/facade.js';
|
|
3
|
+
import { type AccessUser, type ResolvedAccess } from './access.js';
|
|
4
|
+
import { WorkerstackError, type ErrorCodeValue } from './errors.js';
|
|
5
|
+
import { type IdempotencyConfig } from './idempotency.js';
|
|
6
|
+
import { type ListParamsInput, type ListResult } from './list-query.js';
|
|
7
|
+
export interface CrudExecutionContext {
|
|
8
|
+
request: Request;
|
|
9
|
+
user: AccessUser | null;
|
|
10
|
+
session: {
|
|
11
|
+
activeOrganizationId: string | null;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare class CrudOperationError extends WorkerstackError {
|
|
15
|
+
readonly legacyCode: ErrorCodeValue;
|
|
16
|
+
constructor(status: number, legacyCode: ErrorCodeValue, message: string, details?: unknown);
|
|
17
|
+
}
|
|
18
|
+
export type CrudOperationsDeps<TSchema extends Record<string, unknown> = Record<string, unknown>> = {
|
|
19
|
+
schema: TSchema;
|
|
20
|
+
db: AnyDb;
|
|
21
|
+
access: ResolvedAccess;
|
|
22
|
+
idempotency?: boolean | IdempotencyConfig;
|
|
23
|
+
realtime?: RealtimeFacade<TSchema>;
|
|
24
|
+
};
|
|
25
|
+
export type CreateResult = {
|
|
26
|
+
type: 'created';
|
|
27
|
+
status: 201;
|
|
28
|
+
record: Record<string, unknown>;
|
|
29
|
+
} | {
|
|
30
|
+
type: 'replay';
|
|
31
|
+
status: number;
|
|
32
|
+
body: string;
|
|
33
|
+
record: Record<string, unknown>;
|
|
34
|
+
};
|
|
35
|
+
export declare function createCrudOperations<TSchema extends Record<string, unknown> = Record<string, unknown>>(deps: CrudOperationsDeps<TSchema>): {
|
|
36
|
+
list(tableName: string, params: ListParamsInput | undefined, ctx: CrudExecutionContext): Promise<ListResult<Record<string, unknown>>>;
|
|
37
|
+
get(tableName: string, rawId: string | number, ctx: CrudExecutionContext): Promise<Record<string, unknown>>;
|
|
38
|
+
create(tableName: string, body: unknown, rawBody: string | undefined, idempotencyKey: string | undefined, ctx: CrudExecutionContext): Promise<CreateResult>;
|
|
39
|
+
update(tableName: string, rawId: string | number, body: unknown, ctx: CrudExecutionContext): Promise<Record<string, unknown>>;
|
|
40
|
+
delete(tableName: string, rawId: string | number, ctx: CrudExecutionContext): Promise<void>;
|
|
41
|
+
};
|
|
42
|
+
export type CrudOperations = ReturnType<typeof createCrudOperations>;
|
|
43
|
+
//# sourceMappingURL=crud-operations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud-operations.d.ts","sourceRoot":"","sources":["../src/crud-operations.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvD,OAAO,EAML,KAAK,UAAU,EACf,KAAK,cAAc,EAGpB,MAAM,UAAU,CAAA;AACjB,OAAO,EACL,gBAAgB,EAIhB,KAAK,cAAc,EACpB,MAAM,UAAU,CAAA;AACjB,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,eAAe,CAAA;AACtB,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,cAAc,CAAA;AAIrB,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE;QAAE,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CACjD;AAiBD,qBAAa,kBAAmB,SAAQ,gBAAgB;IAGpD,QAAQ,CAAC,UAAU,EAAE,cAAc;gBADnC,MAAM,EAAE,MAAM,EACL,UAAU,EAAE,cAAc,EACnC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,OAAO;CAepB;AAED,MAAM,MAAM,kBAAkB,CAC5B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC/D;IACF,MAAM,EAAE,OAAO,CAAA;IACf,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EAAE,cAAc,CAAA;IACtB,WAAW,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAA;IACzC,QAAQ,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;CACnC,CAAA;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACjE;IACE,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC,CAAA;AAyCL,wBAAgB,oBAAoB,CAClC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,IAAI,EAAE,kBAAkB,CAAC,OAAO,CAAC;oBAuClB,MAAM,UACT,eAAe,GAAG,SAAS,OAC9B,oBAAoB,GACxB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;mBAqClC,MAAM,SACV,MAAM,GAAG,MAAM,OACjB,oBAAoB,GACxB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;sBAgCtB,MAAM,QACX,OAAO,WACJ,MAAM,GAAG,SAAS,kBACX,MAAM,GAAG,SAAS,OAC7B,oBAAoB,GACxB,OAAO,CAAC,YAAY,CAAC;sBAiGX,MAAM,SACV,MAAM,GAAG,MAAM,QAChB,OAAO,OACR,oBAAoB,GACxB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;sBAqEtB,MAAM,SACV,MAAM,GAAG,MAAM,OACjB,oBAAoB,GACxB,OAAO,CAAC,IAAI,CAAC;EAgCnB;AA2BD,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { eq, getTableColumns, getTableName, isTable, } from 'drizzle-orm';
|
|
2
|
+
import { checkAccess, rowMatchesScope, sanitizeWriteBody, stampScope, tableEntryForName, } from './access.js';
|
|
3
|
+
import { WorkerstackError, ErrorCode, ListQueryError, } from './errors.js';
|
|
4
|
+
import { lookupIdempotency, resolveIdempotencyConfig, storeIdempotency, } from './idempotency.js';
|
|
5
|
+
import { executeList, resolveListParams, } from './list-query.js';
|
|
6
|
+
import { consoleLogger } from './logging.js';
|
|
7
|
+
import { buildScopeWhere } from './scope.js';
|
|
8
|
+
function operationMetadata(request) {
|
|
9
|
+
const operationId = request.headers.get('x-workerstack-operation-id')?.trim();
|
|
10
|
+
return operationId ? { operationId } : {};
|
|
11
|
+
}
|
|
12
|
+
function errorCodeForStatus(status) {
|
|
13
|
+
if (status === 401)
|
|
14
|
+
return 'UNAUTHORIZED';
|
|
15
|
+
if (status === 403)
|
|
16
|
+
return 'FORBIDDEN';
|
|
17
|
+
if (status === 404)
|
|
18
|
+
return 'NOT_FOUND';
|
|
19
|
+
if (status === 409)
|
|
20
|
+
return 'CONFLICT';
|
|
21
|
+
if (status === 413)
|
|
22
|
+
return 'PAYLOAD_TOO_LARGE';
|
|
23
|
+
if (status === 429)
|
|
24
|
+
return 'TOO_MANY_REQUESTS';
|
|
25
|
+
return 'BAD_REQUEST';
|
|
26
|
+
}
|
|
27
|
+
export class CrudOperationError extends WorkerstackError {
|
|
28
|
+
legacyCode;
|
|
29
|
+
constructor(status, legacyCode, message, details) {
|
|
30
|
+
const code = errorCodeForStatus(status);
|
|
31
|
+
super(code, message, legacyCode === code
|
|
32
|
+
? details
|
|
33
|
+
: {
|
|
34
|
+
code: legacyCode,
|
|
35
|
+
...(details === undefined ? {} : { details }),
|
|
36
|
+
});
|
|
37
|
+
this.legacyCode = legacyCode;
|
|
38
|
+
this.name = 'CrudOperationError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isRecord(value) {
|
|
42
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
43
|
+
}
|
|
44
|
+
function isUniqueConstraintError(error) {
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
let current = error;
|
|
47
|
+
while (isRecord(current) && !seen.has(current)) {
|
|
48
|
+
seen.add(current);
|
|
49
|
+
const code = current['code'];
|
|
50
|
+
if (code === '23505' ||
|
|
51
|
+
code === 'SQLITE_CONSTRAINT_UNIQUE' ||
|
|
52
|
+
code === 'SQLITE_CONSTRAINT_PRIMARYKEY') {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
const message = current['message'];
|
|
56
|
+
if (typeof message === 'string' &&
|
|
57
|
+
(/duplicate key value violates unique constraint/i.test(message) ||
|
|
58
|
+
/unique constraint failed/i.test(message))) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
current = current['cause'];
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
function coerceId(rawId) {
|
|
66
|
+
if (typeof rawId === 'number')
|
|
67
|
+
return rawId;
|
|
68
|
+
return isNaN(Number(rawId)) ? rawId : Number(rawId);
|
|
69
|
+
}
|
|
70
|
+
export function createCrudOperations(deps) {
|
|
71
|
+
const { schema, db, access, realtime } = deps;
|
|
72
|
+
const idempotency = resolveIdempotencyConfig(deps.idempotency);
|
|
73
|
+
const scopeFor = (resolver, ctx) => (resolver ? resolver(ctx) : undefined);
|
|
74
|
+
function resolveTable(tableName) {
|
|
75
|
+
const table = Object.values(schema).find((t) => isTable(t) && getTableName(t) === tableName);
|
|
76
|
+
if (!table) {
|
|
77
|
+
throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found');
|
|
78
|
+
}
|
|
79
|
+
const tableAccess = tableEntryForName(access, tableName);
|
|
80
|
+
if (!tableAccess || !tableAccess.enabled) {
|
|
81
|
+
throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found');
|
|
82
|
+
}
|
|
83
|
+
const idCol = getTableColumns(table)['id'];
|
|
84
|
+
if (!idCol) {
|
|
85
|
+
throw new CrudOperationError(400, ErrorCode.BAD_REQUEST, `Table ${tableName} has no id column`);
|
|
86
|
+
}
|
|
87
|
+
return { table, tableAccess, idCol };
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
async list(tableName, params, ctx) {
|
|
91
|
+
const { table, tableAccess, idCol } = resolveTable(tableName);
|
|
92
|
+
const denied = await checkAccess(tableAccess.list, ctx, tableAccess.ownerColumn);
|
|
93
|
+
if (!denied.allowed) {
|
|
94
|
+
throw new CrudOperationError(denied.status === 401 ? 401 : 403, ErrorCode.FORBIDDEN, 'Forbidden');
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const resolved = resolveListParams(params ?? {}, tableAccess);
|
|
98
|
+
const scope = scopeFor(tableAccess.readScope, ctx);
|
|
99
|
+
const scopeWhere = scope ? buildScopeWhere(table, scope) : undefined;
|
|
100
|
+
return await executeList(db, table, tableAccess, resolved, idCol, scopeWhere);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (err instanceof ListQueryError) {
|
|
104
|
+
throw new CrudOperationError(400, err.code, err.message, err.details);
|
|
105
|
+
}
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
async get(tableName, rawId, ctx) {
|
|
110
|
+
const { table, tableAccess, idCol } = resolveTable(tableName);
|
|
111
|
+
const id = coerceId(rawId);
|
|
112
|
+
const rows = await db.select().from(table).where(eq(idCol, id));
|
|
113
|
+
if (!rows[0]) {
|
|
114
|
+
throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found');
|
|
115
|
+
}
|
|
116
|
+
const row = rows[0];
|
|
117
|
+
const denied = await checkAccess(tableAccess.get, { ...ctx, row }, tableAccess.ownerColumn);
|
|
118
|
+
if (!denied.allowed) {
|
|
119
|
+
throw new CrudOperationError(denied.status === 401 ? 401 : 403, ErrorCode.FORBIDDEN, 'Forbidden');
|
|
120
|
+
}
|
|
121
|
+
const scope = scopeFor(tableAccess.readScope, ctx);
|
|
122
|
+
if (scope && !rowMatchesScope(row, scope)) {
|
|
123
|
+
throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found');
|
|
124
|
+
}
|
|
125
|
+
return row;
|
|
126
|
+
},
|
|
127
|
+
async create(tableName, body, rawBody, idempotencyKey, ctx) {
|
|
128
|
+
const { table, tableAccess } = resolveTable(tableName);
|
|
129
|
+
const denied = await checkAccess(tableAccess.create, ctx, tableAccess.ownerColumn);
|
|
130
|
+
if (!denied.allowed) {
|
|
131
|
+
throw new CrudOperationError(denied.status === 401 ? 401 : 403, ErrorCode.FORBIDDEN, 'Forbidden');
|
|
132
|
+
}
|
|
133
|
+
if (!isRecord(body)) {
|
|
134
|
+
throw new CrudOperationError(400, ErrorCode.BAD_REQUEST, 'Invalid JSON body');
|
|
135
|
+
}
|
|
136
|
+
const trimmedKey = idempotencyKey?.trim();
|
|
137
|
+
const effectiveRawBody = rawBody ?? JSON.stringify(body);
|
|
138
|
+
if (idempotency && trimmedKey) {
|
|
139
|
+
const lookup = await lookupIdempotency(db, tableName, trimmedKey, effectiveRawBody);
|
|
140
|
+
if (lookup.type === 'conflict') {
|
|
141
|
+
throw new CrudOperationError(409, ErrorCode.IDEMPOTENCY_CONFLICT, 'Idempotency key reused with different body');
|
|
142
|
+
}
|
|
143
|
+
if (lookup.type === 'replay') {
|
|
144
|
+
return {
|
|
145
|
+
type: 'replay',
|
|
146
|
+
status: lookup.status,
|
|
147
|
+
body: lookup.response,
|
|
148
|
+
record: JSON.parse(lookup.response),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const values = sanitizeWriteBody(body, tableAccess, 'create', ctx.user?.id ?? null);
|
|
153
|
+
const scope = scopeFor(tableAccess.writeScope, { ...ctx, body });
|
|
154
|
+
const stamped = scope ? stampScope(values, scope) : values;
|
|
155
|
+
let rows;
|
|
156
|
+
try {
|
|
157
|
+
rows = await db.insert(table).values(stamped).returning();
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
if (isUniqueConstraintError(error)) {
|
|
161
|
+
throw new CrudOperationError(409, ErrorCode.CONFLICT, 'Record already exists');
|
|
162
|
+
}
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
const created = rows[0];
|
|
166
|
+
await broadcast(realtime, table, 'create', created, ctx.request);
|
|
167
|
+
if (idempotency && trimmedKey) {
|
|
168
|
+
await storeIdempotency(db, tableName, trimmedKey, effectiveRawBody, 201, created, idempotency);
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
type: 'created',
|
|
172
|
+
status: 201,
|
|
173
|
+
record: created,
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
async update(tableName, rawId, body, ctx) {
|
|
177
|
+
const { table, tableAccess, idCol } = resolveTable(tableName);
|
|
178
|
+
const id = coerceId(rawId);
|
|
179
|
+
const existing = await db.select().from(table).where(eq(idCol, id));
|
|
180
|
+
if (!existing[0]) {
|
|
181
|
+
throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found');
|
|
182
|
+
}
|
|
183
|
+
const existingRow = existing[0];
|
|
184
|
+
const readScope = scopeFor(tableAccess.readScope, ctx);
|
|
185
|
+
if (readScope && !rowMatchesScope(existingRow, readScope)) {
|
|
186
|
+
throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found');
|
|
187
|
+
}
|
|
188
|
+
const denied = await checkAccess(tableAccess.update, { ...ctx, row: existingRow }, tableAccess.ownerColumn);
|
|
189
|
+
if (!denied.allowed) {
|
|
190
|
+
throw new CrudOperationError(denied.status === 401 ? 401 : 403, ErrorCode.FORBIDDEN, 'Forbidden');
|
|
191
|
+
}
|
|
192
|
+
if (!isRecord(body)) {
|
|
193
|
+
throw new CrudOperationError(400, ErrorCode.BAD_REQUEST, 'Invalid JSON body');
|
|
194
|
+
}
|
|
195
|
+
const values = sanitizeWriteBody(body, tableAccess, 'update', ctx.user?.id ?? null);
|
|
196
|
+
if (Object.keys(values).length === 0) {
|
|
197
|
+
throw new CrudOperationError(400, ErrorCode.BAD_REQUEST, 'No writable fields to update');
|
|
198
|
+
}
|
|
199
|
+
const writeScope = scopeFor(tableAccess.writeScope, { ...ctx, body });
|
|
200
|
+
const stamped = writeScope ? stampScope(values, writeScope) : values;
|
|
201
|
+
const rows = await db
|
|
202
|
+
.update(table)
|
|
203
|
+
.set(stamped)
|
|
204
|
+
.where(eq(idCol, id))
|
|
205
|
+
.returning();
|
|
206
|
+
if (!rows[0]) {
|
|
207
|
+
throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found');
|
|
208
|
+
}
|
|
209
|
+
const updated = rows[0];
|
|
210
|
+
await broadcast(realtime, table, 'update', updated, ctx.request);
|
|
211
|
+
return updated;
|
|
212
|
+
},
|
|
213
|
+
async delete(tableName, rawId, ctx) {
|
|
214
|
+
const { table, tableAccess, idCol } = resolveTable(tableName);
|
|
215
|
+
const id = coerceId(rawId);
|
|
216
|
+
const existing = await db.select().from(table).where(eq(idCol, id));
|
|
217
|
+
if (!existing[0]) {
|
|
218
|
+
throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found');
|
|
219
|
+
}
|
|
220
|
+
const existingRow = existing[0];
|
|
221
|
+
const readScope = scopeFor(tableAccess.readScope, ctx);
|
|
222
|
+
if (readScope && !rowMatchesScope(existingRow, readScope)) {
|
|
223
|
+
throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found');
|
|
224
|
+
}
|
|
225
|
+
const denied = await checkAccess(tableAccess.delete, { ...ctx, row: existingRow }, tableAccess.ownerColumn);
|
|
226
|
+
if (!denied.allowed) {
|
|
227
|
+
throw new CrudOperationError(denied.status === 401 ? 401 : 403, ErrorCode.FORBIDDEN, 'Forbidden');
|
|
228
|
+
}
|
|
229
|
+
await db.delete(table).where(eq(idCol, id));
|
|
230
|
+
await broadcast(realtime, table, 'delete', existingRow, ctx.request);
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Broadcast-on-write, best effort: the write has committed, so a failed
|
|
236
|
+
* publish only logs. Awaited, because a Worker cancels I/O that is still
|
|
237
|
+
* pending after the response, and the event would never reach the hub.
|
|
238
|
+
*/
|
|
239
|
+
async function broadcast(realtime, table, action, record, request) {
|
|
240
|
+
if (!realtime)
|
|
241
|
+
return;
|
|
242
|
+
try {
|
|
243
|
+
await realtime.publish(table, action, record, operationMetadata(request));
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
consoleLogger.error('[workerstack] realtime publish failed:', error);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=crud-operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud-operations.js","sourceRoot":"","sources":["../src/crud-operations.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n eq,\n getTableColumns,\n getTableName,\n isTable,\n type Table,\n} from 'drizzle-orm'\n\nimport type { AnyDb } from './dialect'\nimport type { RealtimeFacade } from './realtime/facade'\n\nimport {\n checkAccess,\n rowMatchesScope,\n sanitizeWriteBody,\n stampScope,\n tableEntryForName,\n type AccessUser,\n type ResolvedAccess,\n type ScopeMap,\n type ScopeResolver,\n} from './access'\nimport {\n WorkerstackError,\n ErrorCode,\n ListQueryError,\n type WorkerstackErrorCode,\n type ErrorCodeValue,\n} from './errors'\nimport {\n lookupIdempotency,\n resolveIdempotencyConfig,\n storeIdempotency,\n type IdempotencyConfig,\n} from './idempotency'\nimport {\n executeList,\n resolveListParams,\n type ListParamsInput,\n type ListResult,\n} from './list-query'\nimport { consoleLogger } from './logging'\nimport { buildScopeWhere } from './scope'\n\nexport interface CrudExecutionContext {\n request: Request\n user: AccessUser | null\n session: { activeOrganizationId: string | null }\n}\n\nfunction operationMetadata(request: Request): { operationId?: string } {\n const operationId = request.headers.get('x-workerstack-operation-id')?.trim()\n return operationId ? { operationId } : {}\n}\n\nfunction errorCodeForStatus(status: number): WorkerstackErrorCode {\n if (status === 401) return 'UNAUTHORIZED'\n if (status === 403) return 'FORBIDDEN'\n if (status === 404) return 'NOT_FOUND'\n if (status === 409) return 'CONFLICT'\n if (status === 413) return 'PAYLOAD_TOO_LARGE'\n if (status === 429) return 'TOO_MANY_REQUESTS'\n return 'BAD_REQUEST'\n}\n\nexport class CrudOperationError extends WorkerstackError {\n constructor(\n status: number,\n readonly legacyCode: ErrorCodeValue,\n message: string,\n details?: unknown,\n ) {\n const code = errorCodeForStatus(status)\n super(\n code,\n message,\n legacyCode === code\n ? details\n : {\n code: legacyCode,\n ...(details === undefined ? {} : { details }),\n },\n )\n this.name = 'CrudOperationError'\n }\n}\n\nexport type CrudOperationsDeps<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n> = {\n schema: TSchema\n db: AnyDb\n access: ResolvedAccess\n idempotency?: boolean | IdempotencyConfig\n realtime?: RealtimeFacade<TSchema>\n}\n\nexport type CreateResult =\n | { type: 'created'; status: 201; record: Record<string, unknown> }\n | {\n type: 'replay'\n status: number\n body: string\n record: Record<string, unknown>\n }\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction isUniqueConstraintError(error: unknown): boolean {\n const seen = new Set<unknown>()\n let current = error\n\n while (isRecord(current) && !seen.has(current)) {\n seen.add(current)\n const code = current['code']\n if (\n code === '23505' ||\n code === 'SQLITE_CONSTRAINT_UNIQUE' ||\n code === 'SQLITE_CONSTRAINT_PRIMARYKEY'\n ) {\n return true\n }\n\n const message = current['message']\n if (\n typeof message === 'string' &&\n (/duplicate key value violates unique constraint/i.test(message) ||\n /unique constraint failed/i.test(message))\n ) {\n return true\n }\n\n current = current['cause']\n }\n\n return false\n}\n\nfunction coerceId(rawId: string | number): string | number {\n if (typeof rawId === 'number') return rawId\n return isNaN(Number(rawId)) ? rawId : Number(rawId)\n}\n\nexport function createCrudOperations<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n>(deps: CrudOperationsDeps<TSchema>) {\n const { schema, db, access, realtime } = deps\n const idempotency = resolveIdempotencyConfig(deps.idempotency)\n\n const scopeFor = (\n resolver: ScopeResolver | undefined,\n ctx: {\n user: AccessUser | null\n session: { activeOrganizationId: string | null }\n request: Request\n row?: Record<string, unknown>\n body?: Record<string, unknown>\n },\n ): ScopeMap | undefined => (resolver ? resolver(ctx) : undefined)\n\n function resolveTable(tableName: string) {\n const table = Object.values(schema).find(\n (t) => isTable(t) && getTableName(t) === tableName,\n ) as Table | undefined\n if (!table) {\n throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found')\n }\n const tableAccess = tableEntryForName(access, tableName)\n if (!tableAccess || !tableAccess.enabled) {\n throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found')\n }\n const idCol = getTableColumns(table)['id']\n if (!idCol) {\n throw new CrudOperationError(\n 400,\n ErrorCode.BAD_REQUEST,\n `Table ${tableName} has no id column`,\n )\n }\n return { table, tableAccess, idCol }\n }\n\n return {\n async list(\n tableName: string,\n params: ListParamsInput | undefined,\n ctx: CrudExecutionContext,\n ): Promise<ListResult<Record<string, unknown>>> {\n const { table, tableAccess, idCol } = resolveTable(tableName)\n\n const denied = await checkAccess(\n tableAccess.list,\n ctx,\n tableAccess.ownerColumn,\n )\n if (!denied.allowed) {\n throw new CrudOperationError(\n denied.status === 401 ? 401 : 403,\n ErrorCode.FORBIDDEN,\n 'Forbidden',\n )\n }\n\n try {\n const resolved = resolveListParams(params ?? {}, tableAccess)\n const scope = scopeFor(tableAccess.readScope, ctx)\n const scopeWhere = scope ? buildScopeWhere(table, scope) : undefined\n return await executeList(\n db,\n table,\n tableAccess,\n resolved,\n idCol,\n scopeWhere,\n )\n } catch (err) {\n if (err instanceof ListQueryError) {\n throw new CrudOperationError(400, err.code, err.message, err.details)\n }\n throw err\n }\n },\n\n async get(\n tableName: string,\n rawId: string | number,\n ctx: CrudExecutionContext,\n ): Promise<Record<string, unknown>> {\n const { table, tableAccess, idCol } = resolveTable(tableName)\n const id = coerceId(rawId)\n\n const rows = await db.select().from(table).where(eq(idCol, id))\n if (!rows[0]) {\n throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found')\n }\n const row = rows[0] as Record<string, unknown>\n\n const denied = await checkAccess(\n tableAccess.get,\n { ...ctx, row },\n tableAccess.ownerColumn,\n )\n if (!denied.allowed) {\n throw new CrudOperationError(\n denied.status === 401 ? 401 : 403,\n ErrorCode.FORBIDDEN,\n 'Forbidden',\n )\n }\n\n const scope = scopeFor(tableAccess.readScope, ctx)\n if (scope && !rowMatchesScope(row, scope)) {\n throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found')\n }\n\n return row\n },\n\n async create(\n tableName: string,\n body: unknown,\n rawBody: string | undefined,\n idempotencyKey: string | undefined,\n ctx: CrudExecutionContext,\n ): Promise<CreateResult> {\n const { table, tableAccess } = resolveTable(tableName)\n\n const denied = await checkAccess(\n tableAccess.create,\n ctx,\n tableAccess.ownerColumn,\n )\n if (!denied.allowed) {\n throw new CrudOperationError(\n denied.status === 401 ? 401 : 403,\n ErrorCode.FORBIDDEN,\n 'Forbidden',\n )\n }\n\n if (!isRecord(body)) {\n throw new CrudOperationError(\n 400,\n ErrorCode.BAD_REQUEST,\n 'Invalid JSON body',\n )\n }\n\n const trimmedKey = idempotencyKey?.trim()\n const effectiveRawBody = rawBody ?? JSON.stringify(body)\n\n if (idempotency && trimmedKey) {\n const lookup = await lookupIdempotency(\n db,\n tableName,\n trimmedKey,\n effectiveRawBody,\n )\n if (lookup.type === 'conflict') {\n throw new CrudOperationError(\n 409,\n ErrorCode.IDEMPOTENCY_CONFLICT,\n 'Idempotency key reused with different body',\n )\n }\n if (lookup.type === 'replay') {\n return {\n type: 'replay',\n status: lookup.status,\n body: lookup.response,\n record: JSON.parse(lookup.response) as Record<string, unknown>,\n }\n }\n }\n\n const values = sanitizeWriteBody(\n body,\n tableAccess,\n 'create',\n ctx.user?.id ?? null,\n )\n\n const scope = scopeFor(tableAccess.writeScope, { ...ctx, body })\n const stamped = scope ? stampScope(values, scope) : values\n\n let rows: Record<string, unknown>[]\n try {\n rows = await db.insert(table).values(stamped).returning()\n } catch (error) {\n if (isUniqueConstraintError(error)) {\n throw new CrudOperationError(\n 409,\n ErrorCode.CONFLICT,\n 'Record already exists',\n )\n }\n throw error\n }\n const created = rows[0] as Record<string, unknown>\n await broadcast(realtime, table, 'create', created, ctx.request)\n\n if (idempotency && trimmedKey) {\n await storeIdempotency(\n db,\n tableName,\n trimmedKey,\n effectiveRawBody,\n 201,\n created,\n idempotency,\n )\n }\n\n return {\n type: 'created',\n status: 201,\n record: created,\n }\n },\n\n async update(\n tableName: string,\n rawId: string | number,\n body: unknown,\n ctx: CrudExecutionContext,\n ): Promise<Record<string, unknown>> {\n const { table, tableAccess, idCol } = resolveTable(tableName)\n const id = coerceId(rawId)\n\n const existing = await db.select().from(table).where(eq(idCol, id))\n if (!existing[0]) {\n throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found')\n }\n const existingRow = existing[0] as Record<string, unknown>\n\n const readScope = scopeFor(tableAccess.readScope, ctx)\n if (readScope && !rowMatchesScope(existingRow, readScope)) {\n throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found')\n }\n\n const denied = await checkAccess(\n tableAccess.update,\n { ...ctx, row: existingRow },\n tableAccess.ownerColumn,\n )\n if (!denied.allowed) {\n throw new CrudOperationError(\n denied.status === 401 ? 401 : 403,\n ErrorCode.FORBIDDEN,\n 'Forbidden',\n )\n }\n\n if (!isRecord(body)) {\n throw new CrudOperationError(\n 400,\n ErrorCode.BAD_REQUEST,\n 'Invalid JSON body',\n )\n }\n\n const values = sanitizeWriteBody(\n body,\n tableAccess,\n 'update',\n ctx.user?.id ?? null,\n )\n\n if (Object.keys(values).length === 0) {\n throw new CrudOperationError(\n 400,\n ErrorCode.BAD_REQUEST,\n 'No writable fields to update',\n )\n }\n\n const writeScope = scopeFor(tableAccess.writeScope, { ...ctx, body })\n const stamped = writeScope ? stampScope(values, writeScope) : values\n\n const rows = await db\n .update(table)\n .set(stamped)\n .where(eq(idCol, id))\n .returning()\n\n if (!rows[0]) {\n throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found')\n }\n const updated = rows[0] as Record<string, unknown>\n await broadcast(realtime, table, 'update', updated, ctx.request)\n return updated\n },\n\n async delete(\n tableName: string,\n rawId: string | number,\n ctx: CrudExecutionContext,\n ): Promise<void> {\n const { table, tableAccess, idCol } = resolveTable(tableName)\n const id = coerceId(rawId)\n\n const existing = await db.select().from(table).where(eq(idCol, id))\n if (!existing[0]) {\n throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found')\n }\n const existingRow = existing[0] as Record<string, unknown>\n\n const readScope = scopeFor(tableAccess.readScope, ctx)\n if (readScope && !rowMatchesScope(existingRow, readScope)) {\n throw new CrudOperationError(404, ErrorCode.NOT_FOUND, 'Not found')\n }\n\n const denied = await checkAccess(\n tableAccess.delete,\n { ...ctx, row: existingRow },\n tableAccess.ownerColumn,\n )\n if (!denied.allowed) {\n throw new CrudOperationError(\n denied.status === 401 ? 401 : 403,\n ErrorCode.FORBIDDEN,\n 'Forbidden',\n )\n }\n\n await db.delete(table).where(eq(idCol, id))\n await broadcast(realtime, table, 'delete', existingRow, ctx.request)\n },\n }\n}\n\n/**\n * Broadcast-on-write, best effort: the write has committed, so a failed\n * publish only logs. Awaited, because a Worker cancels I/O that is still\n * pending after the response, and the event would never reach the hub.\n */\nasync function broadcast(\n realtime: RealtimeFacade<any> | undefined,\n table: unknown,\n action: 'create' | 'update' | 'delete',\n record: Record<string, unknown>,\n request: Request,\n): Promise<void> {\n if (!realtime) return\n try {\n await realtime.publish(\n table as never,\n action,\n record as never,\n operationMetadata(request),\n )\n } catch (error) {\n consoleLogger.error('[workerstack] realtime publish failed:', error)\n }\n}\n\nexport type CrudOperations = ReturnType<typeof createCrudOperations>\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DbFor, Driver } from '../db.js';
|
|
2
|
+
import type { AnyDb } from '../dialect.js';
|
|
3
|
+
export type DatabaseConnection = {
|
|
4
|
+
url: string;
|
|
5
|
+
authToken?: string;
|
|
6
|
+
};
|
|
7
|
+
export type TestDatabaseTarget = AsyncDisposable & {
|
|
8
|
+
readonly connection: DatabaseConnection;
|
|
9
|
+
};
|
|
10
|
+
export type TestDatabaseTargetOptions = {
|
|
11
|
+
mode: 'memory' | 'temporary';
|
|
12
|
+
};
|
|
13
|
+
export type TestDatabaseStrategy = {
|
|
14
|
+
createTarget(options: TestDatabaseTargetOptions): Promise<TestDatabaseTarget>;
|
|
15
|
+
};
|
|
16
|
+
export type DatabaseConnectionResult<TSchema extends Record<string, unknown>> = {
|
|
17
|
+
db: DbFor<TSchema>;
|
|
18
|
+
close?: () => void | Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
export type DatabaseAdapter = {
|
|
21
|
+
readonly driver: Driver;
|
|
22
|
+
connect<TSchema extends Record<string, unknown>>(schema: TSchema, connection: DatabaseConnection): Promise<DatabaseConnectionResult<TSchema>>;
|
|
23
|
+
migrate(db: AnyDb, migrationsFolder: string): Promise<void>;
|
|
24
|
+
testing?: TestDatabaseStrategy;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/database/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEvC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,eAAe,GAAG;IACjD,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAA;CACxC,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CAC9E,CAAA;AAED,MAAM,MAAM,wBAAwB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC1E;IACE,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACnC,CAAA;AAEH,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7C,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,kBAAkB,GAC7B,OAAO,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC,CAAA;IAC7C,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3D,OAAO,CAAC,EAAE,oBAAoB,CAAA;CAC/B,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/database/adapter.ts"],"names":[],"mappings":"","sourcesContent":["import type { DbFor, Driver } from '../db'\nimport type { AnyDb } from '../dialect'\n\nexport type DatabaseConnection = {\n url: string\n authToken?: string\n}\n\nexport type TestDatabaseTarget = AsyncDisposable & {\n readonly connection: DatabaseConnection\n}\n\nexport type TestDatabaseTargetOptions = {\n mode: 'memory' | 'temporary'\n}\n\nexport type TestDatabaseStrategy = {\n createTarget(options: TestDatabaseTargetOptions): Promise<TestDatabaseTarget>\n}\n\nexport type DatabaseConnectionResult<TSchema extends Record<string, unknown>> =\n {\n db: DbFor<TSchema>\n close?: () => void | Promise<void>\n }\n\nexport type DatabaseAdapter = {\n readonly driver: Driver\n connect<TSchema extends Record<string, unknown>>(\n schema: TSchema,\n connection: DatabaseConnection,\n ): Promise<DatabaseConnectionResult<TSchema>>\n migrate(db: AnyDb, migrationsFolder: string): Promise<void>\n testing?: TestDatabaseStrategy\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"libsql.d.ts","sourceRoot":"","sources":["../../src/database/libsql.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD,wBAAgB,MAAM,IAAI,eAAe,CA+BxC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { drizzle } from 'drizzle-orm/libsql';
|
|
2
|
+
import { migrate } from 'drizzle-orm/libsql/migrator';
|
|
3
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
export function libsql() {
|
|
7
|
+
return {
|
|
8
|
+
driver: 'libsql',
|
|
9
|
+
async connect(schema, connection) {
|
|
10
|
+
const db = drizzle({ connection, schema });
|
|
11
|
+
return { db: db, close: () => db.$client.close() };
|
|
12
|
+
},
|
|
13
|
+
async migrate(db, migrationsFolder) {
|
|
14
|
+
await migrate(db, { migrationsFolder });
|
|
15
|
+
},
|
|
16
|
+
testing: {
|
|
17
|
+
async createTarget({ mode }) {
|
|
18
|
+
if (mode === 'memory') {
|
|
19
|
+
return {
|
|
20
|
+
connection: { url: ':memory:' },
|
|
21
|
+
async [Symbol.asyncDispose]() { },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const directory = await mkdtemp(join(tmpdir(), 'workerstack-test-'));
|
|
25
|
+
let closed = false;
|
|
26
|
+
return {
|
|
27
|
+
connection: { url: `file:${join(directory, 'database.db')}` },
|
|
28
|
+
async [Symbol.asyncDispose]() {
|
|
29
|
+
if (closed)
|
|
30
|
+
return;
|
|
31
|
+
closed = true;
|
|
32
|
+
await rm(directory, { recursive: true, force: true });
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=libsql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"libsql.js","sourceRoot":"","sources":["../../src/database/libsql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAIhC,MAAM,UAAU,MAAM;IACpB,OAAO;QACL,MAAM,EAAE,QAAQ;QAChB,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU;YAC9B,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAA;YAC1C,OAAO,EAAE,EAAE,EAAE,EAAW,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAA;QAC7D,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,gBAAgB;YAChC,MAAM,OAAO,CAAC,EAAW,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAA;QAClD,CAAC;QACD,OAAO,EAAE;YACP,KAAK,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE;gBACzB,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACtB,OAAO;wBACL,UAAU,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE;wBAC/B,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,KAAI,CAAC;qBACjC,CAAA;gBACH,CAAC;gBACD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAA;gBACpE,IAAI,MAAM,GAAG,KAAK,CAAA;gBAClB,OAAO;oBACL,UAAU,EAAE,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,EAAE;oBAC7D,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;wBACzB,IAAI,MAAM;4BAAE,OAAM;wBAClB,MAAM,GAAG,IAAI,CAAA;wBACb,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;oBACvD,CAAC;iBACF,CAAA;YACH,CAAC;SACF;KACF,CAAA;AACH,CAAC","sourcesContent":["import { drizzle } from 'drizzle-orm/libsql'\nimport { migrate } from 'drizzle-orm/libsql/migrator'\nimport { mkdtemp, rm } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\n\nimport type { DatabaseAdapter } from './adapter'\n\nexport function libsql(): DatabaseAdapter {\n return {\n driver: 'libsql',\n async connect(schema, connection) {\n const db = drizzle({ connection, schema })\n return { db: db as never, close: () => db.$client.close() }\n },\n async migrate(db, migrationsFolder) {\n await migrate(db as never, { migrationsFolder })\n },\n testing: {\n async createTarget({ mode }) {\n if (mode === 'memory') {\n return {\n connection: { url: ':memory:' },\n async [Symbol.asyncDispose]() {},\n }\n }\n const directory = await mkdtemp(join(tmpdir(), 'workerstack-test-'))\n let closed = false\n return {\n connection: { url: `file:${join(directory, 'database.db')}` },\n async [Symbol.asyncDispose]() {\n if (closed) return\n closed = true\n await rm(directory, { recursive: true, force: true })\n },\n }\n },\n },\n }\n}\n"]}
|
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LibSQLDatabase } from 'drizzle-orm/libsql';
|
|
2
|
+
import type { DatabaseAdapter, DatabaseConnection, DatabaseConnectionResult } from './database/adapter.js';
|
|
3
|
+
export type Driver = 'libsql';
|
|
4
|
+
/** The public db type for a schema. */
|
|
5
|
+
export type DbFor<TSchema extends Record<string, unknown>> = LibSQLDatabase<TSchema>;
|
|
6
|
+
/** The database an application receives on `context.db` and `app.db`. */
|
|
7
|
+
export type WorkerstackDb<TSchema extends Record<string, unknown>> = DbFor<TSchema>;
|
|
8
|
+
/** The transaction handle inside `db.transaction(...)`. */
|
|
9
|
+
export type WorkerstackTx<TSchema extends Record<string, unknown>> = Parameters<Parameters<DbFor<TSchema>['transaction']>[0]>[0];
|
|
10
|
+
export declare function createDb<TSchema extends Record<string, unknown>>(schema: TSchema, cfg: DatabaseConnection & {
|
|
11
|
+
adapter: DatabaseAdapter;
|
|
12
|
+
}): Promise<DatabaseConnectionResult<TSchema> & {
|
|
13
|
+
driver: Driver;
|
|
14
|
+
}>;
|
|
15
|
+
//# sourceMappingURL=db.d.ts.map
|
package/dist/db.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACzB,MAAM,oBAAoB,CAAA;AAE3B,MAAM,MAAM,MAAM,GAAG,QAAQ,CAAA;AAE7B,uCAAuC;AACvC,MAAM,MAAM,KAAK,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACvD,cAAc,CAAC,OAAO,CAAC,CAAA;AAEzB,yEAAyE;AACzE,MAAM,MAAM,aAAa,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC/D,KAAK,CAAC,OAAO,CAAC,CAAA;AAEhB,2DAA2D;AAC3D,MAAM,MAAM,aAAa,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,UAAU,CAC7E,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAC7C,CAAC,CAAC,CAAC,CAAA;AAEJ,wBAAsB,QAAQ,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpE,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,kBAAkB,GAAG;IAAE,OAAO,EAAE,eAAe,CAAA;CAAE,GACrD,OAAO,CAAC,wBAAwB,CAAC,OAAO,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAMjE"}
|
package/dist/db.js
ADDED
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAuBA,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAe,EACf,GAAsD;IAEtD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE;QAC/C,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,SAAS,EAAE,GAAG,CAAC,SAAS;KACzB,CAAC,CAAA;IACF,OAAO,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAA;AAClD,CAAC","sourcesContent":["import type { LibSQLDatabase } from 'drizzle-orm/libsql'\n\nimport type {\n DatabaseAdapter,\n DatabaseConnection,\n DatabaseConnectionResult,\n} from './database/adapter'\n\nexport type Driver = 'libsql'\n\n/** The public db type for a schema. */\nexport type DbFor<TSchema extends Record<string, unknown>> =\n LibSQLDatabase<TSchema>\n\n/** The database an application receives on `context.db` and `app.db`. */\nexport type WorkerstackDb<TSchema extends Record<string, unknown>> =\n DbFor<TSchema>\n\n/** The transaction handle inside `db.transaction(...)`. */\nexport type WorkerstackTx<TSchema extends Record<string, unknown>> = Parameters<\n Parameters<DbFor<TSchema>['transaction']>[0]\n>[0]\n\nexport async function createDb<TSchema extends Record<string, unknown>>(\n schema: TSchema,\n cfg: DatabaseConnection & { adapter: DatabaseAdapter },\n): Promise<DatabaseConnectionResult<TSchema> & { driver: Driver }> {\n const result = await cfg.adapter.connect(schema, {\n url: cfg.url,\n authToken: cfg.authToken,\n })\n return { ...result, driver: cfg.adapter.driver }\n}\n"]}
|