@digimuza/saas-core 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1165 -0
- package/LICENSE +21 -0
- package/README.md +234 -0
- package/dist/api-keys/index.d.ts +9 -0
- package/dist/api-keys/index.d.ts.map +1 -0
- package/dist/api-keys/index.js +37 -0
- package/dist/api-keys/index.js.map +1 -0
- package/dist/api-keys/keys.d.ts +55 -0
- package/dist/api-keys/keys.d.ts.map +1 -0
- package/dist/api-keys/keys.js +92 -0
- package/dist/api-keys/keys.js.map +1 -0
- package/dist/api-keys/service.d.ts +111 -0
- package/dist/api-keys/service.d.ts.map +1 -0
- package/dist/api-keys/service.js +210 -0
- package/dist/api-keys/service.js.map +1 -0
- package/dist/audit/index.d.ts +5 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +44 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/service.d.ts +88 -0
- package/dist/audit/service.d.ts.map +1 -0
- package/dist/audit/service.js +128 -0
- package/dist/audit/service.js.map +1 -0
- package/dist/auth/client.d.ts +5 -0
- package/dist/auth/client.d.ts.map +1 -0
- package/dist/auth/client.js +26 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/auth/cookies.d.ts +51 -0
- package/dist/auth/cookies.d.ts.map +1 -0
- package/dist/auth/cookies.js +58 -0
- package/dist/auth/cookies.js.map +1 -0
- package/dist/auth/index.d.ts +14 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +46 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/service.d.ts +67 -0
- package/dist/auth/service.d.ts.map +1 -0
- package/dist/auth/service.js +275 -0
- package/dist/auth/service.js.map +1 -0
- package/dist/auth/types.d.ts +56 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/authorization/index.d.ts +4 -0
- package/dist/authorization/index.d.ts.map +1 -0
- package/dist/authorization/index.js +24 -0
- package/dist/authorization/index.js.map +1 -0
- package/dist/authorization/service.d.ts +92 -0
- package/dist/authorization/service.d.ts.map +1 -0
- package/dist/authorization/service.js +254 -0
- package/dist/authorization/service.js.map +1 -0
- package/dist/billing/index.d.ts +16 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +47 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/provider.d.ts +114 -0
- package/dist/billing/provider.d.ts.map +1 -0
- package/dist/billing/provider.js +2 -0
- package/dist/billing/provider.js.map +1 -0
- package/dist/billing/revolut.d.ts +68 -0
- package/dist/billing/revolut.d.ts.map +1 -0
- package/dist/billing/revolut.js +181 -0
- package/dist/billing/revolut.js.map +1 -0
- package/dist/billing/service.d.ts +124 -0
- package/dist/billing/service.d.ts.map +1 -0
- package/dist/billing/service.js +470 -0
- package/dist/billing/service.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +43 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +212 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/project.d.ts +12 -0
- package/dist/cli/commands/project.d.ts.map +1 -0
- package/dist/cli/commands/project.js +49 -0
- package/dist/cli/commands/project.js.map +1 -0
- package/dist/cli/docker.d.ts +50 -0
- package/dist/cli/docker.d.ts.map +1 -0
- package/dist/cli/docker.js +84 -0
- package/dist/cli/docker.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +198 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +30 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +66 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/templates.d.ts +52 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli/templates.js +225 -0
- package/dist/cli/templates.js.map +1 -0
- package/dist/config/client.d.ts +60 -0
- package/dist/config/client.d.ts.map +1 -0
- package/dist/config/client.js +94 -0
- package/dist/config/client.js.map +1 -0
- package/dist/config/config-loader.d.ts +2 -0
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-loader.js +2 -0
- package/dist/config/config-loader.js.map +1 -0
- package/dist/config/env.d.ts +24 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +40 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/errors.d.ts +17 -0
- package/dist/config/errors.d.ts.map +1 -0
- package/dist/config/errors.js +26 -0
- package/dist/config/errors.js.map +1 -0
- package/dist/config/index.d.ts +72 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +106 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/runtime.d.ts +15 -0
- package/dist/config/runtime.d.ts.map +1 -0
- package/dist/config/runtime.js +22 -0
- package/dist/config/runtime.js.map +1 -0
- package/dist/config/sections.d.ts +226 -0
- package/dist/config/sections.d.ts.map +1 -0
- package/dist/config/sections.js +141 -0
- package/dist/config/sections.js.map +1 -0
- package/dist/db/client.d.ts +43 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +77 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/errors.d.ts +39 -0
- package/dist/db/errors.d.ts.map +1 -0
- package/dist/db/errors.js +67 -0
- package/dist/db/errors.js.map +1 -0
- package/dist/db/index.d.ts +10 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +10 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrate.d.ts +21 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +28 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schema/_shared.d.ts +30 -0
- package/dist/db/schema/_shared.d.ts.map +1 -0
- package/dist/db/schema/_shared.js +36 -0
- package/dist/db/schema/_shared.js.map +1 -0
- package/dist/db/schema/apikeys.d.ts +263 -0
- package/dist/db/schema/apikeys.d.ts.map +1 -0
- package/dist/db/schema/apikeys.js +70 -0
- package/dist/db/schema/apikeys.js.map +1 -0
- package/dist/db/schema/authorization.d.ts +465 -0
- package/dist/db/schema/authorization.d.ts.map +1 -0
- package/dist/db/schema/authorization.js +134 -0
- package/dist/db/schema/authorization.js.map +1 -0
- package/dist/db/schema/billing.d.ts +1100 -0
- package/dist/db/schema/billing.d.ts.map +1 -0
- package/dist/db/schema/billing.js +223 -0
- package/dist/db/schema/billing.js.map +1 -0
- package/dist/db/schema/events.d.ts +161 -0
- package/dist/db/schema/events.d.ts.map +1 -0
- package/dist/db/schema/events.js +66 -0
- package/dist/db/schema/events.js.map +1 -0
- package/dist/db/schema/features.d.ts +243 -0
- package/dist/db/schema/features.d.ts.map +1 -0
- package/dist/db/schema/features.js +87 -0
- package/dist/db/schema/features.js.map +1 -0
- package/dist/db/schema/files.d.ts +253 -0
- package/dist/db/schema/files.d.ts.map +1 -0
- package/dist/db/schema/files.js +74 -0
- package/dist/db/schema/files.js.map +1 -0
- package/dist/db/schema/index.d.ts +38 -0
- package/dist/db/schema/index.d.ts.map +1 -0
- package/dist/db/schema/index.js +38 -0
- package/dist/db/schema/index.js.map +1 -0
- package/dist/db/schema/invitations.d.ts +214 -0
- package/dist/db/schema/invitations.d.ts.map +1 -0
- package/dist/db/schema/invitations.js +80 -0
- package/dist/db/schema/invitations.js.map +1 -0
- package/dist/db/schema/messaging.d.ts +442 -0
- package/dist/db/schema/messaging.d.ts.map +1 -0
- package/dist/db/schema/messaging.js +120 -0
- package/dist/db/schema/messaging.js.map +1 -0
- package/dist/db/schema/users.d.ts +246 -0
- package/dist/db/schema/users.d.ts.map +1 -0
- package/dist/db/schema/users.js +60 -0
- package/dist/db/schema/users.js.map +1 -0
- package/dist/db/schema/webhooks.d.ts +693 -0
- package/dist/db/schema/webhooks.d.ts.map +1 -0
- package/dist/db/schema/webhooks.js +147 -0
- package/dist/db/schema/webhooks.js.map +1 -0
- package/dist/db/schema/workspaces.d.ts +196 -0
- package/dist/db/schema/workspaces.d.ts.map +1 -0
- package/dist/db/schema/workspaces.js +57 -0
- package/dist/db/schema/workspaces.js.map +1 -0
- package/dist/db/types.d.ts +18 -0
- package/dist/db/types.d.ts.map +1 -0
- package/dist/db/types.js +2 -0
- package/dist/db/types.js.map +1 -0
- package/dist/email/adapters.d.ts +29 -0
- package/dist/email/adapters.d.ts.map +1 -0
- package/dist/email/adapters.js +60 -0
- package/dist/email/adapters.js.map +1 -0
- package/dist/email/index.d.ts +20 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +79 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/provider.d.ts +47 -0
- package/dist/email/provider.d.ts.map +1 -0
- package/dist/email/provider.js +7 -0
- package/dist/email/provider.js.map +1 -0
- package/dist/email/resend.d.ts +22 -0
- package/dist/email/resend.d.ts.map +1 -0
- package/dist/email/resend.js +51 -0
- package/dist/email/resend.js.map +1 -0
- package/dist/email/service.d.ts +55 -0
- package/dist/email/service.d.ts.map +1 -0
- package/dist/email/service.js +87 -0
- package/dist/email/service.js.map +1 -0
- package/dist/email/templates.d.ts +81 -0
- package/dist/email/templates.d.ts.map +1 -0
- package/dist/email/templates.js +84 -0
- package/dist/email/templates.js.map +1 -0
- package/dist/entitlements/index.d.ts +10 -0
- package/dist/entitlements/index.d.ts.map +1 -0
- package/dist/entitlements/index.js +28 -0
- package/dist/entitlements/index.js.map +1 -0
- package/dist/entitlements/service.d.ts +83 -0
- package/dist/entitlements/service.d.ts.map +1 -0
- package/dist/entitlements/service.js +158 -0
- package/dist/entitlements/service.js.map +1 -0
- package/dist/errors.d.ts +85 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +95 -0
- package/dist/errors.js.map +1 -0
- package/dist/events/core-events.d.ts +120 -0
- package/dist/events/core-events.d.ts.map +1 -0
- package/dist/events/core-events.js +109 -0
- package/dist/events/core-events.js.map +1 -0
- package/dist/events/index.d.ts +15 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +58 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/publisher.d.ts +46 -0
- package/dist/events/publisher.d.ts.map +1 -0
- package/dist/events/publisher.js +54 -0
- package/dist/events/publisher.js.map +1 -0
- package/dist/events/registry.d.ts +87 -0
- package/dist/events/registry.d.ts.map +1 -0
- package/dist/events/registry.js +34 -0
- package/dist/events/registry.js.map +1 -0
- package/dist/events/service.d.ts +78 -0
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/service.js +182 -0
- package/dist/events/service.js.map +1 -0
- package/dist/features/bucket.d.ts +22 -0
- package/dist/features/bucket.d.ts.map +1 -0
- package/dist/features/bucket.js +42 -0
- package/dist/features/bucket.js.map +1 -0
- package/dist/features/index.d.ts +15 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +78 -0
- package/dist/features/index.js.map +1 -0
- package/dist/features/registry.d.ts +51 -0
- package/dist/features/registry.d.ts.map +1 -0
- package/dist/features/registry.js +52 -0
- package/dist/features/registry.js.map +1 -0
- package/dist/features/service.d.ts +78 -0
- package/dist/features/service.d.ts.map +1 -0
- package/dist/features/service.js +207 -0
- package/dist/features/service.js.map +1 -0
- package/dist/files/index.d.ts +6 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +64 -0
- package/dist/files/index.js.map +1 -0
- package/dist/files/service.d.ts +92 -0
- package/dist/files/service.d.ts.map +1 -0
- package/dist/files/service.js +303 -0
- package/dist/files/service.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs/backend.d.ts +23 -0
- package/dist/jobs/backend.d.ts.map +1 -0
- package/dist/jobs/backend.js +23 -0
- package/dist/jobs/backend.js.map +1 -0
- package/dist/jobs/index.d.ts +29 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +114 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/service.d.ts +67 -0
- package/dist/jobs/service.d.ts.map +1 -0
- package/dist/jobs/service.js +139 -0
- package/dist/jobs/service.js.map +1 -0
- package/dist/jobs/types.d.ts +78 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +4 -0
- package/dist/jobs/types.js.map +1 -0
- package/dist/logger/default.d.ts +7 -0
- package/dist/logger/default.d.ts.map +1 -0
- package/dist/logger/default.js +28 -0
- package/dist/logger/default.js.map +1 -0
- package/dist/logger/index.d.ts +24 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +90 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/redact.d.ts +40 -0
- package/dist/logger/redact.d.ts.map +1 -0
- package/dist/logger/redact.js +178 -0
- package/dist/logger/redact.js.map +1 -0
- package/dist/logger/service.d.ts +14 -0
- package/dist/logger/service.d.ts.map +1 -0
- package/dist/logger/service.js +56 -0
- package/dist/logger/service.js.map +1 -0
- package/dist/logger/sinks.d.ts +37 -0
- package/dist/logger/sinks.d.ts.map +1 -0
- package/dist/logger/sinks.js +135 -0
- package/dist/logger/sinks.js.map +1 -0
- package/dist/logger/types.d.ts +47 -0
- package/dist/logger/types.d.ts.map +1 -0
- package/dist/logger/types.js +8 -0
- package/dist/logger/types.js.map +1 -0
- package/dist/notifications/index.d.ts +8 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +72 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/service.d.ts +79 -0
- package/dist/notifications/service.d.ts.map +1 -0
- package/dist/notifications/service.js +164 -0
- package/dist/notifications/service.js.map +1 -0
- package/dist/realtime/channels.d.ts +37 -0
- package/dist/realtime/channels.d.ts.map +1 -0
- package/dist/realtime/channels.js +58 -0
- package/dist/realtime/channels.js.map +1 -0
- package/dist/realtime/index.d.ts +19 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/index.js +118 -0
- package/dist/realtime/index.js.map +1 -0
- package/dist/realtime/postgres.d.ts +48 -0
- package/dist/realtime/postgres.d.ts.map +1 -0
- package/dist/realtime/postgres.js +151 -0
- package/dist/realtime/postgres.js.map +1 -0
- package/dist/realtime/service.d.ts +75 -0
- package/dist/realtime/service.d.ts.map +1 -0
- package/dist/realtime/service.js +103 -0
- package/dist/realtime/service.js.map +1 -0
- package/dist/realtime/sse.d.ts +55 -0
- package/dist/realtime/sse.d.ts.map +1 -0
- package/dist/realtime/sse.js +123 -0
- package/dist/realtime/sse.js.map +1 -0
- package/dist/realtime/transport.d.ts +52 -0
- package/dist/realtime/transport.d.ts.map +1 -0
- package/dist/realtime/transport.js +2 -0
- package/dist/realtime/transport.js.map +1 -0
- package/dist/storage/index.d.ts +18 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +89 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/keys.d.ts +50 -0
- package/dist/storage/keys.d.ts.map +1 -0
- package/dist/storage/keys.js +120 -0
- package/dist/storage/keys.js.map +1 -0
- package/dist/storage/memory.d.ts +32 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +84 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/provider.d.ts +93 -0
- package/dist/storage/provider.d.ts.map +1 -0
- package/dist/storage/provider.js +2 -0
- package/dist/storage/provider.js.map +1 -0
- package/dist/storage/s3.d.ts +34 -0
- package/dist/storage/s3.d.ts.map +1 -0
- package/dist/storage/s3.js +175 -0
- package/dist/storage/s3.js.map +1 -0
- package/dist/storage/service.d.ts +86 -0
- package/dist/storage/service.d.ts.map +1 -0
- package/dist/storage/service.js +107 -0
- package/dist/storage/service.js.map +1 -0
- package/dist/storage/sigv4.d.ts +58 -0
- package/dist/storage/sigv4.d.ts.map +1 -0
- package/dist/storage/sigv4.js +114 -0
- package/dist/storage/sigv4.js.map +1 -0
- package/dist/streams/client.d.ts +91 -0
- package/dist/streams/client.d.ts.map +1 -0
- package/dist/streams/client.js +128 -0
- package/dist/streams/client.js.map +1 -0
- package/dist/streams/events.d.ts +68 -0
- package/dist/streams/events.d.ts.map +1 -0
- package/dist/streams/events.js +81 -0
- package/dist/streams/events.js.map +1 -0
- package/dist/streams/index.d.ts +47 -0
- package/dist/streams/index.d.ts.map +1 -0
- package/dist/streams/index.js +47 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams/realtime.d.ts +27 -0
- package/dist/streams/realtime.d.ts.map +1 -0
- package/dist/streams/realtime.js +50 -0
- package/dist/streams/realtime.js.map +1 -0
- package/dist/streams/state.d.ts +26 -0
- package/dist/streams/state.d.ts.map +1 -0
- package/dist/streams/state.js +34 -0
- package/dist/streams/state.js.map +1 -0
- package/dist/users/index.d.ts +9 -0
- package/dist/users/index.d.ts.map +1 -0
- package/dist/users/index.js +23 -0
- package/dist/users/index.js.map +1 -0
- package/dist/users/service.d.ts +43 -0
- package/dist/users/service.d.ts.map +1 -0
- package/dist/users/service.js +113 -0
- package/dist/users/service.js.map +1 -0
- package/dist/webhooks/inbound.d.ts +70 -0
- package/dist/webhooks/inbound.d.ts.map +1 -0
- package/dist/webhooks/inbound.js +146 -0
- package/dist/webhooks/inbound.js.map +1 -0
- package/dist/webhooks/index.d.ts +12 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +65 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/outgoing.d.ts +89 -0
- package/dist/webhooks/outgoing.d.ts.map +1 -0
- package/dist/webhooks/outgoing.js +375 -0
- package/dist/webhooks/outgoing.js.map +1 -0
- package/dist/webhooks/signature.d.ts +40 -0
- package/dist/webhooks/signature.d.ts.map +1 -0
- package/dist/webhooks/signature.js +77 -0
- package/dist/webhooks/signature.js.map +1 -0
- package/dist/workspaces/context.d.ts +30 -0
- package/dist/workspaces/context.d.ts.map +1 -0
- package/dist/workspaces/context.js +12 -0
- package/dist/workspaces/context.js.map +1 -0
- package/dist/workspaces/index.d.ts +5 -0
- package/dist/workspaces/index.d.ts.map +1 -0
- package/dist/workspaces/index.js +21 -0
- package/dist/workspaces/index.js.map +1 -0
- package/dist/workspaces/service.d.ts +126 -0
- package/dist/workspaces/service.d.ts.map +1 -0
- package/dist/workspaces/service.js +495 -0
- package/dist/workspaces/service.js.map +1 -0
- package/docs/README.md +71 -0
- package/docs/api-keys.md +184 -0
- package/docs/audit.md +198 -0
- package/docs/auth.md +196 -0
- package/docs/authorization.md +225 -0
- package/docs/billing.md +293 -0
- package/docs/cli.md +238 -0
- package/docs/config.md +163 -0
- package/docs/db.md +219 -0
- package/docs/email.md +222 -0
- package/docs/entitlements.md +194 -0
- package/docs/errors.md +158 -0
- package/docs/events.md +279 -0
- package/docs/feature-flags.md +237 -0
- package/docs/files.md +229 -0
- package/docs/jobs.md +288 -0
- package/docs/logger.md +269 -0
- package/docs/notifications.md +179 -0
- package/docs/publishing.md +128 -0
- package/docs/realtime.md +360 -0
- package/docs/storage.md +279 -0
- package/docs/streams.md +296 -0
- package/docs/users.md +117 -0
- package/docs/webhooks.md +272 -0
- package/docs/workspaces.md +269 -0
- package/drizzle/0000_init_users.sql +21 -0
- package/drizzle/0001_workspaces_roles_permissions.sql +86 -0
- package/drizzle/0002_billing_entitlements.sql +97 -0
- package/drizzle/0003_events.sql +17 -0
- package/drizzle/0004_notifications_audit.sql +64 -0
- package/drizzle/0005_api_keys_webhooks.sql +77 -0
- package/drizzle/0006_feature_flags.sql +22 -0
- package/drizzle/0007_files.sql +23 -0
- package/drizzle/meta/0000_snapshot.json +160 -0
- package/drizzle/meta/0001_snapshot.json +837 -0
- package/drizzle/meta/0002_snapshot.json +1580 -0
- package/drizzle/meta/0003_snapshot.json +1751 -0
- package/drizzle/meta/0004_snapshot.json +2109 -0
- package/drizzle/meta/0005_snapshot.json +2719 -0
- package/drizzle/meta/0006_snapshot.json +2909 -0
- package/drizzle/meta/0007_snapshot.json +3113 -0
- package/drizzle/meta/_journal.json +62 -0
- package/package.json +180 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { CoreDatabase } from "../db/types.ts";
|
|
2
|
+
import { type CoreEventPublisher } from "../events/publisher.ts";
|
|
3
|
+
import type { Role } from "../db/schema/index.ts";
|
|
4
|
+
import type { WorkspaceContext } from "../workspaces/context.ts";
|
|
5
|
+
/**
|
|
6
|
+
* A permission identifier such as `"projects.create"`.
|
|
7
|
+
*
|
|
8
|
+
* Applications define their own vocabulary. Declare it with
|
|
9
|
+
* {@link definePermissions} to get a union type and autocompletion; the core
|
|
10
|
+
* itself accepts any string, because it cannot know a product's permissions.
|
|
11
|
+
*/
|
|
12
|
+
export type PermissionKey = string;
|
|
13
|
+
export interface PermissionDefinition {
|
|
14
|
+
readonly key: string;
|
|
15
|
+
readonly description?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface CreateRoleInput {
|
|
18
|
+
readonly key: string;
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly description?: string;
|
|
21
|
+
readonly isDefault?: boolean;
|
|
22
|
+
readonly permissions?: readonly PermissionKey[];
|
|
23
|
+
}
|
|
24
|
+
export interface AuthorizationService {
|
|
25
|
+
/**
|
|
26
|
+
* Register the application's permission vocabulary.
|
|
27
|
+
*
|
|
28
|
+
* Idempotent, so it is safe to call on every boot or from a migration step.
|
|
29
|
+
* Permissions must exist before a role can be granted them.
|
|
30
|
+
*/
|
|
31
|
+
registerPermissions(definitions: readonly PermissionDefinition[]): Promise<void>;
|
|
32
|
+
/** Every permission the user holds in this workspace: the union across their roles. */
|
|
33
|
+
getEffectivePermissions(context: WorkspaceContext): Promise<Set<PermissionKey>>;
|
|
34
|
+
/** Whether the user holds `permission` in this workspace. */
|
|
35
|
+
can(context: WorkspaceContext, permission: PermissionKey): Promise<boolean>;
|
|
36
|
+
/** True only when the user holds *every* listed permission. */
|
|
37
|
+
canAll(context: WorkspaceContext, required: readonly PermissionKey[]): Promise<boolean>;
|
|
38
|
+
/** True when the user holds *any* of the listed permissions. */
|
|
39
|
+
canAny(context: WorkspaceContext, required: readonly PermissionKey[]): Promise<boolean>;
|
|
40
|
+
/** As {@link can}, but throws {@link ForbiddenError}. */
|
|
41
|
+
require(context: WorkspaceContext, permission: PermissionKey): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Whether the user holds a role with this key in this workspace.
|
|
44
|
+
*
|
|
45
|
+
* Prefer permissions. Role checks couple application logic to the role
|
|
46
|
+
* structure, so renaming or restructuring roles silently changes behaviour.
|
|
47
|
+
*/
|
|
48
|
+
hasRole(context: WorkspaceContext, roleKey: string): Promise<boolean>;
|
|
49
|
+
/** The user's roles in this workspace. */
|
|
50
|
+
getRoles(context: WorkspaceContext): Promise<Role[]>;
|
|
51
|
+
readonly roles: {
|
|
52
|
+
list(context: WorkspaceContext): Promise<Role[]>;
|
|
53
|
+
create(context: WorkspaceContext, input: CreateRoleInput): Promise<Role>;
|
|
54
|
+
/** Replace a role's permission set. */
|
|
55
|
+
setPermissions(context: WorkspaceContext, roleId: string, keys: readonly PermissionKey[]): Promise<void>;
|
|
56
|
+
/** Delete a custom role. System roles are protected. */
|
|
57
|
+
delete(context: WorkspaceContext, roleId: string): Promise<void>;
|
|
58
|
+
assign(context: WorkspaceContext, input: {
|
|
59
|
+
userId: string;
|
|
60
|
+
roleId: string;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
unassign(context: WorkspaceContext, input: {
|
|
63
|
+
userId: string;
|
|
64
|
+
roleId: string;
|
|
65
|
+
}): Promise<void>;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export interface AuthorizationServiceDeps {
|
|
69
|
+
readonly db: CoreDatabase;
|
|
70
|
+
/** Optional event publisher. Omit it and the service emits nothing. */
|
|
71
|
+
readonly events?: CoreEventPublisher;
|
|
72
|
+
}
|
|
73
|
+
export declare function createAuthorizationService(deps: AuthorizationServiceDeps): AuthorizationService;
|
|
74
|
+
/**
|
|
75
|
+
* Declare an application's permission vocabulary and get a union type for it.
|
|
76
|
+
*
|
|
77
|
+
* ```ts
|
|
78
|
+
* export const PERMISSIONS = definePermissions([
|
|
79
|
+
* "projects.read",
|
|
80
|
+
* "projects.create",
|
|
81
|
+
* ] as const);
|
|
82
|
+
*
|
|
83
|
+
* type AppPermission = (typeof PERMISSIONS)[number]; // "projects.read" | "projects.create"
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* Purely a typing helper — it performs no I/O. Pass the same list to
|
|
87
|
+
* `registerPermissions()` to persist it.
|
|
88
|
+
*/
|
|
89
|
+
export declare function definePermissions<const T extends readonly string[]>(keys: T): T;
|
|
90
|
+
/** Look up which roles in a workspace carry a given permission. */
|
|
91
|
+
export declare function findRolesWithPermission(db: CoreDatabase, workspaceId: string, permission: PermissionKey): Promise<Role[]>;
|
|
92
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/authorization/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { and, eq, inArray } from "drizzle-orm";
|
|
2
|
+
import { ForbiddenError, NotFoundError } from "../errors.js";
|
|
3
|
+
import { isForeignKeyViolation } from "../db/errors.js";
|
|
4
|
+
import { permissions as permissionsTable, rolePermissions, roles, workspaceMemberRoles, } from "../db/schema/index.js";
|
|
5
|
+
import { EventOutbox } from "../events/publisher.js";
|
|
6
|
+
export function createAuthorizationService(deps) {
|
|
7
|
+
const outbox = () => new EventOutbox(deps.events);
|
|
8
|
+
/**
|
|
9
|
+
* Confirm a role belongs to the caller's workspace before it is used.
|
|
10
|
+
*
|
|
11
|
+
* The database rejects cross-workspace *grants* outright, but reads and
|
|
12
|
+
* deletes need this check so a role id from another tenant cannot be probed
|
|
13
|
+
* or modified.
|
|
14
|
+
*/
|
|
15
|
+
async function requireOwnRole(context, roleId) {
|
|
16
|
+
const rows = await deps.db
|
|
17
|
+
.select()
|
|
18
|
+
.from(roles)
|
|
19
|
+
.where(and(eq(roles.id, roleId), eq(roles.workspaceId, context.workspaceId)))
|
|
20
|
+
.limit(1);
|
|
21
|
+
const role = rows[0];
|
|
22
|
+
if (role === undefined) {
|
|
23
|
+
throw new NotFoundError("Role not found.");
|
|
24
|
+
}
|
|
25
|
+
return role;
|
|
26
|
+
}
|
|
27
|
+
const roleAdmin = {
|
|
28
|
+
async list(context) {
|
|
29
|
+
return deps.db.select().from(roles).where(eq(roles.workspaceId, context.workspaceId));
|
|
30
|
+
},
|
|
31
|
+
async create(context, input) {
|
|
32
|
+
return deps.db.transaction(async (tx) => {
|
|
33
|
+
const [role] = await tx
|
|
34
|
+
.insert(roles)
|
|
35
|
+
.values({
|
|
36
|
+
workspaceId: context.workspaceId,
|
|
37
|
+
key: input.key,
|
|
38
|
+
name: input.name,
|
|
39
|
+
description: input.description ?? null,
|
|
40
|
+
isDefault: input.isDefault ?? false,
|
|
41
|
+
isSystem: false,
|
|
42
|
+
})
|
|
43
|
+
.returning();
|
|
44
|
+
if (input.permissions !== undefined && input.permissions.length > 0) {
|
|
45
|
+
await tx.insert(rolePermissions).values(input.permissions.map((permissionKey) => ({
|
|
46
|
+
roleId: role.id,
|
|
47
|
+
permissionKey,
|
|
48
|
+
})));
|
|
49
|
+
}
|
|
50
|
+
return role;
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
async setPermissions(context, roleId, keys) {
|
|
54
|
+
await requireOwnRole(context, roleId);
|
|
55
|
+
await deps.db.transaction(async (tx) => {
|
|
56
|
+
await tx.delete(rolePermissions).where(eq(rolePermissions.roleId, roleId));
|
|
57
|
+
if (keys.length > 0) {
|
|
58
|
+
await tx
|
|
59
|
+
.insert(rolePermissions)
|
|
60
|
+
.values(keys.map((permissionKey) => ({ roleId, permissionKey })));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
async delete(context, roleId) {
|
|
65
|
+
const role = await requireOwnRole(context, roleId);
|
|
66
|
+
/**
|
|
67
|
+
* System roles are created with the workspace and other code assumes they
|
|
68
|
+
* exist — deleting `admin` would leave nobody able to administer it.
|
|
69
|
+
*/
|
|
70
|
+
if (role.isSystem) {
|
|
71
|
+
throw new ForbiddenError("System roles cannot be deleted.");
|
|
72
|
+
}
|
|
73
|
+
await deps.db.delete(roles).where(eq(roles.id, roleId));
|
|
74
|
+
},
|
|
75
|
+
async assign(context, input) {
|
|
76
|
+
const events = outbox();
|
|
77
|
+
try {
|
|
78
|
+
const granted = await deps.db.transaction(async (tx) => {
|
|
79
|
+
const inserted = await tx
|
|
80
|
+
.insert(workspaceMemberRoles)
|
|
81
|
+
.values({
|
|
82
|
+
workspaceId: context.workspaceId,
|
|
83
|
+
userId: input.userId,
|
|
84
|
+
roleId: input.roleId,
|
|
85
|
+
})
|
|
86
|
+
.onConflictDoNothing()
|
|
87
|
+
.returning({ roleId: workspaceMemberRoles.roleId });
|
|
88
|
+
// Nothing inserted means the role was already held; not a change, so
|
|
89
|
+
// no event. Re-announcing it would make consumers act twice.
|
|
90
|
+
if (inserted.length === 0) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
await events.record({
|
|
94
|
+
type: "workspace.member.role.updated",
|
|
95
|
+
workspaceId: context.workspaceId,
|
|
96
|
+
actorId: context.userId,
|
|
97
|
+
data: { userId: input.userId, roleId: input.roleId, action: "assigned" },
|
|
98
|
+
}, tx);
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
if (granted) {
|
|
102
|
+
await events.flush();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
/**
|
|
107
|
+
* Both failures are the database enforcing tenancy: the role belongs to
|
|
108
|
+
* another workspace, or the user is not a member of this one. Neither
|
|
109
|
+
* should be reported as an unexpected error.
|
|
110
|
+
*/
|
|
111
|
+
if (isForeignKeyViolation(error, "workspace_member_roles_role_fk") ||
|
|
112
|
+
isForeignKeyViolation(error, "workspace_member_roles_member_fk")) {
|
|
113
|
+
throw new NotFoundError("Role or workspace member not found.");
|
|
114
|
+
}
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
async unassign(context, input) {
|
|
119
|
+
const removed = await deps.db
|
|
120
|
+
.delete(workspaceMemberRoles)
|
|
121
|
+
.where(and(eq(workspaceMemberRoles.workspaceId, context.workspaceId), eq(workspaceMemberRoles.userId, input.userId), eq(workspaceMemberRoles.roleId, input.roleId)))
|
|
122
|
+
.returning({ roleId: workspaceMemberRoles.roleId });
|
|
123
|
+
if (removed.length === 0) {
|
|
124
|
+
throw new NotFoundError("Role assignment not found.");
|
|
125
|
+
}
|
|
126
|
+
const events = outbox();
|
|
127
|
+
await events.record({
|
|
128
|
+
type: "workspace.member.role.updated",
|
|
129
|
+
workspaceId: context.workspaceId,
|
|
130
|
+
actorId: context.userId,
|
|
131
|
+
data: { userId: input.userId, roleId: input.roleId, action: "unassigned" },
|
|
132
|
+
}, deps.db);
|
|
133
|
+
await events.flush();
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
const service = {
|
|
137
|
+
async registerPermissions(definitions) {
|
|
138
|
+
if (definitions.length === 0) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
await deps.db
|
|
142
|
+
.insert(permissionsTable)
|
|
143
|
+
.values(definitions.map((definition) => ({
|
|
144
|
+
key: definition.key,
|
|
145
|
+
description: definition.description ?? null,
|
|
146
|
+
})))
|
|
147
|
+
.onConflictDoUpdate({
|
|
148
|
+
target: permissionsTable.key,
|
|
149
|
+
set: { updatedAt: new Date() },
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
async getEffectivePermissions(context) {
|
|
153
|
+
/**
|
|
154
|
+
* A single join from the member's grants through their roles to the
|
|
155
|
+
* permissions those roles carry. The union is inherent: `select distinct`
|
|
156
|
+
* over all matching rows, so holding several roles simply widens the set.
|
|
157
|
+
*
|
|
158
|
+
* Both halves of the join to `roles` are constrained by workspace, so a
|
|
159
|
+
* stale grant could not contribute another tenant's permissions.
|
|
160
|
+
*/
|
|
161
|
+
const rows = await deps.db
|
|
162
|
+
.selectDistinct({ key: rolePermissions.permissionKey })
|
|
163
|
+
.from(workspaceMemberRoles)
|
|
164
|
+
.innerJoin(roles, and(eq(roles.id, workspaceMemberRoles.roleId), eq(roles.workspaceId, workspaceMemberRoles.workspaceId)))
|
|
165
|
+
.innerJoin(rolePermissions, eq(rolePermissions.roleId, roles.id))
|
|
166
|
+
.where(and(eq(workspaceMemberRoles.workspaceId, context.workspaceId), eq(workspaceMemberRoles.userId, context.userId)));
|
|
167
|
+
return new Set(rows.map((row) => row.key));
|
|
168
|
+
},
|
|
169
|
+
async can(context, permission) {
|
|
170
|
+
const held = await service.getEffectivePermissions(context);
|
|
171
|
+
return held.has(permission);
|
|
172
|
+
},
|
|
173
|
+
async canAll(context, required) {
|
|
174
|
+
if (required.length === 0) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
const held = await service.getEffectivePermissions(context);
|
|
178
|
+
return required.every((permission) => held.has(permission));
|
|
179
|
+
},
|
|
180
|
+
async canAny(context, required) {
|
|
181
|
+
if (required.length === 0) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
const held = await service.getEffectivePermissions(context);
|
|
185
|
+
return required.some((permission) => held.has(permission));
|
|
186
|
+
},
|
|
187
|
+
async require(context, permission) {
|
|
188
|
+
if (!(await service.can(context, permission))) {
|
|
189
|
+
throw new ForbiddenError(`Missing permission "${permission}".`, { permission });
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
async hasRole(context, roleKey) {
|
|
193
|
+
const rows = await deps.db
|
|
194
|
+
.select({ id: roles.id })
|
|
195
|
+
.from(workspaceMemberRoles)
|
|
196
|
+
.innerJoin(roles, and(eq(roles.id, workspaceMemberRoles.roleId), eq(roles.workspaceId, workspaceMemberRoles.workspaceId)))
|
|
197
|
+
.where(and(eq(workspaceMemberRoles.workspaceId, context.workspaceId), eq(workspaceMemberRoles.userId, context.userId), eq(roles.key, roleKey)))
|
|
198
|
+
.limit(1);
|
|
199
|
+
return rows.length > 0;
|
|
200
|
+
},
|
|
201
|
+
async getRoles(context) {
|
|
202
|
+
return deps.db
|
|
203
|
+
.select({
|
|
204
|
+
id: roles.id,
|
|
205
|
+
workspaceId: roles.workspaceId,
|
|
206
|
+
key: roles.key,
|
|
207
|
+
name: roles.name,
|
|
208
|
+
description: roles.description,
|
|
209
|
+
isSystem: roles.isSystem,
|
|
210
|
+
isDefault: roles.isDefault,
|
|
211
|
+
createdAt: roles.createdAt,
|
|
212
|
+
updatedAt: roles.updatedAt,
|
|
213
|
+
})
|
|
214
|
+
.from(workspaceMemberRoles)
|
|
215
|
+
.innerJoin(roles, and(eq(roles.id, workspaceMemberRoles.roleId), eq(roles.workspaceId, workspaceMemberRoles.workspaceId)))
|
|
216
|
+
.where(and(eq(workspaceMemberRoles.workspaceId, context.workspaceId), eq(workspaceMemberRoles.userId, context.userId)));
|
|
217
|
+
},
|
|
218
|
+
roles: roleAdmin,
|
|
219
|
+
};
|
|
220
|
+
return service;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Declare an application's permission vocabulary and get a union type for it.
|
|
224
|
+
*
|
|
225
|
+
* ```ts
|
|
226
|
+
* export const PERMISSIONS = definePermissions([
|
|
227
|
+
* "projects.read",
|
|
228
|
+
* "projects.create",
|
|
229
|
+
* ] as const);
|
|
230
|
+
*
|
|
231
|
+
* type AppPermission = (typeof PERMISSIONS)[number]; // "projects.read" | "projects.create"
|
|
232
|
+
* ```
|
|
233
|
+
*
|
|
234
|
+
* Purely a typing helper — it performs no I/O. Pass the same list to
|
|
235
|
+
* `registerPermissions()` to persist it.
|
|
236
|
+
*/
|
|
237
|
+
export function definePermissions(keys) {
|
|
238
|
+
return keys;
|
|
239
|
+
}
|
|
240
|
+
/** Look up which roles in a workspace carry a given permission. */
|
|
241
|
+
export async function findRolesWithPermission(db, workspaceId, permission) {
|
|
242
|
+
const grantedRoleIds = await db
|
|
243
|
+
.select({ roleId: rolePermissions.roleId })
|
|
244
|
+
.from(rolePermissions)
|
|
245
|
+
.where(eq(rolePermissions.permissionKey, permission));
|
|
246
|
+
if (grantedRoleIds.length === 0) {
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
return db
|
|
250
|
+
.select()
|
|
251
|
+
.from(roles)
|
|
252
|
+
.where(and(eq(roles.workspaceId, workspaceId), inArray(roles.id, grantedRoleIds.map((row) => row.roleId))));
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/authorization/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type BillingService } from "./service.ts";
|
|
2
|
+
import type { BillingProvider } from "./provider.ts";
|
|
3
|
+
export { assertEntitlementDefinitions, countFailedBillingEvents, createBillingService, InvalidWebhookSignatureError, type BillingService, type BillingServiceDeps, type CreateCheckoutOptions, type EntitlementDefinition, type SubscriptionWithPlan, type UpsertPlanInput, type WebhookOutcome, type WebhookRequest, } from "./service.ts";
|
|
4
|
+
export type { BillingProvider, CancelSubscriptionInput, CheckoutSession, CreateCheckoutInput, CreateCustomerInput, PortalInput, ProviderCustomer, ProviderSubscriptionState, ProviderWebhookEvent, WebhookVerificationInput, } from "./provider.ts";
|
|
5
|
+
export { createRevolutProvider, mapRevolutStatus, verifyRevolutSignature, type RevolutAdapterOptions, } from "./revolut.ts";
|
|
6
|
+
/** The Revolut provider, built from the `billing` configuration section. */
|
|
7
|
+
export declare function createConfiguredProvider(): BillingProvider;
|
|
8
|
+
/**
|
|
9
|
+
* Default service, bound to configuration and the Revolut adapter.
|
|
10
|
+
*
|
|
11
|
+
* Both dependencies are getters, so the `billing` and `database` configuration
|
|
12
|
+
* sections resolve on first use rather than at import — importing this module
|
|
13
|
+
* does not require Revolut credentials.
|
|
14
|
+
*/
|
|
15
|
+
export declare const billing: BillingService;
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/billing/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,oBAAoB,EACpB,4BAA4B,EAC5B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,eAAe,EACf,uBAAuB,EACvB,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,gBAAgB,EAChB,yBAAyB,EACzB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AAEtB,4EAA4E;AAC5E,wBAAgB,wBAAwB,IAAI,eAAe,CAO1D;AAID;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,cASpB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public billing surface: `@digimuza/saas-core/billing`. Server-only.
|
|
3
|
+
*
|
|
4
|
+
* Provider-independent by construction: everything above {@link BillingProvider}
|
|
5
|
+
* is generic, so adopting a second payment provider means writing an adapter.
|
|
6
|
+
* Application code must never inspect a plan name to decide what a customer may
|
|
7
|
+
* do — ask `@digimuza/saas-core/entitlements` instead.
|
|
8
|
+
*
|
|
9
|
+
* Billing state changes only from verified webhook events, never from the
|
|
10
|
+
* browser's return trip after checkout: that redirect is attacker-controllable
|
|
11
|
+
* and proves nothing about payment.
|
|
12
|
+
*/
|
|
13
|
+
import { config } from "../config/index.js";
|
|
14
|
+
import { getDatabase } from "../db/client.js";
|
|
15
|
+
import { events as coreEventsService } from "../events/index.js";
|
|
16
|
+
import { createBillingService } from "./service.js";
|
|
17
|
+
import { createRevolutProvider } from "./revolut.js";
|
|
18
|
+
export { assertEntitlementDefinitions, countFailedBillingEvents, createBillingService, InvalidWebhookSignatureError, } from "./service.js";
|
|
19
|
+
export { createRevolutProvider, mapRevolutStatus, verifyRevolutSignature, } from "./revolut.js";
|
|
20
|
+
/** The Revolut provider, built from the `billing` configuration section. */
|
|
21
|
+
export function createConfiguredProvider() {
|
|
22
|
+
const billing = config.billing;
|
|
23
|
+
return createRevolutProvider({
|
|
24
|
+
apiKey: billing.apiKey,
|
|
25
|
+
webhookSecret: billing.webhookSecret,
|
|
26
|
+
apiUrl: billing.apiUrl,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
let configuredProvider;
|
|
30
|
+
/**
|
|
31
|
+
* Default service, bound to configuration and the Revolut adapter.
|
|
32
|
+
*
|
|
33
|
+
* Both dependencies are getters, so the `billing` and `database` configuration
|
|
34
|
+
* sections resolve on first use rather than at import — importing this module
|
|
35
|
+
* does not require Revolut credentials.
|
|
36
|
+
*/
|
|
37
|
+
export const billing = createBillingService({
|
|
38
|
+
get db() {
|
|
39
|
+
return getDatabase();
|
|
40
|
+
},
|
|
41
|
+
get provider() {
|
|
42
|
+
configuredProvider ??= createConfiguredProvider();
|
|
43
|
+
return configuredProvider;
|
|
44
|
+
},
|
|
45
|
+
events: coreEventsService,
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/billing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAuB,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,oBAAoB,EACpB,4BAA4B,GAS7B,MAAM,cAAc,CAAC;AAetB,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,GAEvB,MAAM,cAAc,CAAC;AAEtB,4EAA4E;AAC5E,MAAM,UAAU,wBAAwB;IACtC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC/B,OAAO,qBAAqB,CAAC;QAC3B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;AACL,CAAC;AAED,IAAI,kBAA+C,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,OAAO,GAAmB,oBAAoB,CAAC;IAC1D,IAAI,EAAE;QACJ,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IACD,IAAI,QAAQ;QACV,kBAAkB,KAAK,wBAAwB,EAAE,CAAC;QAClD,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IACD,MAAM,EAAE,iBAAiB;CAC1B,CAAC,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Plan, SubscriptionStatus } from "../db/schema/index.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The seam between the core and a payment provider.
|
|
4
|
+
*
|
|
5
|
+
* Everything above this interface is provider-independent, so adopting a second
|
|
6
|
+
* provider means writing an adapter rather than touching the schema, the
|
|
7
|
+
* services or any application code.
|
|
8
|
+
*
|
|
9
|
+
* Not every provider offers every capability. `cancelSubscription` and
|
|
10
|
+
* `getPortalUrl` are optional: Revolut has no hosted customer portal and no
|
|
11
|
+
* server-side subscription object to cancel, and pretending otherwise would mean
|
|
12
|
+
* shipping methods that silently do nothing. The billing service handles their
|
|
13
|
+
* absence explicitly.
|
|
14
|
+
*/
|
|
15
|
+
export interface BillingProvider {
|
|
16
|
+
/** Stored on every row this provider produces, e.g. `"revolut"`. */
|
|
17
|
+
readonly name: string;
|
|
18
|
+
createCustomer(input: CreateCustomerInput): Promise<ProviderCustomer>;
|
|
19
|
+
createCheckout(input: CreateCheckoutInput): Promise<CheckoutSession>;
|
|
20
|
+
/** Omit when the provider has no server-side subscription to cancel. */
|
|
21
|
+
cancelSubscription?(input: CancelSubscriptionInput): Promise<void>;
|
|
22
|
+
/** Omit when the provider has no hosted billing portal. */
|
|
23
|
+
getPortalUrl?(input: PortalInput): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Verify a webhook's authenticity.
|
|
26
|
+
*
|
|
27
|
+
* Must be given the **raw** request body. Parsing and re-serialising JSON
|
|
28
|
+
* changes key order and whitespace, which invalidates the signature — so a
|
|
29
|
+
* route handler has to read the body as text before touching it.
|
|
30
|
+
*/
|
|
31
|
+
verifyWebhook(input: WebhookVerificationInput): boolean;
|
|
32
|
+
/** Translate a verified webhook body into a provider-independent event. */
|
|
33
|
+
parseWebhookEvent(rawBody: string): ProviderWebhookEvent;
|
|
34
|
+
}
|
|
35
|
+
export interface ProviderCustomer {
|
|
36
|
+
readonly providerCustomerId: string;
|
|
37
|
+
}
|
|
38
|
+
export interface CreateCustomerInput {
|
|
39
|
+
readonly workspaceId: string;
|
|
40
|
+
readonly workspaceName: string;
|
|
41
|
+
readonly email?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface CreateCheckoutInput {
|
|
44
|
+
readonly workspaceId: string;
|
|
45
|
+
readonly providerCustomerId: string;
|
|
46
|
+
readonly plan: Plan;
|
|
47
|
+
readonly successUrl: string;
|
|
48
|
+
readonly cancelUrl: string;
|
|
49
|
+
readonly quantity?: number;
|
|
50
|
+
/** Echoed back on webhooks, where the provider supports it. */
|
|
51
|
+
readonly metadata?: Record<string, string>;
|
|
52
|
+
}
|
|
53
|
+
export interface CheckoutSession {
|
|
54
|
+
/** Where to send the customer to pay. */
|
|
55
|
+
readonly url: string;
|
|
56
|
+
readonly providerCheckoutId?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface CancelSubscriptionInput {
|
|
59
|
+
readonly providerSubscriptionId: string;
|
|
60
|
+
/** Cancel at the end of the paid period rather than immediately. */
|
|
61
|
+
readonly atPeriodEnd: boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface PortalInput {
|
|
64
|
+
readonly providerCustomerId: string;
|
|
65
|
+
readonly returnUrl: string;
|
|
66
|
+
}
|
|
67
|
+
export interface WebhookVerificationInput {
|
|
68
|
+
/** The request body exactly as received, before any parsing. */
|
|
69
|
+
readonly rawBody: string;
|
|
70
|
+
readonly signature: string;
|
|
71
|
+
/** Provider timestamp header, used for replay protection. */
|
|
72
|
+
readonly timestamp?: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Normalised subscription state carried by a webhook.
|
|
76
|
+
*
|
|
77
|
+
* The adapter maps the provider's status vocabulary onto
|
|
78
|
+
* {@link SubscriptionStatus}, so application queries never see provider strings.
|
|
79
|
+
*
|
|
80
|
+
* Date fields must be real `Date` instances. Providers send them as ISO strings,
|
|
81
|
+
* and converting them is the adapter's job — passing a string through would fail
|
|
82
|
+
* deep inside the database driver with an error that names neither the field nor
|
|
83
|
+
* the adapter.
|
|
84
|
+
*/
|
|
85
|
+
export interface ProviderSubscriptionState {
|
|
86
|
+
readonly providerSubscriptionId: string;
|
|
87
|
+
readonly providerCustomerId: string;
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the plan by `plans.provider_price_id` — for providers with a price
|
|
90
|
+
* catalogue (Stripe, Paddle).
|
|
91
|
+
*/
|
|
92
|
+
readonly providerPriceId?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Resolve the plan by `plans.key` — for providers with no catalogue, where the
|
|
95
|
+
* plan is carried in a reference we set when creating the payment. Consulted
|
|
96
|
+
* only when {@link providerPriceId} is absent.
|
|
97
|
+
*/
|
|
98
|
+
readonly planKey?: string;
|
|
99
|
+
readonly status: SubscriptionStatus;
|
|
100
|
+
readonly currentPeriodStart?: Date;
|
|
101
|
+
readonly currentPeriodEnd?: Date;
|
|
102
|
+
readonly cancelAtPeriodEnd?: boolean;
|
|
103
|
+
readonly canceledAt?: Date;
|
|
104
|
+
readonly trialEndsAt?: Date;
|
|
105
|
+
}
|
|
106
|
+
export interface ProviderWebhookEvent {
|
|
107
|
+
/** Provider's own event id. The idempotency key for ingestion. */
|
|
108
|
+
readonly providerEventId: string;
|
|
109
|
+
readonly type: string;
|
|
110
|
+
readonly payload: unknown;
|
|
111
|
+
/** Present when the event changes subscription state. */
|
|
112
|
+
readonly subscription?: ProviderSubscriptionState;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/billing/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEtE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAEtE,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAErE,wEAAwE;IACxE,kBAAkB,CAAC,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE,2DAA2D;IAC3D,YAAY,CAAC,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnD;;;;;;OAMG;IACH,aAAa,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC;IAExD,2EAA2E;IAC3E,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAAC;CAC1D;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,IAAI,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAC;IACjC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,kEAAkE;IAClE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,YAAY,CAAC,EAAE,yBAAyB,CAAC;CACnD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/billing/provider.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { SubscriptionStatus } from "../db/schema/index.ts";
|
|
2
|
+
import type { BillingProvider } from "./provider.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Revolut Merchant API adapter.
|
|
5
|
+
*
|
|
6
|
+
* ## Capability boundaries
|
|
7
|
+
*
|
|
8
|
+
* Revolut Merchant is an order-and-payment API, not a subscription platform. It
|
|
9
|
+
* has no hosted customer portal and no server-side subscription object, so
|
|
10
|
+
* `getPortalUrl` and `cancelSubscription` are deliberately absent from this
|
|
11
|
+
* adapter rather than stubbed. Recurring charges on Revolut are
|
|
12
|
+
* merchant-initiated: the core owns subscription state and drives the charges,
|
|
13
|
+
* and cancellation is therefore a local state change.
|
|
14
|
+
*
|
|
15
|
+
* ## Verification status
|
|
16
|
+
*
|
|
17
|
+
* The webhook signature scheme is fully implemented and unit-tested against the
|
|
18
|
+
* documented algorithm. The HTTP calls are written against the documented
|
|
19
|
+
* endpoints but have **not** been exercised against live Revolut credentials.
|
|
20
|
+
* Treat `createCustomer` and `createCheckout` as unverified until they have run
|
|
21
|
+
* against a sandbox account.
|
|
22
|
+
*/
|
|
23
|
+
export interface RevolutAdapterOptions {
|
|
24
|
+
readonly apiKey: string;
|
|
25
|
+
readonly webhookSecret: string;
|
|
26
|
+
/** Base URL. Sandbox differs from production. */
|
|
27
|
+
readonly apiUrl: string;
|
|
28
|
+
/**
|
|
29
|
+
* Reject webhooks whose timestamp is older than this, in seconds.
|
|
30
|
+
*
|
|
31
|
+
* Replay protection: a signature stays valid forever, so without a freshness
|
|
32
|
+
* window a captured request could be replayed indefinitely.
|
|
33
|
+
*/
|
|
34
|
+
readonly timestampToleranceSeconds?: number;
|
|
35
|
+
/** Injected for tests. Defaults to the global `fetch`. */
|
|
36
|
+
readonly fetchImpl?: typeof fetch;
|
|
37
|
+
/** Injected for tests, so signature freshness is deterministic. */
|
|
38
|
+
readonly now?: () => number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Map Revolut order/payment states onto the core's vocabulary.
|
|
42
|
+
*
|
|
43
|
+
* Unknown states deliberately fall through to `incomplete` rather than `active`:
|
|
44
|
+
* treating an unrecognised state as paid would grant entitlements the customer
|
|
45
|
+
* may not have.
|
|
46
|
+
*/
|
|
47
|
+
export declare function mapRevolutStatus(state: string): SubscriptionStatus;
|
|
48
|
+
/**
|
|
49
|
+
* Verify a Revolut webhook signature.
|
|
50
|
+
*
|
|
51
|
+
* Revolut signs `v1.{timestamp}.{raw body}` with HMAC-SHA256 under the webhook
|
|
52
|
+
* signing secret, and sends the result as `Revolut-Signature: v1=<hex>`. During
|
|
53
|
+
* secret rotation the header may carry several comma-separated signatures, any
|
|
54
|
+
* one of which may match.
|
|
55
|
+
*
|
|
56
|
+
* Exported separately from the adapter so a route handler can verify without
|
|
57
|
+
* constructing API credentials it does not need.
|
|
58
|
+
*/
|
|
59
|
+
export declare function verifyRevolutSignature(input: {
|
|
60
|
+
rawBody: string;
|
|
61
|
+
signature: string;
|
|
62
|
+
timestamp: string;
|
|
63
|
+
secret: string;
|
|
64
|
+
toleranceSeconds?: number;
|
|
65
|
+
now?: () => number;
|
|
66
|
+
}): boolean;
|
|
67
|
+
export declare function createRevolutProvider(options: RevolutAdapterOptions): BillingProvider;
|
|
68
|
+
//# sourceMappingURL=revolut.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revolut.d.ts","sourceRoot":"","sources":["../../src/billing/revolut.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EACV,eAAe,EAOhB,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAC5C,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,mEAAmE;IACnE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAID;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,CAmBlE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,GAAG,OAAO,CAqCV;AAYD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,eAAe,CAiIrF"}
|