@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,576 @@
|
|
|
1
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
2
|
+
import { Cause, Context, Crypto, Data, Effect, Exit, Layer, Option, Redacted, Schedule, Schema, Scope, Stream, } from "effect";
|
|
3
|
+
import { SqlClient } from "effect/sql";
|
|
4
|
+
import { Actor, Actors, Intent, System } from "../../index.js";
|
|
5
|
+
import { InternalActors } from "../../runtime/actors.js";
|
|
6
|
+
import { Outcome, Request } from "../../runtime/request.js";
|
|
7
|
+
import { migrate } from "../../runtime/database/migrations.js";
|
|
8
|
+
import { Database } from "../../runtime/layer.js";
|
|
9
|
+
import { OperatorRuntime } from "../../runtime/operators/repair.js";
|
|
10
|
+
import { ActorTest } from "../actor-test.js";
|
|
11
|
+
import { ActorCluster } from "../cluster.js";
|
|
12
|
+
import { pauseReplay, replayedThrough } from "./read-your-writes.js";
|
|
13
|
+
import { Notebook, tablesDdl, tablesFixture, tablesLayer } from "./tables.js";
|
|
14
|
+
const Recorded = Actor.event("Recorded", { body: Schema.String });
|
|
15
|
+
const Ship = Actor.job("Ship", {
|
|
16
|
+
payload: { body: Schema.String },
|
|
17
|
+
success: Schema.String,
|
|
18
|
+
});
|
|
19
|
+
const Record = Actor.command("Record", { payload: Schema.String });
|
|
20
|
+
const Tick = Actor.command("Tick");
|
|
21
|
+
const Shipped = Actor.command("Shipped", { payload: Schema.String });
|
|
22
|
+
const Snapshot = Actor.stream("Snapshot", { success: Schema.String });
|
|
23
|
+
const Settle = Actor.workflow("Settle", { success: Schema.String });
|
|
24
|
+
const attachments = Actor.blob("attachments");
|
|
25
|
+
const Read = Actor.query("Read", {
|
|
26
|
+
success: Schema.Struct({
|
|
27
|
+
entries: Schema.Array(Schema.String),
|
|
28
|
+
ticks: Schema.Int,
|
|
29
|
+
shipped: Schema.Array(Schema.String),
|
|
30
|
+
events: Schema.Array(Schema.String),
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
33
|
+
const Ledger = Actor.make("Ledger", {
|
|
34
|
+
key: Schema.String,
|
|
35
|
+
state: Actor.state({
|
|
36
|
+
entries: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
|
|
37
|
+
ticks: Schema.Int.pipe(Schema.withDecodingDefault(Effect.succeed(0))),
|
|
38
|
+
shipped: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
|
|
39
|
+
}),
|
|
40
|
+
events: [Recorded],
|
|
41
|
+
jobs: { Ship: { job: Ship, onSuccess: Shipped } },
|
|
42
|
+
blobs: [attachments],
|
|
43
|
+
api: { Record, Read, Snapshot, Settle },
|
|
44
|
+
internal: { Tick, Shipped },
|
|
45
|
+
});
|
|
46
|
+
const ledgerLayer = Layer.mergeAll(Ledger.toLayer(Effect.succeed({
|
|
47
|
+
Record: Effect.fnUntraced(function* (body) {
|
|
48
|
+
const turn = yield* Ledger.Turn;
|
|
49
|
+
yield* turn.state.set({ entries: [...turn.state.entries, body] });
|
|
50
|
+
yield* turn.emit(Recorded.make({ body }));
|
|
51
|
+
yield* turn.blob(attachments).set("latest", new TextEncoder().encode(body));
|
|
52
|
+
const self = yield* Ledger.intents(turn.id);
|
|
53
|
+
yield* self.Tick().pipe(Intent.after("1 second"), Intent.key("tick"));
|
|
54
|
+
yield* turn.enqueue(Ship.make({ body }));
|
|
55
|
+
}),
|
|
56
|
+
Tick: Effect.fnUntraced(function* () {
|
|
57
|
+
const turn = yield* Ledger.Turn;
|
|
58
|
+
yield* turn.state.set({ ticks: turn.state.ticks + 1 });
|
|
59
|
+
}),
|
|
60
|
+
Shipped: Effect.fnUntraced(function* (body) {
|
|
61
|
+
const turn = yield* Ledger.Turn;
|
|
62
|
+
yield* turn.state.set({ shipped: [...turn.state.shipped, body] });
|
|
63
|
+
}),
|
|
64
|
+
Snapshot: () => Stream.unwrap(Effect.gen(function* () {
|
|
65
|
+
const read = yield* Ledger.Read;
|
|
66
|
+
const latest = yield* read.blob(attachments).get("latest");
|
|
67
|
+
const events = yield* read.events(Recorded).pipe(Effect.orDie);
|
|
68
|
+
return Stream.make(`${Option.match(latest, { onNone: () => "", onSome: (bytes) => new TextDecoder().decode(bytes) })}/${events.length}`);
|
|
69
|
+
})),
|
|
70
|
+
Settle: () => Effect.succeed("settled"),
|
|
71
|
+
})), Ledger.toJobLayer(Effect.succeed({ Ship: ({ body }) => Effect.succeed(body) })), Ledger.toQueryLayer(Effect.succeed({
|
|
72
|
+
Read: Effect.fnUntraced(function* () {
|
|
73
|
+
const read = yield* Ledger.Read;
|
|
74
|
+
const events = yield* read.events(Recorded).pipe(Effect.orDie);
|
|
75
|
+
return {
|
|
76
|
+
entries: read.state.entries,
|
|
77
|
+
ticks: read.state.ticks,
|
|
78
|
+
shipped: read.state.shipped,
|
|
79
|
+
events: events.map(({ event }) => event.body),
|
|
80
|
+
};
|
|
81
|
+
}),
|
|
82
|
+
})));
|
|
83
|
+
const Replicated = Actor.make("Replicated", {
|
|
84
|
+
key: Schema.String,
|
|
85
|
+
state: Actor.state({
|
|
86
|
+
entries: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
|
|
87
|
+
}),
|
|
88
|
+
api: {
|
|
89
|
+
Put: Actor.command("Put", { payload: Schema.String }),
|
|
90
|
+
Read: Actor.query("Read", { success: Schema.Array(Schema.String) }),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
const replicatedLayer = Layer.mergeAll(Replicated.toLayer(Effect.succeed({
|
|
94
|
+
Put: Effect.fnUntraced(function* (body) {
|
|
95
|
+
const turn = yield* Replicated.Turn;
|
|
96
|
+
yield* turn.state.set({ entries: [...turn.state.entries, body] });
|
|
97
|
+
}),
|
|
98
|
+
})), Replicated.toQueryLayer(Effect.succeed({
|
|
99
|
+
Read: Effect.fnUntraced(function* () {
|
|
100
|
+
const read = yield* Replicated.Read;
|
|
101
|
+
return read.state.entries;
|
|
102
|
+
}),
|
|
103
|
+
})));
|
|
104
|
+
const live = Layer.mergeAll(ledgerLayer, replicatedLayer, tablesLayer(tablesFixture()));
|
|
105
|
+
const VIEWS = [
|
|
106
|
+
"actors",
|
|
107
|
+
"state",
|
|
108
|
+
"receipts",
|
|
109
|
+
"events",
|
|
110
|
+
"outbox",
|
|
111
|
+
"timers",
|
|
112
|
+
"jobs",
|
|
113
|
+
"dead_letters",
|
|
114
|
+
"workflows",
|
|
115
|
+
"workflow_steps",
|
|
116
|
+
];
|
|
117
|
+
/** Hands every inspection view to `owner`, as the guide's script does. */
|
|
118
|
+
const viewsTo = (owner) => `DO $$
|
|
119
|
+
DECLARE view record;
|
|
120
|
+
BEGIN
|
|
121
|
+
FOR view IN SELECT relname FROM pg_class
|
|
122
|
+
WHERE relnamespace = 'durable'::regnamespace AND relkind = 'v'
|
|
123
|
+
LOOP
|
|
124
|
+
EXECUTE format('ALTER VIEW durable.%I OWNER TO %I', view.relname, '${owner}');
|
|
125
|
+
END LOOP;
|
|
126
|
+
END $$`;
|
|
127
|
+
/**
|
|
128
|
+
* The operator script of the row-level security guide: a tenant role the
|
|
129
|
+
* runtime takes for tenant-scoped transactions, and a separate view-owner role
|
|
130
|
+
* the policies bind, which the tenant role can't act as.
|
|
131
|
+
*/
|
|
132
|
+
export const grants = ({ role, viewOwner, }) => [
|
|
133
|
+
`CREATE ROLE ${role} NOLOGIN`,
|
|
134
|
+
`GRANT USAGE ON SCHEMA public TO ${role}`,
|
|
135
|
+
`GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO ${role}`,
|
|
136
|
+
`GRANT ${role} TO CURRENT_USER`,
|
|
137
|
+
`CREATE ROLE ${viewOwner} NOLOGIN`,
|
|
138
|
+
`GRANT USAGE ON SCHEMA public TO ${viewOwner}`,
|
|
139
|
+
`GRANT SELECT ON ALL TABLES IN SCHEMA public TO ${viewOwner}`,
|
|
140
|
+
`GRANT ${viewOwner} TO CURRENT_USER`,
|
|
141
|
+
`GRANT CREATE ON SCHEMA durable TO ${viewOwner}`,
|
|
142
|
+
viewsTo(viewOwner),
|
|
143
|
+
`REVOKE CREATE ON SCHEMA durable FROM ${viewOwner}`,
|
|
144
|
+
];
|
|
145
|
+
const databaseLayer = (target) => Redacted.isRedacted(target)
|
|
146
|
+
? Database.postgres({ url: target, maxConnections: 2, offTurnConnections: 2 })
|
|
147
|
+
: Database.pglite(target);
|
|
148
|
+
/** Runs `effect` on its own connection to `target`, outside any runtime. */
|
|
149
|
+
const onDatabase = (target, effect) => Layer.build(databaseLayer(target)).pipe(Effect.flatMap((context) => Effect.provideContext(effect, context)), Effect.scoped, Effect.orDie);
|
|
150
|
+
/**
|
|
151
|
+
* A fresh database migrated, with the owned tables' drizzle-kit DDL applied
|
|
152
|
+
* and, unless `grant` is false, the operator script run for a new role. On
|
|
153
|
+
* PGlite the fresh database is an in-memory instance, which this keeps open
|
|
154
|
+
* across the setup and the runtime; on Postgres the role is dropped at the end.
|
|
155
|
+
*/
|
|
156
|
+
const prepared = (environment, options = { grant: true }) => Effect.gen(function* () {
|
|
157
|
+
const crypto = yield* Crypto.Crypto;
|
|
158
|
+
const uuid = (yield* crypto.randomUUIDv4.pipe(Effect.orDie)).replaceAll("-", "");
|
|
159
|
+
const role = `durable_tenant_${uuid}`;
|
|
160
|
+
const viewOwner = `durable_views_${uuid}`;
|
|
161
|
+
const fresh = yield* environment.freshDatabase;
|
|
162
|
+
const target = Redacted.isRedacted(fresh)
|
|
163
|
+
? fresh
|
|
164
|
+
: {
|
|
165
|
+
liveClient: yield* Effect.acquireRelease(Effect.sync(() => new PGlite()).pipe(Effect.tap((client) => Effect.promise(() => client.waitReady))), (client) => Effect.promise(() => client.query("SELECT 1")).pipe(Effect.andThen(Effect.promise(() => client.close())))),
|
|
166
|
+
};
|
|
167
|
+
yield* onDatabase(target, Effect.gen(function* () {
|
|
168
|
+
const sql = yield* SqlClient.SqlClient;
|
|
169
|
+
yield* migrate;
|
|
170
|
+
for (const statement of [
|
|
171
|
+
...tablesDdl,
|
|
172
|
+
...(options.grant ? grants({ role, viewOwner }) : []),
|
|
173
|
+
])
|
|
174
|
+
yield* sql.unsafe(statement);
|
|
175
|
+
}));
|
|
176
|
+
if (options.grant && Redacted.isRedacted(target))
|
|
177
|
+
yield* Effect.addFinalizer(() => onDatabase(target, Effect.gen(function* () {
|
|
178
|
+
const sql = yield* SqlClient.SqlClient;
|
|
179
|
+
for (const owner of [role, viewOwner]) {
|
|
180
|
+
yield* sql.unsafe(`REASSIGN OWNED BY ${owner} TO CURRENT_USER`);
|
|
181
|
+
yield* sql.unsafe(`DROP OWNED BY ${owner}`);
|
|
182
|
+
yield* sql.unsafe(`DROP ROLE ${owner}`);
|
|
183
|
+
}
|
|
184
|
+
})));
|
|
185
|
+
return { target, role, viewOwner };
|
|
186
|
+
});
|
|
187
|
+
/** A fresh database for `environment`, migrated with the owned tables and a new tenant role, as row-level security cases need. */
|
|
188
|
+
export const preparedForRowLevelSecurity = (environment) => prepared(environment);
|
|
189
|
+
const runtimeOn = (target, role, replica) => Effect.gen(function* () {
|
|
190
|
+
const crypto = yield* Crypto.Crypto;
|
|
191
|
+
return yield* Layer.build(Layer.fresh(live.pipe(Layer.provideMerge(ActorTest.layer({
|
|
192
|
+
database: Redacted.isRedacted(target) ? target : { liveClient: target.liveClient },
|
|
193
|
+
rowLevelSecurity: { role },
|
|
194
|
+
replica,
|
|
195
|
+
})), Layer.provide(Layer.succeed(Crypto.Crypto, crypto)))));
|
|
196
|
+
});
|
|
197
|
+
/** Runs `body` on a runtime with row-level security on, over a fresh prepared database. */
|
|
198
|
+
const withRowLevelSecurity = (environment, body) => environment.run(Effect.gen(function* () {
|
|
199
|
+
const { target, role } = yield* prepared(environment);
|
|
200
|
+
const context = yield* runtimeOn(target, role).pipe(Effect.orDie);
|
|
201
|
+
return yield* body({ role, target }).pipe(Effect.provideContext(context));
|
|
202
|
+
}));
|
|
203
|
+
class Probed extends Data.TaggedError("Probed") {
|
|
204
|
+
}
|
|
205
|
+
/** Runs `effect` in a transaction that always rolls back, returning its value. */
|
|
206
|
+
const rolledBack = (effect) => Effect.gen(function* () {
|
|
207
|
+
const sql = yield* SqlClient.SqlClient;
|
|
208
|
+
const exit = yield* sql
|
|
209
|
+
.withTransaction(Effect.flatMap(effect, (result) => new Probed({ result })))
|
|
210
|
+
.pipe(Effect.flip);
|
|
211
|
+
return exit instanceof Probed ? exit.result : yield* Effect.fail(exit);
|
|
212
|
+
});
|
|
213
|
+
const rejection = (exit) => Exit.isSuccess(exit) ? "succeeded" : Cause.pretty(exit.cause);
|
|
214
|
+
/** An attempt in a savepoint, so its failure leaves the probe's transaction usable. */
|
|
215
|
+
const attempt = (effect) => Effect.gen(function* () {
|
|
216
|
+
const sql = yield* SqlClient.SqlClient;
|
|
217
|
+
return rejection(yield* effect.pipe(sql.withTransaction, Effect.exit));
|
|
218
|
+
});
|
|
219
|
+
/** Every table the `durable_tenant` policy protects: framework tables and owned tables. */
|
|
220
|
+
const protectedTables = Effect.gen(function* () {
|
|
221
|
+
const sql = yield* SqlClient.SqlClient;
|
|
222
|
+
const rows = yield* sql `
|
|
223
|
+
SELECT c.relname AS table FROM pg_class c JOIN pg_policy p ON p.polrelid = c.oid
|
|
224
|
+
WHERE p.polname = 'durable_tenant' AND c.relrowsecurity
|
|
225
|
+
ORDER BY c.relname`;
|
|
226
|
+
return rows.map(({ table }) => table);
|
|
227
|
+
});
|
|
228
|
+
/** Writes one of everything for `tenant`, then lets the timer, effect, and route run. */
|
|
229
|
+
const populate = (tenant) => Effect.gen(function* () {
|
|
230
|
+
const ledger = yield* Ledger.get("shared-id").pipe(Actor.tenant(tenant));
|
|
231
|
+
yield* ledger.Record(tenant);
|
|
232
|
+
const notebook = yield* Notebook.get("shared-id").pipe(Actor.tenant(tenant));
|
|
233
|
+
yield* notebook.Write({ id: "note", body: tenant });
|
|
234
|
+
});
|
|
235
|
+
const decodeEntries = Schema.decodeEffect(Schema.fromJsonString(Schema.Struct({ value: Schema.Array(Schema.String) })));
|
|
236
|
+
/** Row-level-security cases: every framework and owned table carries the tenant policy, and runners serving two tenants each see only their own rows. */
|
|
237
|
+
export const rlsConformance = [
|
|
238
|
+
{
|
|
239
|
+
name: "row-level security on: three runners serve two tenants' turns, timers, effects, and reads, each seeing only its own",
|
|
240
|
+
requiresIndependentConnections: true,
|
|
241
|
+
timeoutMs: 90_000,
|
|
242
|
+
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
243
|
+
const { target, role } = yield* prepared(environment);
|
|
244
|
+
if (!Redacted.isRedacted(target))
|
|
245
|
+
return yield* Effect.die(new Error("The three-runner case needs Postgres"));
|
|
246
|
+
const crypto = yield* Crypto.Crypto;
|
|
247
|
+
const context = yield* Layer.build(ActorTest.cluster({
|
|
248
|
+
database: target,
|
|
249
|
+
runners: 3,
|
|
250
|
+
shardLockExpiration: "3 seconds",
|
|
251
|
+
actors: live,
|
|
252
|
+
rowLevelSecurity: { role },
|
|
253
|
+
}).pipe(Layer.provide(Layer.succeed(Crypto.Crypto, crypto))));
|
|
254
|
+
const cluster = Context.get(context, ActorCluster);
|
|
255
|
+
const tenant = yield* cluster.on(0)(ActorTest.use((test) => Effect.succeed(test.tenant)));
|
|
256
|
+
const tenants = [tenant, `${tenant}-b`];
|
|
257
|
+
const ids = Array.from({ length: 6 }, (_, index) => `ledger-${index}`);
|
|
258
|
+
const owners = new Set();
|
|
259
|
+
for (const [index, id] of ids.entries())
|
|
260
|
+
for (const scoped of tenants)
|
|
261
|
+
yield* cluster.on(index % 3)(Effect.gen(function* () {
|
|
262
|
+
const ledger = yield* Ledger.get(id).pipe(Actor.tenant(scoped));
|
|
263
|
+
yield* ledger.Record(`${scoped}/${id}`);
|
|
264
|
+
const notebook = yield* Notebook.get(id).pipe(Actor.tenant(scoped));
|
|
265
|
+
yield* notebook.Write({ id: "note", body: scoped });
|
|
266
|
+
owners.add(yield* cluster.owner(ledger.ref));
|
|
267
|
+
}));
|
|
268
|
+
for (const [index, id] of ids.entries())
|
|
269
|
+
for (const scoped of tenants) {
|
|
270
|
+
const reader = (index + 1) % 3;
|
|
271
|
+
const read = cluster.on(reader)(Ledger.get(id).pipe(Actor.tenant(scoped), Effect.flatMap((ledger) => ledger.Read())));
|
|
272
|
+
const settled = yield* read.pipe(Effect.repeat({
|
|
273
|
+
schedule: Schedule.spaced("200 millis"),
|
|
274
|
+
until: (state) => state.ticks === 1 && state.shipped.length === 1,
|
|
275
|
+
}), Effect.timeout("30 seconds"));
|
|
276
|
+
expect(settled).toEqual({
|
|
277
|
+
entries: [`${scoped}/${id}`],
|
|
278
|
+
ticks: 1,
|
|
279
|
+
shipped: [`${scoped}/${id}`],
|
|
280
|
+
events: [`${scoped}/${id}`],
|
|
281
|
+
});
|
|
282
|
+
const notes = yield* cluster.on(reader)(Notebook.get(id).pipe(Actor.tenant(scoped), Effect.flatMap((notebook) => notebook.List())));
|
|
283
|
+
expect(notes.map(({ body }) => body)).toEqual([scoped]);
|
|
284
|
+
}
|
|
285
|
+
expect(owners.size > 1).toBe(true);
|
|
286
|
+
})),
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: "row-level security on: every framework table and owned table carries the tenant policy",
|
|
290
|
+
run: ({ expect, environment }) => withRowLevelSecurity(environment, () => Effect.gen(function* () {
|
|
291
|
+
const sql = yield* SqlClient.SqlClient;
|
|
292
|
+
const tenantTables = yield* sql `
|
|
293
|
+
SELECT c.relname AS table FROM pg_class c
|
|
294
|
+
WHERE c.relnamespace = current_schema()::regnamespace AND c.relkind = 'r'
|
|
295
|
+
AND EXISTS (SELECT 1 FROM pg_attribute a
|
|
296
|
+
WHERE a.attrelid = c.oid AND a.attname = 'tenant_id' AND NOT a.attisdropped)
|
|
297
|
+
ORDER BY c.relname`;
|
|
298
|
+
expect(yield* protectedTables).toEqual(tenantTables.map(({ table }) => table));
|
|
299
|
+
expect(yield* protectedTables).toContain("conformance_notes");
|
|
300
|
+
expect(yield* protectedTables).toContain("actor_generations");
|
|
301
|
+
})),
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: "row-level security on: turns, timers, effects, queries, and owned rows serve two tenants, each seeing only its own",
|
|
305
|
+
timeoutMs: 30_000,
|
|
306
|
+
run: ({ expect, environment }) => withRowLevelSecurity(environment, () => Effect.gen(function* () {
|
|
307
|
+
const test = yield* ActorTest;
|
|
308
|
+
const tenants = [test.tenant, `${test.tenant}-b`];
|
|
309
|
+
for (const tenant of tenants)
|
|
310
|
+
yield* populate(tenant);
|
|
311
|
+
yield* test.advance("2 seconds");
|
|
312
|
+
for (const tenant of tenants) {
|
|
313
|
+
const ledger = yield* Ledger.get("shared-id").pipe(Actor.tenant(tenant));
|
|
314
|
+
expect(yield* ledger.Read()).toEqual({
|
|
315
|
+
entries: [tenant],
|
|
316
|
+
ticks: 1,
|
|
317
|
+
shipped: [tenant],
|
|
318
|
+
events: [tenant],
|
|
319
|
+
});
|
|
320
|
+
const notebook = yield* Notebook.get("shared-id").pipe(Actor.tenant(tenant));
|
|
321
|
+
expect((yield* notebook.List()).map(({ body }) => body)).toEqual([tenant]);
|
|
322
|
+
}
|
|
323
|
+
yield* test.cleanup;
|
|
324
|
+
})),
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: "row-level security on: a transaction naming one tenant reads, changes, and inserts no other tenant's rows in any protected table",
|
|
328
|
+
timeoutMs: 30_000,
|
|
329
|
+
run: ({ expect, environment }) => withRowLevelSecurity(environment, ({ role }) => Effect.gen(function* () {
|
|
330
|
+
const sql = yield* SqlClient.SqlClient;
|
|
331
|
+
const test = yield* ActorTest;
|
|
332
|
+
const [home, abroad] = [test.tenant, `${test.tenant}-b`];
|
|
333
|
+
for (const tenant of [home, abroad])
|
|
334
|
+
yield* populate(tenant);
|
|
335
|
+
const tables = yield* protectedTables;
|
|
336
|
+
const stored = yield* sql `
|
|
337
|
+
SELECT count(*)::int AS rows FROM actor_generations WHERE tenant_id = ${abroad}`;
|
|
338
|
+
expect(stored).toEqual([{ rows: 2 }]);
|
|
339
|
+
const probe = yield* rolledBack(Effect.gen(function* () {
|
|
340
|
+
yield* sql `SELECT set_config('role', ${role}, true),
|
|
341
|
+
set_config('durable.tenant', ${home}, true)`;
|
|
342
|
+
const seen = {};
|
|
343
|
+
const changed = {};
|
|
344
|
+
for (const table of tables) {
|
|
345
|
+
const [row] = yield* sql `
|
|
346
|
+
SELECT count(*)::int AS other FROM ${sql(table)} WHERE tenant_id <> ${home}`;
|
|
347
|
+
seen[table] = row.other;
|
|
348
|
+
const updated = yield* sql `
|
|
349
|
+
UPDATE ${sql(table)} SET tenant_id = tenant_id WHERE tenant_id = ${abroad}
|
|
350
|
+
RETURNING 1`;
|
|
351
|
+
changed[table] = updated.length;
|
|
352
|
+
}
|
|
353
|
+
const [own] = yield* sql `
|
|
354
|
+
SELECT count(*)::int AS rows FROM actor_generations`;
|
|
355
|
+
const inserted = yield* attempt(sql `INSERT INTO actor_generations (routing_key, tenant_id, actor_type, actor_id)
|
|
356
|
+
VALUES (0, ${abroad}, 'Ledger', 'forged')`);
|
|
357
|
+
const moved = yield* attempt(sql `UPDATE actor_generations SET tenant_id = ${abroad} WHERE tenant_id = ${home}`);
|
|
358
|
+
return { seen, changed, own: own.rows, inserted, moved };
|
|
359
|
+
}));
|
|
360
|
+
for (const table of tables) {
|
|
361
|
+
expect({ table, other: probe.seen[table] }).toEqual({ table, other: 0 });
|
|
362
|
+
expect({ table, changed: probe.changed[table] }).toEqual({ table, changed: 0 });
|
|
363
|
+
}
|
|
364
|
+
expect(probe.own).toBe(2);
|
|
365
|
+
expect(probe.inserted).toContain("row-level security");
|
|
366
|
+
expect(probe.moved).toContain("row-level security");
|
|
367
|
+
const unnamed = yield* rolledBack(Effect.gen(function* () {
|
|
368
|
+
yield* sql `SELECT set_config('role', ${role}, true)`;
|
|
369
|
+
return yield* sql `SELECT count(*)::int AS rows FROM actor_state`;
|
|
370
|
+
}));
|
|
371
|
+
expect(unnamed).toEqual([{ rows: 0 }]);
|
|
372
|
+
})),
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: "row-level security on: an export runs as the tenant role and carries only its tenant's actors",
|
|
376
|
+
timeoutMs: 30_000,
|
|
377
|
+
run: ({ expect, environment }) => withRowLevelSecurity(environment, ({ role }) => Effect.gen(function* () {
|
|
378
|
+
const sql = yield* SqlClient.SqlClient;
|
|
379
|
+
const test = yield* ActorTest;
|
|
380
|
+
const operators = yield* OperatorRuntime;
|
|
381
|
+
const tenants = [test.tenant, `${test.tenant}-b`];
|
|
382
|
+
for (const tenant of tenants)
|
|
383
|
+
yield* populate(tenant);
|
|
384
|
+
const exported = (tenant, actorType) => operators.exportSeed({ tenant, actorType, actorId: "shared-id" });
|
|
385
|
+
for (const tenant of tenants) {
|
|
386
|
+
const ledger = Option.getOrThrow(yield* exported(tenant, "Ledger"));
|
|
387
|
+
const notebook = Option.getOrThrow(yield* exported(tenant, "Notebook"));
|
|
388
|
+
expect(ledger.state.entries).toEqual([tenant]);
|
|
389
|
+
expect(ledger.omitted).toMatchObject({ events: 1, tableRows: 0, blobs: 1 });
|
|
390
|
+
expect(notebook.omitted).toMatchObject({ receipts: 1, tableRows: 1, blobs: 0 });
|
|
391
|
+
}
|
|
392
|
+
yield* sql.unsafe(`REVOKE SELECT ON actor_outbox FROM ${role}`);
|
|
393
|
+
const refused = yield* exported(test.tenant, "Ledger").pipe(Effect.exit);
|
|
394
|
+
yield* sql.unsafe(`GRANT SELECT ON actor_outbox TO ${role}`);
|
|
395
|
+
expect(Exit.isFailure(refused)).toBe(true);
|
|
396
|
+
expect(Option.isSome(yield* exported(test.tenant, "Ledger"))).toBe(true);
|
|
397
|
+
expect(Option.isNone(yield* exported(`${test.tenant}-c`, "Ledger"))).toBe(true);
|
|
398
|
+
})),
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
name: "row-level security on: turns, queries, and every caller-facing read outside a turn run as the tenant role",
|
|
402
|
+
timeoutMs: 60_000,
|
|
403
|
+
run: ({ expect, environment }) => withRowLevelSecurity(environment, ({ role }) => Effect.gen(function* () {
|
|
404
|
+
const sql = yield* SqlClient.SqlClient;
|
|
405
|
+
const test = yield* ActorTest;
|
|
406
|
+
const internal = yield* InternalActors;
|
|
407
|
+
const ledger = yield* Ledger.get("revoked");
|
|
408
|
+
yield* ledger.Record("first");
|
|
409
|
+
const run = yield* ledger.Settle({});
|
|
410
|
+
expect(yield* run.result).toBe("settled");
|
|
411
|
+
const snapshot = ledger.Snapshot().pipe(Stream.runCollect, Effect.map((chunk) => [...chunk]));
|
|
412
|
+
const reads = {
|
|
413
|
+
turn: ledger.Record("refused"),
|
|
414
|
+
query: ledger.Read(),
|
|
415
|
+
exists: internal.exists(ledger.ref),
|
|
416
|
+
feed: internal.readFeed(ledger.ref, ["Recorded"], undefined, 10),
|
|
417
|
+
poll: run.poll,
|
|
418
|
+
stream: snapshot,
|
|
419
|
+
};
|
|
420
|
+
expect(yield* internal.exists(ledger.ref)).toBe(true);
|
|
421
|
+
expect((yield* internal.readFeed(ledger.ref, ["Recorded"], undefined, 10)).length).toBe(1);
|
|
422
|
+
expect(Option.isSome(yield* run.poll)).toBe(true);
|
|
423
|
+
expect(yield* snapshot).toEqual(["first/1"]);
|
|
424
|
+
const revoked = [
|
|
425
|
+
["turn", "INSERT", "actor_receipts"],
|
|
426
|
+
["query", "SELECT", "actor_state"],
|
|
427
|
+
["exists", "SELECT", "actor_generations"],
|
|
428
|
+
["feed", "SELECT", "actor_events"],
|
|
429
|
+
["poll", "SELECT", "actor_workflow_executions"],
|
|
430
|
+
["stream", "SELECT", "actor_blobs"],
|
|
431
|
+
];
|
|
432
|
+
const outcomes = {};
|
|
433
|
+
for (const [read, privilege, table] of revoked) {
|
|
434
|
+
yield* sql.unsafe(`REVOKE ${privilege} ON ${table} FROM ${role}`);
|
|
435
|
+
outcomes[read] = rejection(yield* reads[read].pipe(Effect.timeout("20 seconds"), Effect.exit));
|
|
436
|
+
yield* sql.unsafe(`GRANT ${privilege} ON ${table} TO ${role}`);
|
|
437
|
+
}
|
|
438
|
+
expect(Object.fromEntries(revoked.map(([read]) => [read, outcomes[read] !== "succeeded"]))).toEqual({ turn: true, query: true, exists: true, feed: true, poll: true, stream: true });
|
|
439
|
+
yield* ledger.Record("second");
|
|
440
|
+
expect((yield* ledger.Read()).entries).toEqual(["first", "second"]);
|
|
441
|
+
expect(yield* test.receiptsFor(ledger.ref, "Record")).toBe(2);
|
|
442
|
+
expect(yield* snapshot).toEqual(["second/2"]);
|
|
443
|
+
})),
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
name: "row-level security on: a role granted only the durable schema reads its transaction's tenant through the views and no runtime table",
|
|
447
|
+
timeoutMs: 30_000,
|
|
448
|
+
run: ({ expect, environment }) => withRowLevelSecurity(environment, () => Effect.gen(function* () {
|
|
449
|
+
const sql = yield* SqlClient.SqlClient;
|
|
450
|
+
const test = yield* ActorTest;
|
|
451
|
+
const [home, abroad] = [test.tenant, `${test.tenant}-b`];
|
|
452
|
+
for (const tenant of [home, abroad])
|
|
453
|
+
yield* populate(tenant);
|
|
454
|
+
const uuid = yield* (yield* Crypto.Crypto).randomUUIDv4.pipe(Effect.orDie);
|
|
455
|
+
const inspector = `inspector_${uuid.replaceAll("-", "")}`;
|
|
456
|
+
const tenantsIn = (tenant) => Effect.gen(function* () {
|
|
457
|
+
yield* sql `SELECT set_config('durable.tenant', ${tenant}, true)`;
|
|
458
|
+
const seen = {};
|
|
459
|
+
for (const view of VIEWS) {
|
|
460
|
+
const rows = yield* sql `
|
|
461
|
+
SELECT DISTINCT tenant_id AS tenant FROM ${sql(`durable.${view}`)}`;
|
|
462
|
+
seen[view] = rows.map(({ tenant }) => tenant);
|
|
463
|
+
}
|
|
464
|
+
const [counted] = yield* sql `
|
|
465
|
+
SELECT (SELECT count(*)::int FROM durable.actors) AS actors,
|
|
466
|
+
(SELECT count(*)::int FROM durable.events) AS events`;
|
|
467
|
+
return { seen, counted: counted };
|
|
468
|
+
});
|
|
469
|
+
const probe = yield* rolledBack(Effect.gen(function* () {
|
|
470
|
+
yield* sql.unsafe(`CREATE ROLE ${inspector} NOLOGIN`);
|
|
471
|
+
yield* sql.unsafe(`GRANT USAGE ON SCHEMA durable TO ${inspector}`);
|
|
472
|
+
yield* sql.unsafe(`GRANT SELECT ON ALL TABLES IN SCHEMA durable TO ${inspector}`);
|
|
473
|
+
yield* sql.unsafe(`SET LOCAL ROLE ${inspector}`);
|
|
474
|
+
const atHome = yield* tenantsIn(home);
|
|
475
|
+
const atAbroad = yield* tenantsIn(abroad);
|
|
476
|
+
const nowhere = yield* tenantsIn("");
|
|
477
|
+
const denied = {};
|
|
478
|
+
for (const table of yield* protectedTables)
|
|
479
|
+
denied[table] = yield* attempt(sql `SELECT 1 FROM ${sql(table)} LIMIT 1`);
|
|
480
|
+
return { atHome, atAbroad, nowhere, denied };
|
|
481
|
+
}));
|
|
482
|
+
for (const view of VIEWS) {
|
|
483
|
+
expect(probe.atHome.seen[view].filter((tenant) => tenant !== home)).toEqual([]);
|
|
484
|
+
expect(probe.atAbroad.seen[view].filter((tenant) => tenant !== abroad)).toEqual([]);
|
|
485
|
+
expect(probe.nowhere.seen[view]).toEqual([]);
|
|
486
|
+
}
|
|
487
|
+
expect(probe.atHome.counted).toEqual({ actors: 2, events: 1 });
|
|
488
|
+
expect(probe.atAbroad.counted).toEqual({ actors: 2, events: 1 });
|
|
489
|
+
expect(probe.nowhere.counted).toEqual({ actors: 0, events: 0 });
|
|
490
|
+
for (const [table, reason] of Object.entries(probe.denied))
|
|
491
|
+
expect({ table, denied: /permission denied/.test(reason) }).toEqual({
|
|
492
|
+
table,
|
|
493
|
+
denied: true,
|
|
494
|
+
});
|
|
495
|
+
})),
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
name: "row-level security on: the runtime refuses to start when the role is missing or owns an owned table, or a view belongs to an exempt role or one the tenant role can act as",
|
|
499
|
+
timeoutMs: 30_000,
|
|
500
|
+
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
501
|
+
const missing = yield* prepared(environment, { grant: false });
|
|
502
|
+
const absent = rejection(yield* runtimeOn(missing.target, missing.role).pipe(Effect.scoped, Effect.exit));
|
|
503
|
+
const { target, role } = yield* prepared(environment);
|
|
504
|
+
yield* onDatabase(target, Effect.gen(function* () {
|
|
505
|
+
const sql = yield* SqlClient.SqlClient;
|
|
506
|
+
yield* sql `ALTER VIEW durable.receipts OWNER TO CURRENT_USER`;
|
|
507
|
+
}));
|
|
508
|
+
const unowned = rejection(yield* runtimeOn(target, role).pipe(Effect.scoped, Effect.exit));
|
|
509
|
+
const shared = yield* prepared(environment);
|
|
510
|
+
yield* onDatabase(shared.target, Effect.gen(function* () {
|
|
511
|
+
const sql = yield* SqlClient.SqlClient;
|
|
512
|
+
yield* sql.unsafe(`GRANT CREATE ON SCHEMA durable TO ${shared.role}`);
|
|
513
|
+
yield* sql.unsafe(viewsTo(shared.role));
|
|
514
|
+
}));
|
|
515
|
+
const actable = rejection(yield* runtimeOn(shared.target, shared.role).pipe(Effect.scoped, Effect.exit));
|
|
516
|
+
const owner = yield* prepared(environment);
|
|
517
|
+
yield* onDatabase(owner.target, Effect.gen(function* () {
|
|
518
|
+
const sql = yield* SqlClient.SqlClient;
|
|
519
|
+
yield* sql.unsafe(`ALTER TABLE conformance_notes OWNER TO ${owner.role}`);
|
|
520
|
+
}));
|
|
521
|
+
const owning = rejection(yield* runtimeOn(owner.target, owner.role).pipe(Effect.scoped, Effect.exit));
|
|
522
|
+
expect(absent).toContain(`role ${missing.role} does not exist`);
|
|
523
|
+
expect(unowned).toContain("durable.receipts belongs to");
|
|
524
|
+
expect(unowned).toContain("which the policies exempt");
|
|
525
|
+
expect(actable).toContain(`which ${shared.role} can act as`);
|
|
526
|
+
expect(owning).toContain(`role ${owner.role} owns public.conformance_notes`);
|
|
527
|
+
})),
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
name: "row-level security on: a replica read runs as the tenant role and binds the tenant, falling through to the primary when the role is refused there",
|
|
531
|
+
requiresReplica: true,
|
|
532
|
+
timeoutMs: 60_000,
|
|
533
|
+
run: ({ expect, environment }) => environment.run(Effect.gen(function* () {
|
|
534
|
+
const { target, role } = yield* prepared(environment);
|
|
535
|
+
const replica = environment.replica;
|
|
536
|
+
if (!Redacted.isRedacted(target))
|
|
537
|
+
return yield* Effect.die("A replica needs Postgres");
|
|
538
|
+
const onReplica = new URL(Redacted.value(replica.database));
|
|
539
|
+
onReplica.pathname = new URL(Redacted.value(target)).pathname;
|
|
540
|
+
const context = yield* runtimeOn(target, role, Redacted.make(onReplica.href)).pipe(Effect.orDie);
|
|
541
|
+
yield* Effect.gen(function* () {
|
|
542
|
+
const sql = yield* SqlClient.SqlClient;
|
|
543
|
+
const internal = yield* InternalActors;
|
|
544
|
+
const control = yield* replica.connect;
|
|
545
|
+
const ledger = yield* Replicated.get("replicated");
|
|
546
|
+
const primaryVersion = Effect.map(sql `SELECT (pg_current_wal_insert_lsn() - '0/0')::text AS version`, (rows) => rows[0].version).pipe(Effect.orDie);
|
|
547
|
+
const entries = internal
|
|
548
|
+
.query(Request.make({
|
|
549
|
+
ref: ledger.ref,
|
|
550
|
+
caller: System.make({ source: "process" }),
|
|
551
|
+
command: "Read",
|
|
552
|
+
commandId: "",
|
|
553
|
+
payload: '{"value":null}',
|
|
554
|
+
}))
|
|
555
|
+
.pipe(Effect.flatMap((outcome) => Outcome.guards.Success(outcome)
|
|
556
|
+
? decodeEntries(outcome.value)
|
|
557
|
+
: Effect.die(`Read answered ${outcome._tag}`)), Effect.map(({ value }) => value), Effect.orDie);
|
|
558
|
+
yield* ledger.Put("first");
|
|
559
|
+
yield* sql.unsafe(`REVOKE SELECT ON actor_state FROM ${role}`);
|
|
560
|
+
yield* replayedThrough(control, yield* primaryVersion);
|
|
561
|
+
yield* Effect.scoped(Effect.gen(function* () {
|
|
562
|
+
yield* pauseReplay(control);
|
|
563
|
+
yield* sql.unsafe(`GRANT SELECT ON actor_state TO ${role}`);
|
|
564
|
+
yield* ledger.Put("second");
|
|
565
|
+
expect(yield* entries).toEqual(["first", "second"]);
|
|
566
|
+
}));
|
|
567
|
+
yield* replayedThrough(control, yield* primaryVersion);
|
|
568
|
+
yield* Effect.scoped(Effect.gen(function* () {
|
|
569
|
+
yield* pauseReplay(control);
|
|
570
|
+
yield* ledger.Put("third");
|
|
571
|
+
expect(yield* entries).toEqual(["first", "second"]);
|
|
572
|
+
}));
|
|
573
|
+
}).pipe(Effect.provideContext(context));
|
|
574
|
+
})),
|
|
575
|
+
},
|
|
576
|
+
];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { ActorCluster } from "../cluster.ts";
|
|
3
|
+
import type { ConformanceCase, ConformanceExpect } from "../conformance.ts";
|
|
4
|
+
import { type ClusterSimulationOptions } from "../simulate-cluster.ts";
|
|
5
|
+
export declare const simulationActors: Layer.Layer<never, never, import("../index.ts").InternalActors>;
|
|
6
|
+
/**
|
|
7
|
+
* Direct adds and relayed payments on a few actors, sent through `commands`
|
|
8
|
+
* commands of a seeded cluster simulation, then the totals each actor holds
|
|
9
|
+
* against the sums the script sent. Actor names carry `tag`, so scripts of
|
|
10
|
+
* several runs share a cluster without sharing state.
|
|
11
|
+
*/
|
|
12
|
+
export declare const clusterScript: ({ expect, options, commands, tag, }: {
|
|
13
|
+
readonly expect: ConformanceExpect;
|
|
14
|
+
readonly options: Omit<ClusterSimulationOptions, "commands">;
|
|
15
|
+
readonly commands: number;
|
|
16
|
+
readonly tag?: string;
|
|
17
|
+
}) => Effect.Effect<{
|
|
18
|
+
report: import("../simulate-cluster.ts").ClusterSimulationReport;
|
|
19
|
+
expected: Map<string, number>;
|
|
20
|
+
held: Map<string, unknown>;
|
|
21
|
+
}, never, ActorCluster>;
|
|
22
|
+
export declare const sums: (expected: ReadonlyMap<string, number>) => Map<string, {
|
|
23
|
+
total: number;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Each seed gets a cluster of its own: settling moves the runners' clocks, and
|
|
27
|
+
* a runner restarted by one seed's kill would start the next one behind them.
|
|
28
|
+
* The seeded case logs each seed's schedule tagged `CLUSTER_SIMULATION`, so a
|
|
29
|
+
* night's schedules can be read from its log.
|
|
30
|
+
*/
|
|
31
|
+
export declare const simulationConformance: ReadonlyArray<ConformanceCase>;
|