@rikalabs/akter 0.1.0-alpha.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 +15 -0
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +116 -0
- package/dist/actor/codecs.d.ts +77 -0
- package/dist/actor/codecs.js +87 -0
- package/dist/actor/connections.d.ts +34 -0
- package/dist/actor/connections.js +145 -0
- package/dist/actor/definition.d.ts +893 -0
- package/dist/actor/definition.js +147 -0
- package/dist/actor/descriptor.d.ts +172 -0
- package/dist/actor/descriptor.js +367 -0
- package/dist/actor/handles.d.ts +77 -0
- package/dist/actor/handles.js +265 -0
- package/dist/actor/jobs.d.ts +10 -0
- package/dist/actor/jobs.js +137 -0
- package/dist/actor/layers.d.ts +41 -0
- package/dist/actor/layers.js +177 -0
- package/dist/actor/reads.d.ts +47 -0
- package/dist/actor/reads.js +224 -0
- package/dist/actor/served.d.ts +120 -0
- package/dist/actor/served.js +72 -0
- package/dist/actor/turns.d.ts +13 -0
- package/dist/actor/turns.js +332 -0
- package/dist/actor/workflows.d.ts +16 -0
- package/dist/actor/workflows.js +37 -0
- package/dist/client/calls.d.ts +47 -0
- package/dist/client/calls.js +425 -0
- package/dist/client/clock.d.ts +56 -0
- package/dist/client/clock.js +119 -0
- package/dist/client/fleet-page.d.ts +15 -0
- package/dist/client/fleet-page.js +13 -0
- package/dist/client/fleet.d.ts +29 -0
- package/dist/client/fleet.js +51 -0
- package/dist/client/index.d.ts +17 -0
- package/dist/client/index.js +6 -0
- package/dist/client/make.d.ts +157 -0
- package/dist/client/make.js +221 -0
- package/dist/client/offline/queue.d.ts +118 -0
- package/dist/client/offline/queue.js +292 -0
- package/dist/client/offline/store.d.ts +74 -0
- package/dist/client/offline/store.js +78 -0
- package/dist/client/optimistic.d.ts +57 -0
- package/dist/client/optimistic.js +136 -0
- package/dist/client/report.d.ts +2 -0
- package/dist/client/report.js +8 -0
- package/dist/client/sessions/connection.d.ts +118 -0
- package/dist/client/sessions/connection.js +162 -0
- package/dist/client/sessions/feed.d.ts +42 -0
- package/dist/client/sessions/feed.js +43 -0
- package/dist/client/sessions/sse.d.ts +47 -0
- package/dist/client/sessions/sse.js +112 -0
- package/dist/client/sessions/stream.d.ts +28 -0
- package/dist/client/sessions/stream.js +35 -0
- package/dist/client/sessions/watch.d.ts +34 -0
- package/dist/client/sessions/watch.js +40 -0
- package/dist/client/transport.d.ts +37 -0
- package/dist/client/transport.js +66 -0
- package/dist/contexts/command.d.ts +132 -0
- package/dist/contexts/command.js +20 -0
- package/dist/contexts/connection.d.ts +68 -0
- package/dist/contexts/connection.js +0 -0
- package/dist/contexts/job.d.ts +49 -0
- package/dist/contexts/job.js +0 -0
- package/dist/contexts/workflow.d.ts +147 -0
- package/dist/contexts/workflow.js +22 -0
- package/dist/errors/actor.d.ts +200 -0
- package/dist/errors/actor.js +271 -0
- package/dist/errors/content.d.ts +19 -0
- package/dist/errors/content.js +14 -0
- package/dist/errors/database.d.ts +22 -0
- package/dist/errors/database.js +23 -0
- package/dist/errors/events.d.ts +17 -0
- package/dist/errors/events.js +14 -0
- package/dist/errors/subscription.d.ts +12 -0
- package/dist/errors/subscription.js +8 -0
- package/dist/errors/workflow.d.ts +28 -0
- package/dist/errors/workflow.js +17 -0
- package/dist/handles/actors.d.ts +17 -0
- package/dist/handles/actors.js +7 -0
- package/dist/handles/content.d.ts +59 -0
- package/dist/handles/content.js +36 -0
- package/dist/handles/intents.d.ts +164 -0
- package/dist/handles/intents.js +214 -0
- package/dist/handles/run.d.ts +31 -0
- package/dist/handles/run.js +21 -0
- package/dist/handles/workflow.d.ts +27 -0
- package/dist/handles/workflow.js +23 -0
- package/dist/identity/caller.d.ts +105 -0
- package/dist/identity/caller.js +68 -0
- package/dist/identity/child.d.ts +18 -0
- package/dist/identity/child.js +39 -0
- package/dist/identity/command.d.ts +19 -0
- package/dist/identity/command.js +21 -0
- package/dist/identity/connection.d.ts +24 -0
- package/dist/identity/connection.js +29 -0
- package/dist/identity/content.d.ts +18 -0
- package/dist/identity/content.js +12 -0
- package/dist/identity/digest.d.ts +4 -0
- package/dist/identity/digest.js +5 -0
- package/dist/identity/execution.d.ts +32 -0
- package/dist/identity/execution.js +63 -0
- package/dist/identity/mint.d.ts +51 -0
- package/dist/identity/mint.js +76 -0
- package/dist/identity/version.d.ts +2 -0
- package/dist/identity/version.js +3 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.js +97 -0
- package/dist/members/blob.d.ts +43 -0
- package/dist/members/blob.js +29 -0
- package/dist/members/command.d.ts +109 -0
- package/dist/members/command.js +61 -0
- package/dist/members/connection.d.ts +55 -0
- package/dist/members/connection.js +49 -0
- package/dist/members/event.d.ts +36 -0
- package/dist/members/event.js +24 -0
- package/dist/members/job.d.ts +157 -0
- package/dist/members/job.js +67 -0
- package/dist/members/payload.d.ts +93 -0
- package/dist/members/payload.js +145 -0
- package/dist/members/reducer.d.ts +64 -0
- package/dist/members/reducer.js +27 -0
- package/dist/members/stream.d.ts +36 -0
- package/dist/members/stream.js +14 -0
- package/dist/members/subscription.d.ts +182 -0
- package/dist/members/subscription.js +91 -0
- package/dist/members/workflow.d.ts +99 -0
- package/dist/members/workflow.js +2 -0
- package/dist/policies/access.d.ts +35 -0
- package/dist/policies/access.js +6 -0
- package/dist/policies/command.d.ts +127 -0
- package/dist/policies/command.js +45 -0
- package/dist/policies/job.d.ts +28 -0
- package/dist/policies/job.js +60 -0
- package/dist/policies/schedules.d.ts +33 -0
- package/dist/policies/schedules.js +131 -0
- package/dist/protocol/frames.d.ts +66 -0
- package/dist/protocol/frames.js +55 -0
- package/dist/protocol/inspection.d.ts +807 -0
- package/dist/protocol/inspection.js +243 -0
- package/dist/protocol/wire.d.ts +184 -0
- package/dist/protocol/wire.js +150 -0
- package/dist/runtime/actors.d.ts +155 -0
- package/dist/runtime/actors.js +4 -0
- package/dist/runtime/admission.d.ts +34 -0
- package/dist/runtime/admission.js +72 -0
- package/dist/runtime/adoption/backfill.d.ts +27 -0
- package/dist/runtime/adoption/backfill.js +144 -0
- package/dist/runtime/adoption/enforce.d.ts +55 -0
- package/dist/runtime/adoption/enforce.js +241 -0
- package/dist/runtime/adoption/observe.d.ts +43 -0
- package/dist/runtime/adoption/observe.js +139 -0
- package/dist/runtime/adoption/plan.d.ts +45 -0
- package/dist/runtime/adoption/plan.js +192 -0
- package/dist/runtime/adoption/startup.d.ts +32 -0
- package/dist/runtime/adoption/startup.js +76 -0
- package/dist/runtime/adoption/status.d.ts +23 -0
- package/dist/runtime/adoption/status.js +47 -0
- package/dist/runtime/adoption/target.d.ts +33 -0
- package/dist/runtime/adoption/target.js +49 -0
- package/dist/runtime/connections/holder.d.ts +221 -0
- package/dist/runtime/connections/holder.js +816 -0
- package/dist/runtime/connections/owner.d.ts +263 -0
- package/dist/runtime/connections/owner.js +896 -0
- package/dist/runtime/connections/protocol.d.ts +441 -0
- package/dist/runtime/connections/protocol.js +222 -0
- package/dist/runtime/connections/reads.d.ts +24 -0
- package/dist/runtime/connections/reads.js +19 -0
- package/dist/runtime/connections/streams.d.ts +62 -0
- package/dist/runtime/connections/streams.js +128 -0
- package/dist/runtime/connections/transport.d.ts +40 -0
- package/dist/runtime/connections/transport.js +68 -0
- package/dist/runtime/connections/watch.d.ts +52 -0
- package/dist/runtime/connections/watch.js +159 -0
- package/dist/runtime/content/grant.d.ts +34 -0
- package/dist/runtime/content/grant.js +58 -0
- package/dist/runtime/content/store.d.ts +63 -0
- package/dist/runtime/content/store.js +294 -0
- package/dist/runtime/cron/key.d.ts +2 -0
- package/dist/runtime/cron/key.js +2 -0
- package/dist/runtime/cron/schedule.d.ts +69 -0
- package/dist/runtime/cron/schedule.js +144 -0
- package/dist/runtime/database/bounded.d.ts +43 -0
- package/dist/runtime/database/bounded.js +108 -0
- package/dist/runtime/database/connection.d.ts +8 -0
- package/dist/runtime/database/connection.js +18 -0
- package/dist/runtime/database/coordination.d.ts +19 -0
- package/dist/runtime/database/coordination.js +72 -0
- package/dist/runtime/database/flock.d.ts +10 -0
- package/dist/runtime/database/flock.js +60 -0
- package/dist/runtime/database/gate.d.ts +26 -0
- package/dist/runtime/database/gate.js +54 -0
- package/dist/runtime/database/keepalive.d.ts +11 -0
- package/dist/runtime/database/keepalive.js +22 -0
- package/dist/runtime/database/migrations.d.ts +177 -0
- package/dist/runtime/database/migrations.js +1018 -0
- package/dist/runtime/database/neki/migrations.d.ts +31 -0
- package/dist/runtime/database/neki/migrations.js +276 -0
- package/dist/runtime/database/neki/session.d.ts +23 -0
- package/dist/runtime/database/neki/session.js +31 -0
- package/dist/runtime/database/pglite.d.ts +21 -0
- package/dist/runtime/database/pglite.js +74 -0
- package/dist/runtime/database/replica.d.ts +41 -0
- package/dist/runtime/database/replica.js +51 -0
- package/dist/runtime/database/shards.d.ts +21 -0
- package/dist/runtime/database/shards.js +37 -0
- package/dist/runtime/database/snapshot.d.ts +7 -0
- package/dist/runtime/database/snapshot.js +7 -0
- package/dist/runtime/database/tenancy.d.ts +69 -0
- package/dist/runtime/database/tenancy.js +145 -0
- package/dist/runtime/database/transient.d.ts +10 -0
- package/dist/runtime/database/transient.js +25 -0
- package/dist/runtime/drain.d.ts +132 -0
- package/dist/runtime/drain.js +109 -0
- package/dist/runtime/entity/mailbox.d.ts +86 -0
- package/dist/runtime/entity/mailbox.js +121 -0
- package/dist/runtime/entity/register.d.ts +424 -0
- package/dist/runtime/entity/register.js +669 -0
- package/dist/runtime/events/append.d.ts +192 -0
- package/dist/runtime/events/append.js +128 -0
- package/dist/runtime/events/cursor.d.ts +2 -0
- package/dist/runtime/events/cursor.js +5 -0
- package/dist/runtime/events/replay.d.ts +20 -0
- package/dist/runtime/events/replay.js +45 -0
- package/dist/runtime/feeds.d.ts +26 -0
- package/dist/runtime/feeds.js +100 -0
- package/dist/runtime/fleet/checks.d.ts +17 -0
- package/dist/runtime/fleet/checks.js +106 -0
- package/dist/runtime/fleet/maintainer.d.ts +38 -0
- package/dist/runtime/fleet/maintainer.js +383 -0
- package/dist/runtime/fleet/pgoutput.d.ts +41 -0
- package/dist/runtime/fleet/pgoutput.js +102 -0
- package/dist/runtime/fleet/setup.d.ts +36 -0
- package/dist/runtime/fleet/setup.js +58 -0
- package/dist/runtime/fleet/subscribe.d.ts +42 -0
- package/dist/runtime/fleet/subscribe.js +147 -0
- package/dist/runtime/index.d.ts +66 -0
- package/dist/runtime/index.js +37 -0
- package/dist/runtime/inspector/http.d.ts +19 -0
- package/dist/runtime/inspector/http.js +36 -0
- package/dist/runtime/inspector/layer.d.ts +46 -0
- package/dist/runtime/inspector/layer.js +180 -0
- package/dist/runtime/inspector/queries.d.ts +711 -0
- package/dist/runtime/inspector/queries.js +494 -0
- package/dist/runtime/jobs/attempt.d.ts +254 -0
- package/dist/runtime/jobs/attempt.js +413 -0
- package/dist/runtime/jobs/isolation.d.ts +20 -0
- package/dist/runtime/jobs/isolation.js +10 -0
- package/dist/runtime/jobs/progress.d.ts +95 -0
- package/dist/runtime/jobs/progress.js +144 -0
- package/dist/runtime/layer.d.ts +260 -0
- package/dist/runtime/layer.js +876 -0
- package/dist/runtime/members.d.ts +381 -0
- package/dist/runtime/members.js +8 -0
- package/dist/runtime/operators/audit.d.ts +64 -0
- package/dist/runtime/operators/audit.js +56 -0
- package/dist/runtime/operators/auth.d.ts +29 -0
- package/dist/runtime/operators/auth.js +42 -0
- package/dist/runtime/operators/export.d.ts +68 -0
- package/dist/runtime/operators/export.js +137 -0
- package/dist/runtime/operators/grants.d.ts +45 -0
- package/dist/runtime/operators/grants.js +40 -0
- package/dist/runtime/operators/repair.d.ts +206 -0
- package/dist/runtime/operators/repair.js +226 -0
- package/dist/runtime/operators/routes.d.ts +35 -0
- package/dist/runtime/operators/routes.js +190 -0
- package/dist/runtime/operators/seed.d.ts +123 -0
- package/dist/runtime/operators/seed.js +123 -0
- package/dist/runtime/payloads/versions.d.ts +65 -0
- package/dist/runtime/payloads/versions.js +240 -0
- package/dist/runtime/peering/authority.d.ts +42 -0
- package/dist/runtime/peering/authority.js +132 -0
- package/dist/runtime/peering/credentials.d.ts +21 -0
- package/dist/runtime/peering/credentials.js +13 -0
- package/dist/runtime/peering/transport.d.ts +51 -0
- package/dist/runtime/peering/transport.js +175 -0
- package/dist/runtime/queries.d.ts +18 -0
- package/dist/runtime/queries.js +169 -0
- package/dist/runtime/readiness.d.ts +36 -0
- package/dist/runtime/readiness.js +44 -0
- package/dist/runtime/registration.d.ts +58 -0
- package/dist/runtime/registration.js +186 -0
- package/dist/runtime/request.d.ts +158 -0
- package/dist/runtime/request.js +102 -0
- package/dist/runtime/retry.d.ts +8 -0
- package/dist/runtime/retry.js +11 -0
- package/dist/runtime/runner.d.ts +93 -0
- package/dist/runtime/runner.js +122 -0
- package/dist/runtime/storage/codec.d.ts +39 -0
- package/dist/runtime/storage/codec.js +41 -0
- package/dist/runtime/storage/generation.d.ts +69 -0
- package/dist/runtime/storage/generation.js +49 -0
- package/dist/runtime/storage/placements.d.ts +15 -0
- package/dist/runtime/storage/placements.js +38 -0
- package/dist/runtime/storage/platform.d.ts +6 -0
- package/dist/runtime/storage/platform.js +9 -0
- package/dist/runtime/storage/retention.d.ts +73 -0
- package/dist/runtime/storage/retention.js +158 -0
- package/dist/runtime/subscriptions/identity.d.ts +31 -0
- package/dist/runtime/subscriptions/identity.js +45 -0
- package/dist/runtime/subscriptions/relay.d.ts +175 -0
- package/dist/runtime/subscriptions/relay.js +763 -0
- package/dist/runtime/subscriptions/storage.d.ts +40 -0
- package/dist/runtime/subscriptions/storage.js +98 -0
- package/dist/runtime/sweeps.d.ts +39 -0
- package/dist/runtime/sweeps.js +54 -0
- package/dist/runtime/telemetry/defects.d.ts +57 -0
- package/dist/runtime/telemetry/defects.js +43 -0
- package/dist/runtime/telemetry/metrics.d.ts +52 -0
- package/dist/runtime/telemetry/metrics.js +138 -0
- package/dist/runtime/telemetry/routes.d.ts +19 -0
- package/dist/runtime/telemetry/routes.js +18 -0
- package/dist/runtime/telemetry/sampler.d.ts +26 -0
- package/dist/runtime/telemetry/sampler.js +101 -0
- package/dist/runtime/telemetry/spans.d.ts +38 -0
- package/dist/runtime/telemetry/spans.js +44 -0
- package/dist/runtime/telemetry/usage.d.ts +52 -0
- package/dist/runtime/telemetry/usage.js +30 -0
- package/dist/runtime/topology/admission.d.ts +13 -0
- package/dist/runtime/topology/admission.js +36 -0
- package/dist/runtime/topology/locks.d.ts +56 -0
- package/dist/runtime/topology/locks.js +81 -0
- package/dist/runtime/topology/messages.d.ts +15 -0
- package/dist/runtime/topology/messages.js +32 -0
- package/dist/runtime/turn/admission.d.ts +22 -0
- package/dist/runtime/turn/admission.js +41 -0
- package/dist/runtime/turn/blobs.d.ts +38 -0
- package/dist/runtime/turn/blobs.js +215 -0
- package/dist/runtime/turn/execute.d.ts +208 -0
- package/dist/runtime/turn/execute.js +972 -0
- package/dist/runtime/turn/group.d.ts +165 -0
- package/dist/runtime/turn/group.js +326 -0
- package/dist/runtime/turn/hooks.d.ts +44 -0
- package/dist/runtime/turn/hooks.js +28 -0
- package/dist/runtime/turn/outbox.d.ts +159 -0
- package/dist/runtime/turn/outbox.js +269 -0
- package/dist/runtime/turn/pipeline.d.ts +68 -0
- package/dist/runtime/turn/pipeline.js +83 -0
- package/dist/runtime/turn/receipt.d.ts +81 -0
- package/dist/runtime/turn/receipt.js +32 -0
- package/dist/runtime/turn/relay.d.ts +179 -0
- package/dist/runtime/turn/relay.js +685 -0
- package/dist/runtime/turn/rows.d.ts +43 -0
- package/dist/runtime/turn/rows.js +592 -0
- package/dist/runtime/workflows/compatibility.d.ts +125 -0
- package/dist/runtime/workflows/compatibility.js +285 -0
- package/dist/runtime/workflows/engine.d.ts +107 -0
- package/dist/runtime/workflows/engine.js +826 -0
- package/dist/runtime/workflows/manifest.d.ts +34 -0
- package/dist/runtime/workflows/manifest.js +39 -0
- package/dist/runtime/workflows/steps.d.ts +33 -0
- package/dist/runtime/workflows/steps.js +152 -0
- package/dist/serve/api.d.ts +99 -0
- package/dist/serve/api.js +403 -0
- package/dist/serve/assertion/binding.d.ts +41 -0
- package/dist/serve/assertion/binding.js +92 -0
- package/dist/serve/assertion/verify.d.ts +93 -0
- package/dist/serve/assertion/verify.js +189 -0
- package/dist/serve/auth.d.ts +200 -0
- package/dist/serve/auth.js +92 -0
- package/dist/serve/clock.d.ts +10 -0
- package/dist/serve/clock.js +25 -0
- package/dist/serve/jwt.d.ts +65 -0
- package/dist/serve/jwt.js +202 -0
- package/dist/serve/layer.d.ts +98 -0
- package/dist/serve/layer.js +809 -0
- package/dist/serve/mcp/endpoint.d.ts +39 -0
- package/dist/serve/mcp/endpoint.js +187 -0
- package/dist/serve/mcp/tools.d.ts +37 -0
- package/dist/serve/mcp/tools.js +125 -0
- package/dist/serve/origin.d.ts +6 -0
- package/dist/serve/origin.js +21 -0
- package/dist/serve/sessions/feed.d.ts +79 -0
- package/dist/serve/sessions/feed.js +115 -0
- package/dist/serve/sessions/socket.d.ts +56 -0
- package/dist/serve/sessions/socket.js +212 -0
- package/dist/serve/sessions/sse.d.ts +27 -0
- package/dist/serve/sessions/sse.js +26 -0
- package/dist/serve/sessions/stream.d.ts +9 -0
- package/dist/serve/sessions/stream.js +9 -0
- package/dist/serve/sessions/watch.d.ts +12 -0
- package/dist/serve/sessions/watch.js +11 -0
- package/dist/serve/wire.d.ts +16 -0
- package/dist/serve/wire.js +50 -0
- package/dist/state/blob.d.ts +71 -0
- package/dist/state/blob.js +0 -0
- package/dist/state/migration.d.ts +53 -0
- package/dist/state/migration.js +29 -0
- package/dist/tables/fleet.d.ts +170 -0
- package/dist/tables/fleet.js +214 -0
- package/dist/tables/owned.d.ts +250 -0
- package/dist/tables/owned.js +233 -0
- package/dist/testing/actor-test.d.ts +360 -0
- package/dist/testing/actor-test.js +337 -0
- package/dist/testing/cluster.d.ts +93 -0
- package/dist/testing/cluster.js +296 -0
- package/dist/testing/conformance/access.d.ts +5 -0
- package/dist/testing/conformance/access.js +531 -0
- package/dist/testing/conformance/admission.d.ts +11 -0
- package/dist/testing/conformance/admission.js +182 -0
- package/dist/testing/conformance/adoption.d.ts +2 -0
- package/dist/testing/conformance/adoption.js +1019 -0
- package/dist/testing/conformance/assertions.d.ts +202 -0
- package/dist/testing/conformance/assertions.js +849 -0
- package/dist/testing/conformance/batches.d.ts +41 -0
- package/dist/testing/conformance/batches.js +388 -0
- package/dist/testing/conformance/blobs.d.ts +14 -0
- package/dist/testing/conformance/blobs.js +405 -0
- package/dist/testing/conformance/capacity.d.ts +8 -0
- package/dist/testing/conformance/capacity.js +433 -0
- package/dist/testing/conformance/client.d.ts +5 -0
- package/dist/testing/conformance/client.js +719 -0
- package/dist/testing/conformance/cold-serve.d.ts +16 -0
- package/dist/testing/conformance/cold-serve.js +254 -0
- package/dist/testing/conformance/connections/actors.d.ts +1143 -0
- package/dist/testing/conformance/connections/actors.js +180 -0
- package/dist/testing/conformance/connections/harness.d.ts +1754 -0
- package/dist/testing/conformance/connections/harness.js +142 -0
- package/dist/testing/conformance/connections/holders.d.ts +4 -0
- package/dist/testing/conformance/connections/holders.js +165 -0
- package/dist/testing/conformance/connections/lifecycle.d.ts +4 -0
- package/dist/testing/conformance/connections/lifecycle.js +113 -0
- package/dist/testing/conformance/connections/limits.d.ts +4 -0
- package/dist/testing/conformance/connections/limits.js +127 -0
- package/dist/testing/conformance/connections/reauthorization.d.ts +4 -0
- package/dist/testing/conformance/connections/reauthorization.js +170 -0
- package/dist/testing/conformance/connections/resync.d.ts +4 -0
- package/dist/testing/conformance/connections/resync.js +360 -0
- package/dist/testing/conformance/connections/takeover.d.ts +4 -0
- package/dist/testing/conformance/connections/takeover.js +287 -0
- package/dist/testing/conformance/connections.d.ts +4 -0
- package/dist/testing/conformance/connections.js +15 -0
- package/dist/testing/conformance/content-blobs.d.ts +16 -0
- package/dist/testing/conformance/content-blobs.js +534 -0
- package/dist/testing/conformance/counter.d.ts +16 -0
- package/dist/testing/conformance/counter.js +813 -0
- package/dist/testing/conformance/cron.d.ts +5 -0
- package/dist/testing/conformance/cron.js +665 -0
- package/dist/testing/conformance/drain.d.ts +26 -0
- package/dist/testing/conformance/drain.js +540 -0
- package/dist/testing/conformance/effect-control.d.ts +38 -0
- package/dist/testing/conformance/effect-control.js +1059 -0
- package/dist/testing/conformance/effects.d.ts +23 -0
- package/dist/testing/conformance/effects.js +432 -0
- package/dist/testing/conformance/events.d.ts +28 -0
- package/dist/testing/conformance/events.js +335 -0
- package/dist/testing/conformance/export.d.ts +2 -0
- package/dist/testing/conformance/export.js +368 -0
- package/dist/testing/conformance/fleet.d.ts +444 -0
- package/dist/testing/conformance/fleet.js +1009 -0
- package/dist/testing/conformance/groups.d.ts +7 -0
- package/dist/testing/conformance/groups.js +353 -0
- package/dist/testing/conformance/heap.d.ts +3 -0
- package/dist/testing/conformance/heap.js +149 -0
- package/dist/testing/conformance/http.d.ts +2809 -0
- package/dist/testing/conformance/http.js +1234 -0
- package/dist/testing/conformance/inspection-views.d.ts +8 -0
- package/dist/testing/conformance/inspection-views.js +272 -0
- package/dist/testing/conformance/inspector.d.ts +7 -0
- package/dist/testing/conformance/inspector.js +650 -0
- package/dist/testing/conformance/mint.d.ts +58 -0
- package/dist/testing/conformance/mint.js +692 -0
- package/dist/testing/conformance/multi-runner.d.ts +8 -0
- package/dist/testing/conformance/multi-runner.js +403 -0
- package/dist/testing/conformance/neki/plan.d.ts +19 -0
- package/dist/testing/conformance/neki/plan.js +30 -0
- package/dist/testing/conformance/observability.d.ts +3 -0
- package/dist/testing/conformance/observability.js +343 -0
- package/dist/testing/conformance/offline.d.ts +4 -0
- package/dist/testing/conformance/offline.js +474 -0
- package/dist/testing/conformance/operator-harness.d.ts +57 -0
- package/dist/testing/conformance/operator-harness.js +78 -0
- package/dist/testing/conformance/operator.d.ts +3 -0
- package/dist/testing/conformance/operator.js +379 -0
- package/dist/testing/conformance/outbox-cross-shard.d.ts +14 -0
- package/dist/testing/conformance/outbox-cross-shard.js +160 -0
- package/dist/testing/conformance/outbox.d.ts +36 -0
- package/dist/testing/conformance/outbox.js +437 -0
- package/dist/testing/conformance/payload-migrations.d.ts +3 -0
- package/dist/testing/conformance/payload-migrations.js +612 -0
- package/dist/testing/conformance/pipeline.d.ts +3 -0
- package/dist/testing/conformance/pipeline.js +1040 -0
- package/dist/testing/conformance/placement.d.ts +22 -0
- package/dist/testing/conformance/placement.js +539 -0
- package/dist/testing/conformance/platform.d.ts +40 -0
- package/dist/testing/conformance/platform.js +101 -0
- package/dist/testing/conformance/progress.d.ts +39 -0
- package/dist/testing/conformance/progress.js +666 -0
- package/dist/testing/conformance/properties.d.ts +6 -0
- package/dist/testing/conformance/properties.js +295 -0
- package/dist/testing/conformance/protocols.d.ts +4 -0
- package/dist/testing/conformance/protocols.js +618 -0
- package/dist/testing/conformance/provider/advertise.d.ts +21 -0
- package/dist/testing/conformance/provider/advertise.js +44 -0
- package/dist/testing/conformance/read-your-writes.d.ts +20 -0
- package/dist/testing/conformance/read-your-writes.js +192 -0
- package/dist/testing/conformance/reducers.d.ts +8 -0
- package/dist/testing/conformance/reducers.js +258 -0
- package/dist/testing/conformance/relay.d.ts +42 -0
- package/dist/testing/conformance/relay.js +1115 -0
- package/dist/testing/conformance/restore.d.ts +43 -0
- package/dist/testing/conformance/restore.js +341 -0
- package/dist/testing/conformance/retention.d.ts +14 -0
- package/dist/testing/conformance/retention.js +463 -0
- package/dist/testing/conformance/rls.d.ts +24 -0
- package/dist/testing/conformance/rls.js +576 -0
- package/dist/testing/conformance/simulation.d.ts +31 -0
- package/dist/testing/conformance/simulation.js +167 -0
- package/dist/testing/conformance/single-shard.d.ts +2 -0
- package/dist/testing/conformance/single-shard.js +125 -0
- package/dist/testing/conformance/singleton.d.ts +3 -0
- package/dist/testing/conformance/singleton.js +293 -0
- package/dist/testing/conformance/statements.d.ts +60 -0
- package/dist/testing/conformance/statements.js +108 -0
- package/dist/testing/conformance/streams.d.ts +7 -0
- package/dist/testing/conformance/streams.js +280 -0
- package/dist/testing/conformance/subscriptions/access.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/access.js +98 -0
- package/dist/testing/conformance/subscriptions/actors.d.ts +4910 -0
- package/dist/testing/conformance/subscriptions/actors.js +372 -0
- package/dist/testing/conformance/subscriptions/cluster.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/cluster.js +89 -0
- package/dist/testing/conformance/subscriptions/delivery.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/delivery.js +253 -0
- package/dist/testing/conformance/subscriptions/deploys.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/deploys.js +162 -0
- package/dist/testing/conformance/subscriptions/epochs.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/epochs.js +169 -0
- package/dist/testing/conformance/subscriptions/failures.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/failures.js +108 -0
- package/dist/testing/conformance/subscriptions/feeds.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/feeds.js +218 -0
- package/dist/testing/conformance/subscriptions/harness.d.ts +124 -0
- package/dist/testing/conformance/subscriptions/harness.js +143 -0
- package/dist/testing/conformance/subscriptions/operator.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/operator.js +128 -0
- package/dist/testing/conformance/subscriptions/retention.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/retention.js +288 -0
- package/dist/testing/conformance/subscriptions/routing.d.ts +4 -0
- package/dist/testing/conformance/subscriptions/routing.js +179 -0
- package/dist/testing/conformance/subscriptions.d.ts +4 -0
- package/dist/testing/conformance/subscriptions.js +19 -0
- package/dist/testing/conformance/tables.d.ts +125 -0
- package/dist/testing/conformance/tables.js +664 -0
- package/dist/testing/conformance/transports/actors.d.ts +1907 -0
- package/dist/testing/conformance/transports/actors.js +163 -0
- package/dist/testing/conformance/transports/client.d.ts +3 -0
- package/dist/testing/conformance/transports/client.js +237 -0
- package/dist/testing/conformance/transports/feeds.d.ts +3 -0
- package/dist/testing/conformance/transports/feeds.js +232 -0
- package/dist/testing/conformance/transports/harness.d.ts +93 -0
- package/dist/testing/conformance/transports/harness.js +84 -0
- package/dist/testing/conformance/transports/streams.d.ts +3 -0
- package/dist/testing/conformance/transports/streams.js +96 -0
- package/dist/testing/conformance/transports/websocket.d.ts +3 -0
- package/dist/testing/conformance/transports/websocket.js +384 -0
- package/dist/testing/conformance/transports/wire.d.ts +141 -0
- package/dist/testing/conformance/transports/wire.js +204 -0
- package/dist/testing/conformance/transports.d.ts +3 -0
- package/dist/testing/conformance/transports.js +11 -0
- package/dist/testing/conformance/watch.d.ts +59 -0
- package/dist/testing/conformance/watch.js +879 -0
- package/dist/testing/conformance/workflow-engine.d.ts +97 -0
- package/dist/testing/conformance/workflow-engine.js +253 -0
- package/dist/testing/conformance/workflow-versions.d.ts +3 -0
- package/dist/testing/conformance/workflow-versions.js +545 -0
- package/dist/testing/conformance/workflows/access.d.ts +4 -0
- package/dist/testing/conformance/workflows/access.js +147 -0
- package/dist/testing/conformance/workflows/actors.d.ts +2317 -0
- package/dist/testing/conformance/workflows/actors.js +305 -0
- package/dist/testing/conformance/workflows/basics.d.ts +4 -0
- package/dist/testing/conformance/workflows/basics.js +144 -0
- package/dist/testing/conformance/workflows/engine.d.ts +4 -0
- package/dist/testing/conformance/workflows/engine.js +80 -0
- package/dist/testing/conformance/workflows/guards.d.ts +4 -0
- package/dist/testing/conformance/workflows/guards.js +172 -0
- package/dist/testing/conformance/workflows/harness.d.ts +14 -0
- package/dist/testing/conformance/workflows/harness.js +44 -0
- package/dist/testing/conformance/workflows/recovery.d.ts +4 -0
- package/dist/testing/conformance/workflows/recovery.js +181 -0
- package/dist/testing/conformance/workflows/starts.d.ts +4 -0
- package/dist/testing/conformance/workflows/starts.js +57 -0
- package/dist/testing/conformance/workflows/suspension.d.ts +4 -0
- package/dist/testing/conformance/workflows/suspension.js +106 -0
- package/dist/testing/conformance/workflows/waits.d.ts +4 -0
- package/dist/testing/conformance/workflows/waits.js +284 -0
- package/dist/testing/conformance/workflows.d.ts +4 -0
- package/dist/testing/conformance/workflows.js +19 -0
- package/dist/testing/conformance.d.ts +330 -0
- package/dist/testing/conformance.js +364 -0
- package/dist/testing/database.d.ts +31 -0
- package/dist/testing/database.js +70 -0
- package/dist/testing/foundation.d.ts +41 -0
- package/dist/testing/foundation.js +517 -0
- package/dist/testing/index.d.ts +20 -0
- package/dist/testing/index.js +12 -0
- package/dist/testing/property.d.ts +40 -0
- package/dist/testing/property.js +95 -0
- package/dist/testing/simulate-cluster.d.ts +112 -0
- package/dist/testing/simulate-cluster.js +257 -0
- package/dist/testing/simulate.d.ts +90 -0
- package/dist/testing/simulate.js +127 -0
- package/package.json +1 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { SQL } from "drizzle-orm";
|
|
2
|
+
import * as PostgresDrizzle from "drizzle-orm/effect-postgres";
|
|
3
|
+
import { Effect, Option } from "effect";
|
|
4
|
+
import { SqlClient, SqlError } from "effect/sql";
|
|
5
|
+
import { type Group, type Filter, type AnyOwnedTable, type ScopedRead, type TableScope } from "../../tables/owned.ts";
|
|
6
|
+
type Database = PostgresDrizzle.EffectPgDatabase;
|
|
7
|
+
/** Drizzle bound to the runtime's own client, so it shares the turn connection. */
|
|
8
|
+
export declare const rowsDatabase: Effect.Effect<Option.None<Database> | Option.Some<Database>, never, never>;
|
|
9
|
+
/**
|
|
10
|
+
* Renders an application filter on `table` as SQL after `copyFilter` has
|
|
11
|
+
* checked and copied it, for reads the framework builds outside a turn.
|
|
12
|
+
*/
|
|
13
|
+
export declare const filterSql: ({ table, filter, }: {
|
|
14
|
+
readonly table: AnyOwnedTable;
|
|
15
|
+
readonly filter: Filter<AnyOwnedTable>;
|
|
16
|
+
}) => SQL<unknown> | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Binds owned-table reads and, when `write` is set, mutations to one turn's
|
|
19
|
+
* actor. Writes join the turn's transaction or fail; reads outside a
|
|
20
|
+
* transaction, as in a stream handler, run in their own tenant transaction.
|
|
21
|
+
* Drizzle wraps driver failures, so the inner `SqlError` is surfaced to let it
|
|
22
|
+
* decide between a retried turn and a deterministic defect. Off-turn contexts
|
|
23
|
+
* get no mutation methods at all, whatever their static type. A group query is
|
|
24
|
+
* read once per part and re-built from the framework's own client, so the
|
|
25
|
+
* caller's select object never runs.
|
|
26
|
+
*/
|
|
27
|
+
export declare const bindTables: (database: Option.Option<Database>, scope: TableScope, write: boolean, checked: ReadonlySet<AnyOwnedTable>) => Effect.Effect<{
|
|
28
|
+
rows: (table: AnyOwnedTable) => ScopedRead<AnyOwnedTable>;
|
|
29
|
+
group: Group;
|
|
30
|
+
}, never, SqlClient.SqlClient>;
|
|
31
|
+
/** How a layer registers adopted tables: its writer role, and whether the registering layer writes them. */
|
|
32
|
+
export interface AdoptionStartup {
|
|
33
|
+
readonly role: string | undefined;
|
|
34
|
+
readonly writes: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Records which actor type owns each declared table and checks that the
|
|
38
|
+
* physical table's primary key leads with the ownership columns. A table
|
|
39
|
+
* claimed by another actor type, missing, or keyed without ownership fails
|
|
40
|
+
* startup instead of running unscoped.
|
|
41
|
+
*/
|
|
42
|
+
export declare const checkTables: (actor: string, tables: readonly AnyOwnedTable[], role: string | undefined, adoption: AdoptionStartup) => Effect.Effect<boolean, SqlError.SqlError, SqlClient.SqlClient>;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
import { PgClient } from "@effect/sql-pg";
|
|
2
|
+
import { PgliteClient } from "@effect/sql-pglite";
|
|
3
|
+
import { and, Column, count, entityKind, eq, getTableColumns, is, noopEncoder, Param, relationsFilterToSQL, relationsOrderToSQL, SQL, sql as fragment, StringChunk, Table, } from "drizzle-orm";
|
|
4
|
+
import * as PostgresDrizzle from "drizzle-orm/effect-postgres";
|
|
5
|
+
import * as PgliteDrizzle from "drizzle-orm/effect-pglite";
|
|
6
|
+
import { PgSelectBase, } from "drizzle-orm/pg-core";
|
|
7
|
+
import { Cause, Effect, Option, Predicate } from "effect";
|
|
8
|
+
import { SqlClient, SqlError } from "effect/sql";
|
|
9
|
+
import { checkAdoptedTable, checkOwnedTable, inTenant, TenantScope } from "../database/tenancy.js";
|
|
10
|
+
import { checkEnforcedTable, checkWriterRole } from "../adoption/startup.js";
|
|
11
|
+
import { ownerIndexExists, ownerIndexSql } from "../adoption/plan.js";
|
|
12
|
+
import { routingKey as routingKeyOf } from "../storage/codec.js";
|
|
13
|
+
import { OWNERSHIP, ownership, } from "../../tables/owned.js";
|
|
14
|
+
/** Drizzle bound to the runtime's own client, so it shares the turn connection. */
|
|
15
|
+
export const rowsDatabase = Effect.gen(function* () {
|
|
16
|
+
const pglite = yield* Effect.serviceOption(PgliteClient.PgliteClient);
|
|
17
|
+
if (Option.isSome(pglite)) {
|
|
18
|
+
const database = yield* PgliteDrizzle.makeWithDefaults().pipe(Effect.provideService(PgliteClient.PgliteClient, pglite.value));
|
|
19
|
+
return Option.some(database);
|
|
20
|
+
}
|
|
21
|
+
const postgres = yield* Effect.serviceOption(PgClient.PgClient);
|
|
22
|
+
if (Option.isNone(postgres))
|
|
23
|
+
return Option.none();
|
|
24
|
+
return Option.some(yield* PostgresDrizzle.makeWithDefaults().pipe(Effect.provideService(PgClient.PgClient, postgres.value)));
|
|
25
|
+
});
|
|
26
|
+
const isEntity = (value) => value instanceof Object && entityKind in value.constructor;
|
|
27
|
+
const isRecord = (value) => {
|
|
28
|
+
const prototype = value === null || value === undefined ? undefined : Object.getPrototypeOf(value);
|
|
29
|
+
return prototype === Object.prototype || prototype === null;
|
|
30
|
+
};
|
|
31
|
+
const CANONICAL_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
32
|
+
const reject = (message) => {
|
|
33
|
+
throw new Error(message);
|
|
34
|
+
};
|
|
35
|
+
const checkColumn = (info, key, write) => {
|
|
36
|
+
if (info.ownerKeys.includes(key) && (write || !info.adopted || key === info.routingKey))
|
|
37
|
+
reject(`Ownership column ${key} of ${info.name} comes from the turn, not the application`);
|
|
38
|
+
if (!info.columns.includes(key))
|
|
39
|
+
reject(`Unknown column ${key} of ${info.name}`);
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Copies an application value into fresh primitives, dates, bytes, arrays, and
|
|
43
|
+
* plain records. Drizzle renders anything with `getSQL` as SQL, so a function,
|
|
44
|
+
* class instance, or SQL wrapper anywhere inside is rejected, and the copy
|
|
45
|
+
* means a getter cannot change the value after it was checked. Dates are
|
|
46
|
+
* structured-cloned so own properties such as `getSQL` are dropped.
|
|
47
|
+
*/
|
|
48
|
+
const copyOperand = (info, value) => {
|
|
49
|
+
if (value === null || value === undefined)
|
|
50
|
+
return value;
|
|
51
|
+
if (Predicate.isString(value) ||
|
|
52
|
+
Predicate.isNumber(value) ||
|
|
53
|
+
Predicate.isBigInt(value) ||
|
|
54
|
+
Predicate.isBoolean(value))
|
|
55
|
+
return value;
|
|
56
|
+
const prototype = Object.getPrototypeOf(value);
|
|
57
|
+
if (prototype === Date.prototype)
|
|
58
|
+
return structuredClone(value);
|
|
59
|
+
if (value instanceof Uint8Array && (prototype === Uint8Array.prototype || Buffer.isBuffer(value)))
|
|
60
|
+
return Uint8Array.from(value);
|
|
61
|
+
if (Array.isArray(value) && prototype === Array.prototype)
|
|
62
|
+
return value.map((item) => copyOperand(info, item));
|
|
63
|
+
if (!isRecord(value))
|
|
64
|
+
return reject(`Values on ${info.name} are plain data, not SQL or objects`);
|
|
65
|
+
const copy = {};
|
|
66
|
+
for (const key of Object.keys(value)) {
|
|
67
|
+
if (key === "RAW")
|
|
68
|
+
reject(`RAW filters on ${info.name} are not supported`);
|
|
69
|
+
copy[key] = copyOperand(info, value[key]);
|
|
70
|
+
}
|
|
71
|
+
return copy;
|
|
72
|
+
};
|
|
73
|
+
const copyValues = (info, values) => {
|
|
74
|
+
if (!isRecord(values))
|
|
75
|
+
reject(`Rows of ${info.name} are plain objects`);
|
|
76
|
+
for (const key of Object.keys(values))
|
|
77
|
+
checkColumn(info, key, true);
|
|
78
|
+
return copyOperand(info, values);
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Copies a filter, rejecting `RAW` and reserved columns. A bare date or byte
|
|
82
|
+
* string as a column's value is refused because Drizzle reads any object there
|
|
83
|
+
* as an operator map and would match every row instead of one.
|
|
84
|
+
*/
|
|
85
|
+
const copyFilter = (info, filter) => {
|
|
86
|
+
if (!isRecord(filter))
|
|
87
|
+
return reject(`Filters on ${info.name} are plain objects`);
|
|
88
|
+
const copy = {};
|
|
89
|
+
for (const key of Object.keys(filter)) {
|
|
90
|
+
const value = filter[key];
|
|
91
|
+
if (key === "RAW")
|
|
92
|
+
reject(`RAW filters on ${info.name} are not supported`);
|
|
93
|
+
else if (key === "AND" || key === "OR") {
|
|
94
|
+
if (!Array.isArray(value))
|
|
95
|
+
return reject(`${key} on ${info.name} takes an array`);
|
|
96
|
+
copy[key] = value.map((item) => copyFilter(info, item));
|
|
97
|
+
}
|
|
98
|
+
else if (key === "NOT")
|
|
99
|
+
copy[key] = copyFilter(info, value);
|
|
100
|
+
else {
|
|
101
|
+
checkColumn(info, key, false);
|
|
102
|
+
const operand = copyOperand(info, value);
|
|
103
|
+
if (operand instanceof Date || operand instanceof Uint8Array)
|
|
104
|
+
reject(`Compare ${key} of ${info.name} with { eq: value }`);
|
|
105
|
+
copy[key] = operand;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return copy;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Renders an application filter on `table` as SQL after `copyFilter` has
|
|
112
|
+
* checked and copied it, for reads the framework builds outside a turn.
|
|
113
|
+
*/
|
|
114
|
+
export const filterSql = ({ table, filter, }) => {
|
|
115
|
+
const info = ownership(table);
|
|
116
|
+
if (info === undefined)
|
|
117
|
+
return reject("Filters apply to owned tables");
|
|
118
|
+
return relationsFilterToSQL(table, copyFilter(info, filter));
|
|
119
|
+
};
|
|
120
|
+
const checkOrder = (info, order) => {
|
|
121
|
+
for (const [key, direction] of Object.entries(order)) {
|
|
122
|
+
checkColumn(info, key, false);
|
|
123
|
+
if (direction !== "asc" && direction !== "desc")
|
|
124
|
+
reject(`Order on ${info.name} is "asc" or "desc"`);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* The words Drizzle's comparison, boolean, pattern, and aggregate operators
|
|
129
|
+
* emit; any other SQL text in a group query is rejected so raw fragments
|
|
130
|
+
* cannot name other tables, subqueries, or comments.
|
|
131
|
+
*/
|
|
132
|
+
const OPERATOR_TEXT = /^(?:\s+|[(),*=<>!~@&|]+|and|or|not|in|is|null|like|ilike|between|asc|desc|nulls|first|last|distinct|true|false|count|sum|avg|min|max|lower|upper|coalesce)*$/i;
|
|
133
|
+
const GROUP = {
|
|
134
|
+
name: "a group query",
|
|
135
|
+
schema: undefined,
|
|
136
|
+
table: "",
|
|
137
|
+
columns: [],
|
|
138
|
+
primaryKey: [],
|
|
139
|
+
owner: undefined,
|
|
140
|
+
adopted: false,
|
|
141
|
+
access: "read",
|
|
142
|
+
ownerKeys: [],
|
|
143
|
+
ownerColumns: [],
|
|
144
|
+
routingKey: undefined,
|
|
145
|
+
tenantKey: "",
|
|
146
|
+
actorKey: "",
|
|
147
|
+
tenantKind: "text",
|
|
148
|
+
actorKind: "text",
|
|
149
|
+
placement: undefined,
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Rebuilds a group query's expressions from values read once, so what was
|
|
153
|
+
* checked is exactly what renders: getters, proxies, and hidden `getSQL`
|
|
154
|
+
* members on the caller's objects never reach Drizzle. Columns resolve to the
|
|
155
|
+
* real columns of the query's owned tables, text must be operator words with
|
|
156
|
+
* balanced parentheses (the scope predicate is parenthesized beside it), and
|
|
157
|
+
* parameters carry copied plain data. A decoder only maps result values in
|
|
158
|
+
* JavaScript, so it is rebound rather than trusted; a column decoder resolves
|
|
159
|
+
* like any other column.
|
|
160
|
+
*/
|
|
161
|
+
const groupRebuilder = (tables) => {
|
|
162
|
+
const column = (node) => {
|
|
163
|
+
const table = node.table;
|
|
164
|
+
const name = node.name;
|
|
165
|
+
if (!tables.includes(table))
|
|
166
|
+
return reject("Group queries reference columns of the tables they select from or join");
|
|
167
|
+
if (ownership(table).ownerColumns.includes(name))
|
|
168
|
+
return reject("Group queries do not read or filter ownership columns");
|
|
169
|
+
const real = Object.values(getTableColumns(table)).find((candidate) => candidate.name === name);
|
|
170
|
+
return real ?? reject(`Unknown column ${name}`);
|
|
171
|
+
};
|
|
172
|
+
const expression = (root) => {
|
|
173
|
+
let depth = 0;
|
|
174
|
+
const walk = (node) => {
|
|
175
|
+
if (node === undefined)
|
|
176
|
+
return undefined;
|
|
177
|
+
if (Array.isArray(node))
|
|
178
|
+
return node.map(walk);
|
|
179
|
+
if (is(node, StringChunk)) {
|
|
180
|
+
const text = String(node.value);
|
|
181
|
+
if (!OPERATOR_TEXT.test(text))
|
|
182
|
+
reject(`Group queries support Drizzle operators, not raw SQL: ${text}`);
|
|
183
|
+
for (const character of text) {
|
|
184
|
+
if (character === "(")
|
|
185
|
+
depth += 1;
|
|
186
|
+
else if (character === ")")
|
|
187
|
+
depth -= 1;
|
|
188
|
+
if (depth < 0)
|
|
189
|
+
reject("Group query expressions must balance their parentheses");
|
|
190
|
+
}
|
|
191
|
+
return new StringChunk(text);
|
|
192
|
+
}
|
|
193
|
+
if (is(node, SQL.Aliased)) {
|
|
194
|
+
const alias = String(node.fieldAlias);
|
|
195
|
+
return new SQL.Aliased(walk(node.sql), alias);
|
|
196
|
+
}
|
|
197
|
+
if (is(node, SQL)) {
|
|
198
|
+
const decoder = node.decoder;
|
|
199
|
+
const rebuilt = new SQL(walk([...node.queryChunks]));
|
|
200
|
+
rebuilt.decoder = is(decoder, Column)
|
|
201
|
+
? column(decoder)
|
|
202
|
+
: { mapFromDriverValue: decoder.mapFromDriverValue.bind(decoder) };
|
|
203
|
+
return rebuilt;
|
|
204
|
+
}
|
|
205
|
+
if (is(node, Column))
|
|
206
|
+
return column(node);
|
|
207
|
+
if (is(node, Param)) {
|
|
208
|
+
const encoder = node.encoder;
|
|
209
|
+
const value = copyOperand(GROUP, node.value);
|
|
210
|
+
if (encoder === noopEncoder)
|
|
211
|
+
return new Param(value);
|
|
212
|
+
if (is(encoder, Column))
|
|
213
|
+
return new Param(value, column(encoder));
|
|
214
|
+
return reject("Group query parameters are plain values");
|
|
215
|
+
}
|
|
216
|
+
if (isEntity(node))
|
|
217
|
+
return reject("Group queries cannot reference tables, subqueries, names, or placeholders");
|
|
218
|
+
return copyOperand(GROUP, node);
|
|
219
|
+
};
|
|
220
|
+
const rebuilt = walk(root);
|
|
221
|
+
if (depth !== 0)
|
|
222
|
+
reject("Group query expressions must balance their parentheses");
|
|
223
|
+
return rebuilt;
|
|
224
|
+
};
|
|
225
|
+
const selection = (fields) => {
|
|
226
|
+
if (is(fields, Column))
|
|
227
|
+
return column(fields);
|
|
228
|
+
if (is(fields, SQL) || is(fields, SQL.Aliased))
|
|
229
|
+
return expression(fields);
|
|
230
|
+
if (isEntity(fields) || !isRecord(fields))
|
|
231
|
+
return reject("Group queries select columns and Drizzle expressions");
|
|
232
|
+
const copy = {};
|
|
233
|
+
for (const key of Object.keys(fields))
|
|
234
|
+
copy[key] = selection(fields[key]);
|
|
235
|
+
return copy;
|
|
236
|
+
};
|
|
237
|
+
return { expression, selection };
|
|
238
|
+
};
|
|
239
|
+
/**
|
|
240
|
+
* Binds owned-table reads and, when `write` is set, mutations to one turn's
|
|
241
|
+
* actor. Writes join the turn's transaction or fail; reads outside a
|
|
242
|
+
* transaction, as in a stream handler, run in their own tenant transaction.
|
|
243
|
+
* Drizzle wraps driver failures, so the inner `SqlError` is surfaced to let it
|
|
244
|
+
* decide between a retried turn and a deterministic defect. Off-turn contexts
|
|
245
|
+
* get no mutation methods at all, whatever their static type. A group query is
|
|
246
|
+
* read once per part and re-built from the framework's own client, so the
|
|
247
|
+
* caller's select object never runs.
|
|
248
|
+
*/
|
|
249
|
+
export const bindTables = Effect.fnUntraced(function* (database, scope, write, checked) {
|
|
250
|
+
const sql = yield* SqlClient.SqlClient;
|
|
251
|
+
const connection = yield* Effect.serviceOption(sql.transactionService);
|
|
252
|
+
const { role } = yield* TenantScope;
|
|
253
|
+
if (write && Option.isNone(connection))
|
|
254
|
+
return yield* Effect.die(new Error("Owned rows need the turn transaction"));
|
|
255
|
+
const db = Option.getOrUndefined(database);
|
|
256
|
+
const run = (build) => Effect.andThen(scope.guard, () => Option.isNone(database)
|
|
257
|
+
? Effect.die(new Error("Owned tables need a PgClient or PgliteClient database"))
|
|
258
|
+
: Effect.suspend(build).pipe((effect) => Option.isSome(connection)
|
|
259
|
+
? Effect.provideService(effect, sql.transactionService, connection.value)
|
|
260
|
+
: inTenant({ sql, role, tenant: scope.ref.tenant })(effect), Effect.catch((error) => {
|
|
261
|
+
const wrapped = Predicate.hasProperty(error, "cause") ? error.cause : undefined;
|
|
262
|
+
const cause = Cause.isCause(wrapped) ? Cause.squash(wrapped) : wrapped;
|
|
263
|
+
return Effect.die(SqlError.isSqlError(cause) ? cause : error);
|
|
264
|
+
})));
|
|
265
|
+
const { ref } = scope;
|
|
266
|
+
const routingKey = routingKeyOf({ ref, placement: scope.placement });
|
|
267
|
+
const ownerOf = (info) => {
|
|
268
|
+
const owner = { [info.tenantKey]: ref.tenant, [info.actorKey]: ref.id };
|
|
269
|
+
if (info.routingKey === undefined)
|
|
270
|
+
return owner;
|
|
271
|
+
return { ...owner, [info.routingKey]: routingKey };
|
|
272
|
+
};
|
|
273
|
+
/**
|
|
274
|
+
* Off-turn contexts and read-adopted tables get no mutation methods at all,
|
|
275
|
+
* whatever their static type.
|
|
276
|
+
*/
|
|
277
|
+
const rows = (table) => {
|
|
278
|
+
const info = ownership(table);
|
|
279
|
+
const check = () => {
|
|
280
|
+
if (info === undefined || !scope.tables.includes(table))
|
|
281
|
+
reject(`${info?.name ?? "This table"} is not an owned table of ${ref.actor}`);
|
|
282
|
+
if (info.tenantKind === "uuid" && !CANONICAL_UUID.test(ref.tenant))
|
|
283
|
+
reject(`${info.name} maps a uuid tenant column, and tenant ${ref.tenant} is not a lowercase uuid`);
|
|
284
|
+
if (info.actorKind === "uuid" && !CANONICAL_UUID.test(ref.id))
|
|
285
|
+
reject(`${info.name} maps a uuid actor column, and actor id ${ref.id} is not a lowercase uuid`);
|
|
286
|
+
return info;
|
|
287
|
+
};
|
|
288
|
+
const columns = getTableColumns(table);
|
|
289
|
+
const where = (filter) => {
|
|
290
|
+
const info = check();
|
|
291
|
+
const copy = filter === undefined ? {} : copyFilter(info, filter);
|
|
292
|
+
return and(info.routingKey === undefined ? undefined : eq(columns[info.routingKey], routingKey), eq(columns[info.tenantKey], ref.tenant), eq(columns[info.actorKey], ref.id), relationsFilterToSQL(table, copy));
|
|
293
|
+
};
|
|
294
|
+
const fields = () => Object.fromEntries(check().columns.map((key) => [key, columns[key]]));
|
|
295
|
+
const order = (orderBy) => {
|
|
296
|
+
if (orderBy === undefined)
|
|
297
|
+
return [];
|
|
298
|
+
checkOrder(check(), orderBy);
|
|
299
|
+
const sql = relationsOrderToSQL(table, orderBy);
|
|
300
|
+
return sql === undefined ? [] : [sql];
|
|
301
|
+
};
|
|
302
|
+
const list = (options) => {
|
|
303
|
+
const query = db
|
|
304
|
+
.select(fields())
|
|
305
|
+
.from(table)
|
|
306
|
+
.where(where(options.where))
|
|
307
|
+
.orderBy(...order(options.orderBy))
|
|
308
|
+
.$dynamic();
|
|
309
|
+
const limited = options.limit === undefined ? query : query.limit(options.limit);
|
|
310
|
+
return options.offset === undefined ? limited : limited.offset(options.offset);
|
|
311
|
+
};
|
|
312
|
+
const prepare = (values) => {
|
|
313
|
+
const info = check();
|
|
314
|
+
const list = Array.isArray(values) ? values : [values];
|
|
315
|
+
const owner = ownerOf(info);
|
|
316
|
+
return {
|
|
317
|
+
info,
|
|
318
|
+
list: list.map((value) => ({ ...copyValues(info, value), ...owner })),
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
const wrote = Effect.sync(() => scope.wrote?.(check().name));
|
|
322
|
+
const filtered = (mutation) => ({
|
|
323
|
+
where: (filter) => run(() => mutation(filter)).pipe(Effect.tap(() => wrote), Effect.asVoid),
|
|
324
|
+
});
|
|
325
|
+
const read = {
|
|
326
|
+
one: (options = {}) => run(() => list({ ...options, limit: 1 })).pipe(Effect.map((found) => Option.fromNullishOr(found[0]))),
|
|
327
|
+
all: (options = {}) => run(() => list(options)),
|
|
328
|
+
count: (options = {}) => run(() => db
|
|
329
|
+
.select({ rows: count() })
|
|
330
|
+
.from(table)
|
|
331
|
+
.where(where(options.where))
|
|
332
|
+
.pipe(Effect.map((found) => found[0]?.rows ?? 0))),
|
|
333
|
+
};
|
|
334
|
+
if (!write || info?.access === "read")
|
|
335
|
+
return read;
|
|
336
|
+
return {
|
|
337
|
+
...read,
|
|
338
|
+
insert: (values) => run(() => {
|
|
339
|
+
const { list } = prepare(values);
|
|
340
|
+
return list.length === 0 ? Effect.void : db.insert(table).values(list);
|
|
341
|
+
}).pipe(Effect.tap(() => wrote), Effect.asVoid),
|
|
342
|
+
upsert: (values) => run(() => {
|
|
343
|
+
const { info, list } = prepare(values);
|
|
344
|
+
if (list.length === 0)
|
|
345
|
+
return Effect.void;
|
|
346
|
+
const target = (info.adopted ? info.primaryKey : [...OWNERSHIP, ...info.primaryKey]).map((key) => columns[key]);
|
|
347
|
+
const set = Object.fromEntries([...new Set(list.flatMap((value) => Object.keys(value)))].flatMap((key) => info.primaryKey.includes(key) || (info.ownerKeys.includes(key) && !info.adopted)
|
|
348
|
+
? []
|
|
349
|
+
: [[key, fragment `excluded.${fragment.identifier(columns[key].name)}`]]));
|
|
350
|
+
const insert = db.insert(table).values(list);
|
|
351
|
+
if (!info.adopted)
|
|
352
|
+
return Object.keys(set).length === 0
|
|
353
|
+
? insert.onConflictDoNothing({ target })
|
|
354
|
+
: insert.onConflictDoUpdate({ target, set });
|
|
355
|
+
delete set[info.tenantKey];
|
|
356
|
+
delete set[info.actorKey];
|
|
357
|
+
const first = columns[info.primaryKey[0]];
|
|
358
|
+
const applied = insert
|
|
359
|
+
.onConflictDoUpdate({
|
|
360
|
+
target,
|
|
361
|
+
set: Object.keys(set).length === 0
|
|
362
|
+
? { [info.primaryKey[0]]: fragment `excluded.${fragment.identifier(first.name)}` }
|
|
363
|
+
: set,
|
|
364
|
+
setWhere: and(eq(columns[info.tenantKey], ref.tenant), eq(columns[info.actorKey], ref.id)),
|
|
365
|
+
})
|
|
366
|
+
.returning({ key: first });
|
|
367
|
+
return applied.pipe(Effect.flatMap((written) => written.length === list.length
|
|
368
|
+
? Effect.void
|
|
369
|
+
: Effect.die(new Error(`An upsert of ${info.name} reached a primary key that belongs to another actor`))));
|
|
370
|
+
}).pipe(Effect.tap(() => wrote), Effect.asVoid),
|
|
371
|
+
update: (values) => filtered((filter) => {
|
|
372
|
+
const copy = copyValues(check(), values);
|
|
373
|
+
if (Object.keys(copy).length === 0)
|
|
374
|
+
reject(`An update of ${check().name} sets at least one column`);
|
|
375
|
+
return db.update(table).set(copy).where(where(filter));
|
|
376
|
+
}),
|
|
377
|
+
delete: () => filtered((filter) => db.delete(table).where(where(filter))),
|
|
378
|
+
};
|
|
379
|
+
};
|
|
380
|
+
const inGroup = (table) => {
|
|
381
|
+
const columns = getTableColumns(table);
|
|
382
|
+
const info = ownership(table);
|
|
383
|
+
return and(eq(columns[info.routingKey], routingKey), eq(columns[info.tenantKey], ref.tenant));
|
|
384
|
+
};
|
|
385
|
+
const ownedTable = (table) => {
|
|
386
|
+
if (!is(table, Table) || !checked.has(table))
|
|
387
|
+
return reject("Group queries read tables registered by an actor type, not aliases or subqueries");
|
|
388
|
+
if (ownership(table).access === "read")
|
|
389
|
+
return reject("Group queries cannot read a table adopted for reading only");
|
|
390
|
+
return table;
|
|
391
|
+
};
|
|
392
|
+
const bound = (value) => {
|
|
393
|
+
if (value === undefined)
|
|
394
|
+
return undefined;
|
|
395
|
+
if (!Predicate.isNumber(value))
|
|
396
|
+
return reject("Group query limits are numbers");
|
|
397
|
+
return value;
|
|
398
|
+
};
|
|
399
|
+
const group = (build) => run(() => {
|
|
400
|
+
const query = build({
|
|
401
|
+
select: db.select.bind(db),
|
|
402
|
+
selectDistinct: db.selectDistinct.bind(db),
|
|
403
|
+
});
|
|
404
|
+
if (!is(query, PgSelectBase))
|
|
405
|
+
return reject("A group query is a Drizzle select");
|
|
406
|
+
const config = {
|
|
407
|
+
...query.config,
|
|
408
|
+
};
|
|
409
|
+
if ((config.withList?.length ?? 0) > 0)
|
|
410
|
+
reject("Group queries cannot use WITH");
|
|
411
|
+
if (config.setOperators.length > 0)
|
|
412
|
+
reject("Group queries cannot use set operators");
|
|
413
|
+
if (config.lockingClause !== undefined)
|
|
414
|
+
reject("Group queries are read-only and take no locks");
|
|
415
|
+
if (config.comment !== undefined)
|
|
416
|
+
reject("Group queries take no SQL comments");
|
|
417
|
+
if (config.distinct !== undefined && config.distinct !== false && config.distinct !== true)
|
|
418
|
+
reject("Group queries support selectDistinct, not DISTINCT ON");
|
|
419
|
+
const from = ownedTable(config.table);
|
|
420
|
+
const joins = [...(config.joins ?? [])].map((join) => {
|
|
421
|
+
const joinType = join.joinType;
|
|
422
|
+
const lateral = join.lateral;
|
|
423
|
+
if ((joinType !== "inner" && joinType !== "left") || lateral === true)
|
|
424
|
+
reject("Group queries support inner and left joins");
|
|
425
|
+
return { joinType, table: ownedTable(join.table), on: join.on };
|
|
426
|
+
});
|
|
427
|
+
const rebuild = groupRebuilder([from, ...joins.map((join) => join.table)]);
|
|
428
|
+
const fields = rebuild.selection(config.fields);
|
|
429
|
+
const where = rebuild.expression(config.where);
|
|
430
|
+
const having = rebuild.expression(config.having);
|
|
431
|
+
const orderBy = rebuild.expression([...(config.orderBy ?? [])]);
|
|
432
|
+
const groupBy = rebuild.expression([...(config.groupBy ?? [])]);
|
|
433
|
+
const limit = bound(config.limit);
|
|
434
|
+
const offset = bound(config.offset);
|
|
435
|
+
let fresh = (config.distinct === true ? db.selectDistinct(fields) : db.select(fields))
|
|
436
|
+
.from(from)
|
|
437
|
+
.$dynamic();
|
|
438
|
+
for (const join of joins) {
|
|
439
|
+
const on = and(rebuild.expression(join.on), inGroup(join.table));
|
|
440
|
+
fresh =
|
|
441
|
+
join.joinType === "inner"
|
|
442
|
+
? fresh.innerJoin(join.table, on)
|
|
443
|
+
: fresh.leftJoin(join.table, on);
|
|
444
|
+
}
|
|
445
|
+
fresh = fresh.where(and(inGroup(from), where));
|
|
446
|
+
if (groupBy.length > 0)
|
|
447
|
+
fresh = fresh.groupBy(...groupBy);
|
|
448
|
+
if (having !== undefined)
|
|
449
|
+
fresh = fresh.having(having);
|
|
450
|
+
if (orderBy.length > 0)
|
|
451
|
+
fresh = fresh.orderBy(...orderBy);
|
|
452
|
+
if (limit !== undefined)
|
|
453
|
+
fresh = fresh.limit(limit);
|
|
454
|
+
if (offset !== undefined)
|
|
455
|
+
fresh = fresh.offset(offset);
|
|
456
|
+
const rows = fresh;
|
|
457
|
+
return rows;
|
|
458
|
+
});
|
|
459
|
+
return { rows, group };
|
|
460
|
+
});
|
|
461
|
+
/**
|
|
462
|
+
* Refuses an adopted table whose physical state does not match its
|
|
463
|
+
* declaration: a writable one needs its `actor_adoptions` row (which
|
|
464
|
+
* `durable adopt observe` writes) with the declared type and columns and a
|
|
465
|
+
* bigint `routing_key`; every adopted table needs mapped columns of a mappable
|
|
466
|
+
* type and an index that leads with them. Each message names the fix.
|
|
467
|
+
*/
|
|
468
|
+
const checkAdoptedDeclaration = Effect.fnUntraced(function* ({ actor, table, info, schema, role, adoption, }) {
|
|
469
|
+
const sql = yield* SqlClient.SqlClient;
|
|
470
|
+
const columns = getTableColumns(table);
|
|
471
|
+
const tenantColumn = columns[info.tenantKey].name;
|
|
472
|
+
const actorColumn = columns[info.actorKey].name;
|
|
473
|
+
const refuse = (message) => Effect.die(new Error(`Adopted table ${info.name} ${message}`));
|
|
474
|
+
const physical = yield* sql `
|
|
475
|
+
SELECT a.attname AS name, t.typname AS type FROM pg_attribute a
|
|
476
|
+
JOIN pg_class c ON c.oid = a.attrelid JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
477
|
+
JOIN pg_type t ON t.oid = a.atttypid
|
|
478
|
+
WHERE n.nspname = ${schema} AND c.relname = ${info.table} AND a.attnum > 0 AND NOT a.attisdropped`;
|
|
479
|
+
if (physical.length === 0)
|
|
480
|
+
return yield* refuse("does not exist");
|
|
481
|
+
for (const [column, kind] of [
|
|
482
|
+
[tenantColumn, info.tenantKind],
|
|
483
|
+
[actorColumn, info.actorKind],
|
|
484
|
+
]) {
|
|
485
|
+
const found = physical.find((candidate) => candidate.name === column);
|
|
486
|
+
if (found === undefined)
|
|
487
|
+
return yield* refuse(`has no column ${column}`);
|
|
488
|
+
if ((found.type === "uuid") !== (kind === "uuid") ||
|
|
489
|
+
!["text", "varchar", "uuid"].includes(found.type))
|
|
490
|
+
return yield* refuse(`maps ${column}, which is ${found.type}, not the declared ${kind} column`);
|
|
491
|
+
}
|
|
492
|
+
let enforcement;
|
|
493
|
+
if (info.access === "write") {
|
|
494
|
+
const [recorded] = yield* sql `SELECT actor_type, tenant_column, actor_column, mode, writer_role, allowed_roles
|
|
495
|
+
FROM actor_adoptions WHERE table_schema = ${schema} AND table_name = ${info.table}`;
|
|
496
|
+
if (recorded === undefined)
|
|
497
|
+
return yield* refuse(`has no adoption record; run durable adopt observe ${info.table} before serving it`);
|
|
498
|
+
if (recorded.actor_type !== actor)
|
|
499
|
+
return yield* refuse(`is adopted by actor ${recorded.actor_type}, not ${actor}`);
|
|
500
|
+
if (recorded.tenant_column !== tenantColumn || recorded.actor_column !== actorColumn)
|
|
501
|
+
return yield* refuse(`was adopted with columns (${recorded.tenant_column}, ${recorded.actor_column}) but declares (${tenantColumn}, ${actorColumn}); run durable adopt observe ${info.table} again`);
|
|
502
|
+
if (recorded.mode === "enforce")
|
|
503
|
+
enforcement = { writerRole: recorded.writer_role, allowedRoles: recorded.allowed_roles };
|
|
504
|
+
const routing = physical.find((candidate) => candidate.name === "routing_key");
|
|
505
|
+
if (routing?.type !== "int8")
|
|
506
|
+
return yield* refuse(`has no bigint routing_key; run durable adopt observe ${info.table}`);
|
|
507
|
+
}
|
|
508
|
+
const target = {
|
|
509
|
+
schema,
|
|
510
|
+
table: info.table,
|
|
511
|
+
access: info.access,
|
|
512
|
+
tenantColumn,
|
|
513
|
+
actorColumn,
|
|
514
|
+
};
|
|
515
|
+
if (!(yield* ownerIndexExists(target)))
|
|
516
|
+
return yield* refuse(`needs an index that leads with its owner columns: ${ownerIndexSql(target)}`);
|
|
517
|
+
if (role !== undefined)
|
|
518
|
+
yield* checkAdoptedTable(schema, info.table, role, info.access === "write");
|
|
519
|
+
if (enforcement === undefined || !adoption.writes)
|
|
520
|
+
return enforcement !== undefined;
|
|
521
|
+
if (role !== undefined && role !== enforcement.writerRole)
|
|
522
|
+
return yield* refuse(`is enforced for writer role ${enforcement.writerRole}, but row-level security takes ${role}; both options must name the same role`);
|
|
523
|
+
yield* checkEnforcedTable({
|
|
524
|
+
target: { schema, table: info.table, tenantColumn, actorColumn, actor },
|
|
525
|
+
...enforcement,
|
|
526
|
+
runtimeRole: adoption.role,
|
|
527
|
+
refuse,
|
|
528
|
+
});
|
|
529
|
+
return true;
|
|
530
|
+
});
|
|
531
|
+
/**
|
|
532
|
+
* Records which actor type owns each declared table and checks that the
|
|
533
|
+
* physical table's primary key leads with the ownership columns. A table
|
|
534
|
+
* claimed by another actor type, missing, or keyed without ownership fails
|
|
535
|
+
* startup instead of running unscoped.
|
|
536
|
+
*/
|
|
537
|
+
export const checkTables = Effect.fnUntraced(function* (actor, tables, role, adoption) {
|
|
538
|
+
const sql = yield* SqlClient.SqlClient;
|
|
539
|
+
const guarded = [];
|
|
540
|
+
let enforced = false;
|
|
541
|
+
for (const table of tables) {
|
|
542
|
+
const info = ownership(table);
|
|
543
|
+
const schema = info.schema ?? (yield* sql `SELECT current_schema() AS schema`)[0].schema;
|
|
544
|
+
guarded.push({ schema, table: info.table });
|
|
545
|
+
if (info.adopted) {
|
|
546
|
+
const isEnforced = yield* checkAdoptedDeclaration({
|
|
547
|
+
actor,
|
|
548
|
+
table,
|
|
549
|
+
info,
|
|
550
|
+
schema,
|
|
551
|
+
role,
|
|
552
|
+
adoption,
|
|
553
|
+
});
|
|
554
|
+
if (isEnforced && adoption.writes)
|
|
555
|
+
enforced = true;
|
|
556
|
+
yield* sql `INSERT INTO actor_tables (table_schema, table_name, actor_type)
|
|
557
|
+
VALUES (${schema}, ${info.table}, ${actor}) ON CONFLICT DO NOTHING`;
|
|
558
|
+
const [claimed] = yield* sql `
|
|
559
|
+
SELECT actor_type FROM actor_tables WHERE table_schema = ${schema} AND table_name = ${info.table}`;
|
|
560
|
+
if (claimed?.actor_type !== actor)
|
|
561
|
+
return yield* Effect.die(new Error(`Table ${info.name} is owned by actor ${claimed?.actor_type}, not ${actor}`));
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
const key = yield* sql `
|
|
565
|
+
SELECT a.attname AS name FROM pg_index i
|
|
566
|
+
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
|
|
567
|
+
WHERE i.indisprimary AND i.indrelid = (
|
|
568
|
+
SELECT c.oid FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
569
|
+
WHERE n.nspname = ${schema} AND c.relname = ${info.table})
|
|
570
|
+
ORDER BY array_position(i.indkey::int2[], a.attnum)`;
|
|
571
|
+
const columns = getTableColumns(table);
|
|
572
|
+
const expected = [...OWNERSHIP, ...info.primaryKey.map((column) => columns[column].name)];
|
|
573
|
+
if (key.map(({ name }) => name).join() !== expected.join())
|
|
574
|
+
return yield* Effect.die(new Error(`Owned table ${info.name} needs primary key (${expected.join(", ")}); apply its drizzle-kit migration`));
|
|
575
|
+
if (role !== undefined)
|
|
576
|
+
yield* checkOwnedTable(schema, info.table, role);
|
|
577
|
+
yield* sql `INSERT INTO actor_tables (table_schema, table_name, actor_type)
|
|
578
|
+
VALUES (${schema}, ${info.table}, ${actor}) ON CONFLICT DO NOTHING`;
|
|
579
|
+
const [recorded] = yield* sql `
|
|
580
|
+
SELECT actor_type FROM actor_tables WHERE table_schema = ${schema} AND table_name = ${info.table}`;
|
|
581
|
+
if (recorded?.actor_type !== actor)
|
|
582
|
+
return yield* Effect.die(new Error(`Table ${info.name} is owned by actor ${recorded?.actor_type}, not ${actor}`));
|
|
583
|
+
}
|
|
584
|
+
if (!enforced)
|
|
585
|
+
return false;
|
|
586
|
+
yield* checkWriterRole({
|
|
587
|
+
role: adoption.role,
|
|
588
|
+
tables: guarded,
|
|
589
|
+
refuse: (message) => Effect.die(new Error(`Adoption writer role: ${message}`)),
|
|
590
|
+
});
|
|
591
|
+
return true;
|
|
592
|
+
});
|