@wopr-network/platform-core 0.1.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/biome.json +61 -0
- package/dist/admin/admin-audit-log-repository.d.ts +33 -0
- package/dist/admin/admin-audit-log-repository.js +102 -0
- package/dist/admin/audit-log.d.ts +49 -0
- package/dist/admin/audit-log.js +63 -0
- package/dist/admin/index.d.ts +6 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/role-store.d.ts +37 -0
- package/dist/admin/role-store.js +106 -0
- package/dist/auth/api-key-repository.d.ts +11 -0
- package/dist/auth/api-key-repository.js +33 -0
- package/dist/auth/api-key-repository.test.d.ts +1 -0
- package/dist/auth/api-key-repository.test.js +46 -0
- package/dist/auth/auth.test.d.ts +1 -0
- package/dist/auth/auth.test.js +140 -0
- package/dist/auth/better-auth.d.ts +42 -0
- package/dist/auth/better-auth.js +196 -0
- package/dist/auth/index.d.ts +186 -0
- package/dist/auth/index.js +422 -0
- package/dist/auth/login-history-repository.d.ts +14 -0
- package/dist/auth/login-history-repository.js +15 -0
- package/dist/auth/login-history-repository.test.d.ts +1 -0
- package/dist/auth/login-history-repository.test.js +47 -0
- package/dist/auth/middleware.d.ts +55 -0
- package/dist/auth/middleware.js +101 -0
- package/dist/auth/middleware.test.d.ts +1 -0
- package/dist/auth/middleware.test.js +213 -0
- package/dist/auth/scoped-tokens.test.d.ts +1 -0
- package/dist/auth/scoped-tokens.test.js +306 -0
- package/dist/auth/tenant-access.test.d.ts +1 -0
- package/dist/auth/tenant-access.test.js +62 -0
- package/dist/auth/user-creator.d.ts +9 -0
- package/dist/auth/user-creator.js +47 -0
- package/dist/auth/user-creator.test.d.ts +1 -0
- package/dist/auth/user-creator.test.js +78 -0
- package/dist/auth/user-role-repository.d.ts +31 -0
- package/dist/auth/user-role-repository.js +53 -0
- package/dist/auth/user-role-repository.test.d.ts +1 -0
- package/dist/auth/user-role-repository.test.js +122 -0
- package/dist/billing/drizzle-webhook-seen-repository.d.ts +10 -0
- package/dist/billing/drizzle-webhook-seen-repository.js +28 -0
- package/dist/billing/index.d.ts +7 -0
- package/dist/billing/index.js +7 -0
- package/dist/billing/payment-processor.d.ts +127 -0
- package/dist/billing/payment-processor.js +8 -0
- package/dist/billing/payment-processor.test.d.ts +1 -0
- package/dist/billing/payment-processor.test.js +71 -0
- package/dist/billing/payram/cents-credits-boundary.test.d.ts +1 -0
- package/dist/billing/payram/cents-credits-boundary.test.js +75 -0
- package/dist/billing/payram/charge-store.d.ts +41 -0
- package/dist/billing/payram/charge-store.js +72 -0
- package/dist/billing/payram/charge-store.test.d.ts +1 -0
- package/dist/billing/payram/charge-store.test.js +64 -0
- package/dist/billing/payram/checkout.d.ts +15 -0
- package/dist/billing/payram/checkout.js +24 -0
- package/dist/billing/payram/checkout.test.d.ts +1 -0
- package/dist/billing/payram/checkout.test.js +74 -0
- package/dist/billing/payram/client.d.ts +7 -0
- package/dist/billing/payram/client.js +15 -0
- package/dist/billing/payram/client.test.d.ts +1 -0
- package/dist/billing/payram/client.test.js +52 -0
- package/dist/billing/payram/index.d.ts +8 -0
- package/dist/billing/payram/index.js +4 -0
- package/dist/billing/payram/types.d.ts +40 -0
- package/dist/billing/payram/types.js +1 -0
- package/dist/billing/payram/webhook.d.ts +19 -0
- package/dist/billing/payram/webhook.js +67 -0
- package/dist/billing/payram/webhook.test.d.ts +7 -0
- package/dist/billing/payram/webhook.test.js +248 -0
- package/dist/billing/stripe/cents-credits-boundary.test.d.ts +1 -0
- package/dist/billing/stripe/cents-credits-boundary.test.js +62 -0
- package/dist/billing/stripe/checkout.d.ts +20 -0
- package/dist/billing/stripe/checkout.js +63 -0
- package/dist/billing/stripe/checkout.test.d.ts +1 -0
- package/dist/billing/stripe/checkout.test.js +148 -0
- package/dist/billing/stripe/client.d.ts +14 -0
- package/dist/billing/stripe/client.js +33 -0
- package/dist/billing/stripe/client.test.d.ts +1 -0
- package/dist/billing/stripe/client.test.js +58 -0
- package/dist/billing/stripe/credit-prices.d.ts +63 -0
- package/dist/billing/stripe/credit-prices.js +81 -0
- package/dist/billing/stripe/credit-prices.test.d.ts +1 -0
- package/dist/billing/stripe/credit-prices.test.js +87 -0
- package/dist/billing/stripe/index.d.ts +14 -0
- package/dist/billing/stripe/index.js +8 -0
- package/dist/billing/stripe/payment-methods-detach-all.test.d.ts +1 -0
- package/dist/billing/stripe/payment-methods-detach-all.test.js +40 -0
- package/dist/billing/stripe/payment-methods.d.ts +25 -0
- package/dist/billing/stripe/payment-methods.js +53 -0
- package/dist/billing/stripe/payment-methods.test.d.ts +1 -0
- package/dist/billing/stripe/payment-methods.test.js +122 -0
- package/dist/billing/stripe/portal.d.ts +10 -0
- package/dist/billing/stripe/portal.js +16 -0
- package/dist/billing/stripe/portal.test.d.ts +1 -0
- package/dist/billing/stripe/portal.test.js +48 -0
- package/dist/billing/stripe/setup-intent.d.ts +16 -0
- package/dist/billing/stripe/setup-intent.js +22 -0
- package/dist/billing/stripe/setup-intent.test.d.ts +1 -0
- package/dist/billing/stripe/setup-intent.test.js +58 -0
- package/dist/billing/stripe/stripe-payment-processor.d.ts +49 -0
- package/dist/billing/stripe/stripe-payment-processor.js +166 -0
- package/dist/billing/stripe/stripe-payment-processor.test.d.ts +1 -0
- package/dist/billing/stripe/stripe-payment-processor.test.js +413 -0
- package/dist/billing/stripe/tenant-store.d.ts +56 -0
- package/dist/billing/stripe/tenant-store.js +119 -0
- package/dist/billing/stripe/tenant-store.test.d.ts +1 -0
- package/dist/billing/stripe/tenant-store.test.js +97 -0
- package/dist/billing/stripe/types.d.ts +49 -0
- package/dist/billing/stripe/types.js +1 -0
- package/dist/billing/webhook-seen-repository.d.ts +14 -0
- package/dist/billing/webhook-seen-repository.js +13 -0
- package/dist/config/billing-env.test.d.ts +1 -0
- package/dist/config/billing-env.test.js +48 -0
- package/dist/config/index.d.ts +46 -0
- package/dist/config/index.js +38 -0
- package/dist/config/logger.d.ts +2 -0
- package/dist/config/logger.js +11 -0
- package/dist/config/provider-endpoints.d.ts +6 -0
- package/dist/config/provider-endpoints.js +12 -0
- package/dist/credits/auto-topup-charge.d.ts +27 -0
- package/dist/credits/auto-topup-charge.js +139 -0
- package/dist/credits/auto-topup-charge.test.d.ts +1 -0
- package/dist/credits/auto-topup-charge.test.js +242 -0
- package/dist/credits/auto-topup-event-log-repository.d.ts +16 -0
- package/dist/credits/auto-topup-event-log-repository.js +18 -0
- package/dist/credits/auto-topup-event-log-repository.test.d.ts +1 -0
- package/dist/credits/auto-topup-event-log-repository.test.js +83 -0
- package/dist/credits/auto-topup-schedule.d.ts +27 -0
- package/dist/credits/auto-topup-schedule.js +66 -0
- package/dist/credits/auto-topup-schedule.test.d.ts +1 -0
- package/dist/credits/auto-topup-schedule.test.js +145 -0
- package/dist/credits/auto-topup-settings-repository.d.ts +54 -0
- package/dist/credits/auto-topup-settings-repository.js +184 -0
- package/dist/credits/auto-topup-settings-repository.test.d.ts +1 -0
- package/dist/credits/auto-topup-settings-repository.test.js +104 -0
- package/dist/credits/auto-topup-usage.d.ts +22 -0
- package/dist/credits/auto-topup-usage.js +56 -0
- package/dist/credits/auto-topup-usage.test.d.ts +1 -0
- package/dist/credits/auto-topup-usage.test.js +181 -0
- package/dist/credits/credit-expiry-cron.d.ts +19 -0
- package/dist/credits/credit-expiry-cron.js +50 -0
- package/dist/credits/credit-expiry-cron.test.d.ts +1 -0
- package/dist/credits/credit-expiry-cron.test.js +67 -0
- package/dist/credits/credit-ledger-extra.test.d.ts +1 -0
- package/dist/credits/credit-ledger-extra.test.js +40 -0
- package/dist/credits/credit-ledger.bench.d.ts +1 -0
- package/dist/credits/credit-ledger.bench.js +33 -0
- package/dist/credits/credit-ledger.d.ts +130 -0
- package/dist/credits/credit-ledger.js +293 -0
- package/dist/credits/credit-ledger.test.d.ts +4 -0
- package/dist/credits/credit-ledger.test.js +203 -0
- package/dist/credits/credit-transaction-repository.d.ts +17 -0
- package/dist/credits/credit-transaction-repository.js +35 -0
- package/dist/credits/credit-transaction-repository.test.d.ts +1 -0
- package/dist/credits/credit-transaction-repository.test.js +232 -0
- package/dist/credits/credit.d.ts +75 -0
- package/dist/credits/credit.js +139 -0
- package/dist/credits/credit.test.d.ts +1 -0
- package/dist/credits/credit.test.js +196 -0
- package/dist/credits/dividend-cron.d.ts +29 -0
- package/dist/credits/dividend-cron.js +88 -0
- package/dist/credits/dividend-cron.test.d.ts +1 -0
- package/dist/credits/dividend-cron.test.js +128 -0
- package/dist/credits/dividend-repository.d.ts +29 -0
- package/dist/credits/dividend-repository.js +126 -0
- package/dist/credits/dividend-repository.test.d.ts +1 -0
- package/dist/credits/dividend-repository.test.js +176 -0
- package/dist/credits/index.d.ts +9 -0
- package/dist/credits/index.js +5 -0
- package/dist/credits/repository-types.d.ts +29 -0
- package/dist/credits/repository-types.js +1 -0
- package/dist/credits/signup-grant.d.ts +12 -0
- package/dist/credits/signup-grant.js +35 -0
- package/dist/credits/signup-grant.test.d.ts +1 -0
- package/dist/credits/signup-grant.test.js +51 -0
- package/dist/credits/tenant-customer-repository.d.ts +30 -0
- package/dist/credits/tenant-customer-repository.js +5 -0
- package/dist/db/auth-user-repository.d.ts +46 -0
- package/dist/db/auth-user-repository.js +90 -0
- package/dist/db/credit-column.d.ts +27 -0
- package/dist/db/credit-column.js +13 -0
- package/dist/db/index.d.ts +14 -0
- package/dist/db/index.js +8 -0
- package/dist/db/schema/account-deletion-requests.d.ts +203 -0
- package/dist/db/schema/account-deletion-requests.js +36 -0
- package/dist/db/schema/account-export-requests.d.ts +148 -0
- package/dist/db/schema/account-export-requests.js +19 -0
- package/dist/db/schema/admin-audit.d.ts +194 -0
- package/dist/db/schema/admin-audit.js +21 -0
- package/dist/db/schema/admin-users.d.ts +177 -0
- package/dist/db/schema/admin-users.js +23 -0
- package/dist/db/schema/affiliate-fraud.d.ts +160 -0
- package/dist/db/schema/affiliate-fraud.js +18 -0
- package/dist/db/schema/affiliate.d.ts +277 -0
- package/dist/db/schema/affiliate.js +32 -0
- package/dist/db/schema/coupon-codes.d.ts +143 -0
- package/dist/db/schema/coupon-codes.js +17 -0
- package/dist/db/schema/credit-auto-topup-settings.d.ts +232 -0
- package/dist/db/schema/credit-auto-topup-settings.js +27 -0
- package/dist/db/schema/credit-auto-topup.d.ts +130 -0
- package/dist/db/schema/credit-auto-topup.js +21 -0
- package/dist/db/schema/credits.d.ts +283 -0
- package/dist/db/schema/credits.js +38 -0
- package/dist/db/schema/dividend-distributions.d.ts +130 -0
- package/dist/db/schema/dividend-distributions.js +19 -0
- package/dist/db/schema/email-notifications.d.ts +99 -0
- package/dist/db/schema/email-notifications.js +21 -0
- package/dist/db/schema/index.d.ts +33 -0
- package/dist/db/schema/index.js +33 -0
- package/dist/db/schema/meter-events.d.ts +599 -0
- package/dist/db/schema/meter-events.js +55 -0
- package/dist/db/schema/notification-preferences.d.ts +165 -0
- package/dist/db/schema/notification-preferences.js +18 -0
- package/dist/db/schema/notification-queue.d.ts +236 -0
- package/dist/db/schema/notification-queue.js +40 -0
- package/dist/db/schema/org-memberships.d.ts +63 -0
- package/dist/db/schema/org-memberships.js +15 -0
- package/dist/db/schema/organization-members.d.ts +235 -0
- package/dist/db/schema/organization-members.js +27 -0
- package/dist/db/schema/payram.d.ts +164 -0
- package/dist/db/schema/payram.js +21 -0
- package/dist/db/schema/platform-api-keys.d.ts +143 -0
- package/dist/db/schema/platform-api-keys.js +20 -0
- package/dist/db/schema/promotion-redemptions.d.ts +143 -0
- package/dist/db/schema/promotion-redemptions.js +18 -0
- package/dist/db/schema/promotions.d.ts +445 -0
- package/dist/db/schema/promotions.js +48 -0
- package/dist/db/schema/provider-credentials.d.ts +201 -0
- package/dist/db/schema/provider-credentials.js +36 -0
- package/dist/db/schema/rate-limit-entries.d.ts +75 -0
- package/dist/db/schema/rate-limit-entries.js +7 -0
- package/dist/db/schema/secret-audit-log.d.ts +109 -0
- package/dist/db/schema/secret-audit-log.js +15 -0
- package/dist/db/schema/session-usage.d.ts +194 -0
- package/dist/db/schema/session-usage.js +19 -0
- package/dist/db/schema/spending-limits.d.ts +92 -0
- package/dist/db/schema/spending-limits.js +8 -0
- package/dist/db/schema/tenant-addons.d.ts +58 -0
- package/dist/db/schema/tenant-addons.js +9 -0
- package/dist/db/schema/tenant-api-keys.d.ts +131 -0
- package/dist/db/schema/tenant-api-keys.js +21 -0
- package/dist/db/schema/tenant-capability-settings.d.ts +79 -0
- package/dist/db/schema/tenant-capability-settings.js +12 -0
- package/dist/db/schema/tenant-customers.d.ts +303 -0
- package/dist/db/schema/tenant-customers.js +25 -0
- package/dist/db/schema/tenants.d.ts +126 -0
- package/dist/db/schema/tenants.js +18 -0
- package/dist/db/schema/user-roles.d.ts +98 -0
- package/dist/db/schema/user-roles.js +18 -0
- package/dist/db/schema/webhook-seen-events.d.ts +58 -0
- package/dist/db/schema/webhook-seen-events.js +9 -0
- package/dist/email/billing-emails.d.ts +51 -0
- package/dist/email/billing-emails.js +163 -0
- package/dist/email/billing-emails.test.d.ts +1 -0
- package/dist/email/billing-emails.test.js +162 -0
- package/dist/email/client.d.ts +51 -0
- package/dist/email/client.js +102 -0
- package/dist/email/client.test.d.ts +1 -0
- package/dist/email/client.test.js +120 -0
- package/dist/email/drizzle-billing-email-repository.d.ts +21 -0
- package/dist/email/drizzle-billing-email-repository.js +36 -0
- package/dist/email/drizzle-billing-email-repository.test.d.ts +1 -0
- package/dist/email/drizzle-billing-email-repository.test.js +42 -0
- package/dist/email/index.d.ts +33 -0
- package/dist/email/index.js +22 -0
- package/dist/email/notification-preferences-store.d.ts +12 -0
- package/dist/email/notification-preferences-store.js +82 -0
- package/dist/email/notification-preferences-store.test.d.ts +1 -0
- package/dist/email/notification-preferences-store.test.js +86 -0
- package/dist/email/notification-queue-store.d.ts +25 -0
- package/dist/email/notification-queue-store.js +97 -0
- package/dist/email/notification-queue-store.test.d.ts +1 -0
- package/dist/email/notification-queue-store.test.js +177 -0
- package/dist/email/notification-repository-types.d.ts +70 -0
- package/dist/email/notification-repository-types.js +6 -0
- package/dist/email/notification-service.d.ts +41 -0
- package/dist/email/notification-service.js +196 -0
- package/dist/email/notification-service.test.d.ts +1 -0
- package/dist/email/notification-service.test.js +160 -0
- package/dist/email/notification-templates.d.ts +18 -0
- package/dist/email/notification-templates.js +574 -0
- package/dist/email/notification-templates.test.d.ts +1 -0
- package/dist/email/notification-templates.test.js +238 -0
- package/dist/email/notification-worker.d.ts +24 -0
- package/dist/email/notification-worker.js +109 -0
- package/dist/email/notification-worker.test.d.ts +1 -0
- package/dist/email/notification-worker.test.js +153 -0
- package/dist/email/require-verified.d.ts +25 -0
- package/dist/email/require-verified.js +52 -0
- package/dist/email/require-verified.test.d.ts +1 -0
- package/dist/email/require-verified.test.js +62 -0
- package/dist/email/resend-adapter.d.ts +47 -0
- package/dist/email/resend-adapter.js +137 -0
- package/dist/email/resend-adapter.test.d.ts +1 -0
- package/dist/email/resend-adapter.test.js +190 -0
- package/dist/email/templates.d.ts +22 -0
- package/dist/email/templates.js +359 -0
- package/dist/email/templates.test.d.ts +1 -0
- package/dist/email/templates.test.js +170 -0
- package/dist/email/verification.d.ts +42 -0
- package/dist/email/verification.js +83 -0
- package/dist/email/verification.test.d.ts +1 -0
- package/dist/email/verification.test.js +141 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +23 -0
- package/dist/metering/aggregator.d.ts +54 -0
- package/dist/metering/aggregator.js +123 -0
- package/dist/metering/aggregator.test.d.ts +1 -0
- package/dist/metering/aggregator.test.js +179 -0
- package/dist/metering/dlq.d.ts +31 -0
- package/dist/metering/dlq.js +82 -0
- package/dist/metering/dlq.test.d.ts +1 -0
- package/dist/metering/dlq.test.js +117 -0
- package/dist/metering/drizzle-usage-summary-repository.d.ts +67 -0
- package/dist/metering/drizzle-usage-summary-repository.js +98 -0
- package/dist/metering/emitter.d.ts +66 -0
- package/dist/metering/emitter.js +185 -0
- package/dist/metering/emitter.test.d.ts +1 -0
- package/dist/metering/emitter.test.js +171 -0
- package/dist/metering/index.d.ts +11 -0
- package/dist/metering/index.js +5 -0
- package/dist/metering/load-test.bench.d.ts +1 -0
- package/dist/metering/load-test.bench.js +103 -0
- package/dist/metering/meter-event-repository.d.ts +33 -0
- package/dist/metering/meter-event-repository.js +58 -0
- package/dist/metering/meter-repositories.test.d.ts +1 -0
- package/dist/metering/meter-repositories.test.js +419 -0
- package/dist/metering/metering.test.d.ts +1 -0
- package/dist/metering/metering.test.js +1046 -0
- package/dist/metering/reconciliation-cron.d.ts +37 -0
- package/dist/metering/reconciliation-cron.js +85 -0
- package/dist/metering/reconciliation-cron.test.d.ts +1 -0
- package/dist/metering/reconciliation-cron.test.js +162 -0
- package/dist/metering/reconciliation-repository.d.ts +27 -0
- package/dist/metering/reconciliation-repository.js +43 -0
- package/dist/metering/reconciliation-repository.test.d.ts +1 -0
- package/dist/metering/reconciliation-repository.test.js +160 -0
- package/dist/metering/types.d.ts +88 -0
- package/dist/metering/types.js +1 -0
- package/dist/metering/wal.d.ts +49 -0
- package/dist/metering/wal.js +124 -0
- package/dist/metering/wal.test.d.ts +1 -0
- package/dist/metering/wal.test.js +175 -0
- package/dist/middleware/csrf.d.ts +24 -0
- package/dist/middleware/csrf.js +80 -0
- package/dist/middleware/csrf.test.d.ts +1 -0
- package/dist/middleware/csrf.test.js +152 -0
- package/dist/middleware/drizzle-rate-limit-repository.d.ts +9 -0
- package/dist/middleware/drizzle-rate-limit-repository.js +52 -0
- package/dist/middleware/drizzle-rate-limit-repository.test.d.ts +1 -0
- package/dist/middleware/drizzle-rate-limit-repository.test.js +74 -0
- package/dist/middleware/get-client-ip.d.ts +22 -0
- package/dist/middleware/get-client-ip.js +51 -0
- package/dist/middleware/get-client-ip.test.d.ts +1 -0
- package/dist/middleware/get-client-ip.test.js +40 -0
- package/dist/middleware/index.d.ts +5 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/rate-limit-repository.d.ts +19 -0
- package/dist/middleware/rate-limit-repository.js +1 -0
- package/dist/middleware/rate-limit.d.ts +57 -0
- package/dist/middleware/rate-limit.js +109 -0
- package/dist/middleware/rate-limit.test.d.ts +1 -0
- package/dist/middleware/rate-limit.test.js +247 -0
- package/dist/security/credential-vault/audit-repository.d.ts +27 -0
- package/dist/security/credential-vault/audit-repository.js +42 -0
- package/dist/security/credential-vault/audit-repository.test.d.ts +1 -0
- package/dist/security/credential-vault/audit-repository.test.js +78 -0
- package/dist/security/credential-vault/credential-repository.d.ts +94 -0
- package/dist/security/credential-vault/credential-repository.js +145 -0
- package/dist/security/credential-vault/credential-repository.test.d.ts +1 -0
- package/dist/security/credential-vault/credential-repository.test.js +206 -0
- package/dist/security/credential-vault/index.d.ts +12 -0
- package/dist/security/credential-vault/index.js +6 -0
- package/dist/security/credential-vault/key-rotation.d.ts +18 -0
- package/dist/security/credential-vault/key-rotation.js +52 -0
- package/dist/security/credential-vault/key-rotation.test.d.ts +1 -0
- package/dist/security/credential-vault/key-rotation.test.js +95 -0
- package/dist/security/credential-vault/migrate-plaintext.d.ts +15 -0
- package/dist/security/credential-vault/migrate-plaintext.js +80 -0
- package/dist/security/credential-vault/migrate-plaintext.test.d.ts +1 -0
- package/dist/security/credential-vault/migrate-plaintext.test.js +111 -0
- package/dist/security/credential-vault/migration-check.d.ts +15 -0
- package/dist/security/credential-vault/migration-check.js +71 -0
- package/dist/security/credential-vault/migration-check.test.d.ts +1 -0
- package/dist/security/credential-vault/migration-check.test.js +457 -0
- package/dist/security/credential-vault/store.d.ts +106 -0
- package/dist/security/credential-vault/store.js +181 -0
- package/dist/security/credential-vault/store.test.d.ts +1 -0
- package/dist/security/credential-vault/store.test.js +482 -0
- package/dist/security/encryption.d.ts +22 -0
- package/dist/security/encryption.js +53 -0
- package/dist/security/encryption.test.d.ts +1 -0
- package/dist/security/encryption.test.js +95 -0
- package/dist/security/host-validation.d.ts +11 -0
- package/dist/security/host-validation.js +108 -0
- package/dist/security/host-validation.test.d.ts +1 -0
- package/dist/security/host-validation.test.js +106 -0
- package/dist/security/index.d.ts +11 -0
- package/dist/security/index.js +11 -0
- package/dist/security/key-audit.d.ts +16 -0
- package/dist/security/key-audit.js +35 -0
- package/dist/security/key-audit.test.d.ts +1 -0
- package/dist/security/key-audit.test.js +50 -0
- package/dist/security/key-injection.d.ts +28 -0
- package/dist/security/key-injection.js +57 -0
- package/dist/security/key-injection.test.d.ts +1 -0
- package/dist/security/key-injection.test.js +97 -0
- package/dist/security/key-validation.d.ts +16 -0
- package/dist/security/key-validation.js +78 -0
- package/dist/security/key-validation.test.d.ts +1 -0
- package/dist/security/key-validation.test.js +87 -0
- package/dist/security/redirect-allowlist.d.ts +6 -0
- package/dist/security/redirect-allowlist.js +36 -0
- package/dist/security/redirect-allowlist.test.d.ts +1 -0
- package/dist/security/redirect-allowlist.test.js +55 -0
- package/dist/security/tenant-keys/capability-settings-store.d.ts +22 -0
- package/dist/security/tenant-keys/capability-settings-store.js +33 -0
- package/dist/security/tenant-keys/capability-settings-store.test.d.ts +1 -0
- package/dist/security/tenant-keys/capability-settings-store.test.js +77 -0
- package/dist/security/tenant-keys/index.d.ts +10 -0
- package/dist/security/tenant-keys/index.js +5 -0
- package/dist/security/tenant-keys/key-resolution-repository.d.ts +15 -0
- package/dist/security/tenant-keys/key-resolution-repository.js +18 -0
- package/dist/security/tenant-keys/key-resolution-repository.test.d.ts +1 -0
- package/dist/security/tenant-keys/key-resolution-repository.test.js +72 -0
- package/dist/security/tenant-keys/key-resolution.d.ts +39 -0
- package/dist/security/tenant-keys/key-resolution.js +59 -0
- package/dist/security/tenant-keys/key-resolution.test.d.ts +1 -0
- package/dist/security/tenant-keys/key-resolution.test.js +97 -0
- package/dist/security/tenant-keys/org-key-resolution.d.ts +30 -0
- package/dist/security/tenant-keys/org-key-resolution.js +50 -0
- package/dist/security/tenant-keys/org-key-resolution.test.d.ts +1 -0
- package/dist/security/tenant-keys/org-key-resolution.test.js +103 -0
- package/dist/security/tenant-keys/tenant-key-repository.d.ts +36 -0
- package/dist/security/tenant-keys/tenant-key-repository.js +96 -0
- package/dist/security/tenant-keys/tenant-key-repository.test.d.ts +1 -0
- package/dist/security/tenant-keys/tenant-key-repository.test.js +114 -0
- package/dist/security/types.d.ts +35 -0
- package/dist/security/types.js +15 -0
- package/dist/tenancy/drizzle-org-repository.d.ts +40 -0
- package/dist/tenancy/drizzle-org-repository.js +126 -0
- package/dist/tenancy/index.d.ts +6 -0
- package/dist/tenancy/index.js +3 -0
- package/dist/tenancy/org-member-repository.d.ts +57 -0
- package/dist/tenancy/org-member-repository.js +99 -0
- package/dist/tenancy/org-repository.test.d.ts +1 -0
- package/dist/tenancy/org-repository.test.js +143 -0
- package/dist/tenancy/org-service.d.ts +70 -0
- package/dist/tenancy/org-service.js +223 -0
- package/dist/tenancy/org-service.test.d.ts +1 -0
- package/dist/tenancy/org-service.test.js +550 -0
- package/dist/test/db.d.ts +33 -0
- package/dist/test/db.js +65 -0
- package/dist/trpc/index.d.ts +1 -0
- package/dist/trpc/index.js +1 -0
- package/dist/trpc/init.d.ts +49 -0
- package/dist/trpc/init.js +108 -0
- package/dist/trpc/init.test.d.ts +1 -0
- package/dist/trpc/init.test.js +154 -0
- package/drizzle/migrations/0000_slippery_mandrill.sql +559 -0
- package/drizzle/migrations/meta/0000_snapshot.json +4374 -0
- package/drizzle/migrations/meta/_journal.json +13 -0
- package/drizzle.config.ts +41 -0
- package/package.json +64 -0
- package/src/admin/admin-audit-log-repository.ts +135 -0
- package/src/admin/audit-log.ts +111 -0
- package/src/admin/index.ts +6 -0
- package/src/admin/role-store.ts +134 -0
- package/src/auth/api-key-repository.test.ts +63 -0
- package/src/auth/api-key-repository.ts +46 -0
- package/src/auth/auth.test.ts +166 -0
- package/src/auth/better-auth.ts +216 -0
- package/src/auth/index.ts +520 -0
- package/src/auth/login-history-repository.test.ts +54 -0
- package/src/auth/login-history-repository.ts +28 -0
- package/src/auth/middleware.test.ts +264 -0
- package/src/auth/middleware.ts +117 -0
- package/src/auth/scoped-tokens.test.ts +362 -0
- package/src/auth/tenant-access.test.ts +69 -0
- package/src/auth/user-creator.test.ts +98 -0
- package/src/auth/user-creator.ts +54 -0
- package/src/auth/user-role-repository.test.ts +149 -0
- package/src/auth/user-role-repository.ts +67 -0
- package/src/billing/drizzle-webhook-seen-repository.ts +34 -0
- package/src/billing/index.ts +22 -0
- package/src/billing/payment-processor.test.ts +93 -0
- package/src/billing/payment-processor.ts +150 -0
- package/src/billing/payram/cents-credits-boundary.test.ts +84 -0
- package/src/billing/payram/charge-store.test.ts +84 -0
- package/src/billing/payram/charge-store.ts +109 -0
- package/src/billing/payram/checkout.test.ts +99 -0
- package/src/billing/payram/checkout.ts +40 -0
- package/src/billing/payram/client.test.ts +62 -0
- package/src/billing/payram/client.ts +21 -0
- package/src/billing/payram/index.ts +14 -0
- package/src/billing/payram/types.ts +44 -0
- package/src/billing/payram/webhook.test.ts +318 -0
- package/src/billing/payram/webhook.ts +97 -0
- package/src/billing/stripe/cents-credits-boundary.test.ts +70 -0
- package/src/billing/stripe/checkout.test.ts +186 -0
- package/src/billing/stripe/checkout.ts +82 -0
- package/src/billing/stripe/client.test.ts +64 -0
- package/src/billing/stripe/client.ts +39 -0
- package/src/billing/stripe/credit-prices.test.ts +114 -0
- package/src/billing/stripe/credit-prices.ts +113 -0
- package/src/billing/stripe/index.ts +14 -0
- package/src/billing/stripe/payment-methods-detach-all.test.ts +53 -0
- package/src/billing/stripe/payment-methods.test.ts +157 -0
- package/src/billing/stripe/payment-methods.ts +76 -0
- package/src/billing/stripe/portal.test.ts +63 -0
- package/src/billing/stripe/portal.ts +25 -0
- package/src/billing/stripe/setup-intent.test.ts +78 -0
- package/src/billing/stripe/setup-intent.ts +34 -0
- package/src/billing/stripe/stripe-payment-processor.test.ts +517 -0
- package/src/billing/stripe/stripe-payment-processor.ts +255 -0
- package/src/billing/stripe/tenant-store.test.ts +124 -0
- package/src/billing/stripe/tenant-store.ts +151 -0
- package/src/billing/stripe/types.ts +53 -0
- package/src/billing/webhook-seen-repository.ts +24 -0
- package/src/config/billing-env.test.ts +54 -0
- package/src/config/index.ts +44 -0
- package/src/config/logger.ts +12 -0
- package/src/config/provider-endpoints.ts +14 -0
- package/src/credits/auto-topup-charge.test.ts +292 -0
- package/src/credits/auto-topup-charge.ts +171 -0
- package/src/credits/auto-topup-event-log-repository.test.ts +99 -0
- package/src/credits/auto-topup-event-log-repository.ts +30 -0
- package/src/credits/auto-topup-schedule.test.ts +179 -0
- package/src/credits/auto-topup-schedule.ts +93 -0
- package/src/credits/auto-topup-settings-repository.test.ts +123 -0
- package/src/credits/auto-topup-settings-repository.ts +245 -0
- package/src/credits/auto-topup-usage.test.ts +220 -0
- package/src/credits/auto-topup-usage.ts +68 -0
- package/src/credits/credit-expiry-cron.test.ts +125 -0
- package/src/credits/credit-expiry-cron.ts +76 -0
- package/src/credits/credit-ledger-extra.test.ts +57 -0
- package/src/credits/credit-ledger.bench.ts +56 -0
- package/src/credits/credit-ledger.test.ts +276 -0
- package/src/credits/credit-ledger.ts +450 -0
- package/src/credits/credit-transaction-repository.test.ts +274 -0
- package/src/credits/credit-transaction-repository.ts +62 -0
- package/src/credits/credit.test.ts +234 -0
- package/src/credits/credit.ts +160 -0
- package/src/credits/dividend-cron.test.ts +158 -0
- package/src/credits/dividend-cron.ts +127 -0
- package/src/credits/dividend-repository.test.ts +223 -0
- package/src/credits/dividend-repository.ts +182 -0
- package/src/credits/index.ts +25 -0
- package/src/credits/repository-types.ts +33 -0
- package/src/credits/signup-grant.test.ts +63 -0
- package/src/credits/signup-grant.ts +44 -0
- package/src/credits/tenant-customer-repository.ts +28 -0
- package/src/db/auth-user-repository.ts +124 -0
- package/src/db/credit-column.ts +17 -0
- package/src/db/index.ts +21 -0
- package/src/db/schema/account-deletion-requests.ts +41 -0
- package/src/db/schema/account-export-requests.ts +24 -0
- package/src/db/schema/admin-audit.ts +26 -0
- package/src/db/schema/admin-users.ts +31 -0
- package/src/db/schema/affiliate-fraud.ts +23 -0
- package/src/db/schema/affiliate.ts +38 -0
- package/src/db/schema/coupon-codes.ts +22 -0
- package/src/db/schema/credit-auto-topup-settings.ts +32 -0
- package/src/db/schema/credit-auto-topup.ts +26 -0
- package/src/db/schema/credits.ts +44 -0
- package/src/db/schema/dividend-distributions.ts +24 -0
- package/src/db/schema/email-notifications.ts +26 -0
- package/src/db/schema/index.ts +33 -0
- package/src/db/schema/meter-events.ts +70 -0
- package/src/db/schema/notification-preferences.ts +19 -0
- package/src/db/schema/notification-queue.ts +45 -0
- package/src/db/schema/org-memberships.ts +20 -0
- package/src/db/schema/organization-members.ts +37 -0
- package/src/db/schema/payram.ts +26 -0
- package/src/db/schema/platform-api-keys.ts +25 -0
- package/src/db/schema/promotion-redemptions.ts +23 -0
- package/src/db/schema/promotions.ts +57 -0
- package/src/db/schema/provider-credentials.ts +41 -0
- package/src/db/schema/rate-limit-entries.ts +12 -0
- package/src/db/schema/secret-audit-log.ts +20 -0
- package/src/db/schema/session-usage.ts +24 -0
- package/src/db/schema/spending-limits.ts +9 -0
- package/src/db/schema/tenant-addons.ts +14 -0
- package/src/db/schema/tenant-api-keys.ts +26 -0
- package/src/db/schema/tenant-capability-settings.ts +17 -0
- package/src/db/schema/tenant-customers.ts +35 -0
- package/src/db/schema/tenants.ts +23 -0
- package/src/db/schema/user-roles.ts +23 -0
- package/src/db/schema/webhook-seen-events.ts +14 -0
- package/src/email/billing-emails.test.ts +198 -0
- package/src/email/billing-emails.ts +211 -0
- package/src/email/client.test.ts +149 -0
- package/src/email/client.ts +137 -0
- package/src/email/drizzle-billing-email-repository.test.ts +52 -0
- package/src/email/drizzle-billing-email-repository.ts +59 -0
- package/src/email/index.ts +57 -0
- package/src/email/notification-preferences-store.test.ts +102 -0
- package/src/email/notification-preferences-store.ts +90 -0
- package/src/email/notification-queue-store.test.ts +215 -0
- package/src/email/notification-queue-store.ts +127 -0
- package/src/email/notification-repository-types.ts +101 -0
- package/src/email/notification-service.test.ts +178 -0
- package/src/email/notification-service.ts +265 -0
- package/src/email/notification-templates.test.ts +261 -0
- package/src/email/notification-templates.ts +727 -0
- package/src/email/notification-worker.test.ts +189 -0
- package/src/email/notification-worker.ts +133 -0
- package/src/email/require-verified.ts +65 -0
- package/src/email/resend-adapter.test.ts +253 -0
- package/src/email/resend-adapter.ts +157 -0
- package/src/email/templates.test.ts +217 -0
- package/src/email/templates.ts +469 -0
- package/src/email/verification.test.ts +185 -0
- package/src/email/verification.ts +110 -0
- package/src/index.ts +51 -0
- package/src/metering/aggregator.test.ts +239 -0
- package/src/metering/aggregator.ts +160 -0
- package/src/metering/dlq.test.ts +134 -0
- package/src/metering/dlq.ts +102 -0
- package/src/metering/drizzle-usage-summary-repository.ts +167 -0
- package/src/metering/emitter.test.ts +202 -0
- package/src/metering/emitter.ts +227 -0
- package/src/metering/index.ts +21 -0
- package/src/metering/load-test.bench.ts +130 -0
- package/src/metering/meter-event-repository.ts +87 -0
- package/src/metering/meter-repositories.test.ts +491 -0
- package/src/metering/metering.test.ts +1317 -0
- package/src/metering/reconciliation-cron.test.ts +202 -0
- package/src/metering/reconciliation-cron.ts +134 -0
- package/src/metering/reconciliation-repository.test.ts +196 -0
- package/src/metering/reconciliation-repository.ts +83 -0
- package/src/metering/types.ts +93 -0
- package/src/metering/wal.test.ts +222 -0
- package/src/metering/wal.ts +139 -0
- package/src/middleware/csrf.test.ts +178 -0
- package/src/middleware/csrf.ts +101 -0
- package/src/middleware/drizzle-rate-limit-repository.test.ts +97 -0
- package/src/middleware/drizzle-rate-limit-repository.ts +57 -0
- package/src/middleware/get-client-ip.test.ts +49 -0
- package/src/middleware/get-client-ip.ts +62 -0
- package/src/middleware/index.ts +12 -0
- package/src/middleware/rate-limit-repository.ts +22 -0
- package/src/middleware/rate-limit.test.ts +338 -0
- package/src/middleware/rate-limit.ts +169 -0
- package/src/security/credential-vault/audit-repository.test.ts +91 -0
- package/src/security/credential-vault/audit-repository.ts +64 -0
- package/src/security/credential-vault/credential-repository.test.ts +264 -0
- package/src/security/credential-vault/credential-repository.ts +233 -0
- package/src/security/credential-vault/index.ts +26 -0
- package/src/security/credential-vault/key-rotation.test.ts +139 -0
- package/src/security/credential-vault/key-rotation.ts +70 -0
- package/src/security/credential-vault/migrate-plaintext.test.ts +138 -0
- package/src/security/credential-vault/migrate-plaintext.ts +101 -0
- package/src/security/credential-vault/migration-check.test.ts +533 -0
- package/src/security/credential-vault/migration-check.ts +88 -0
- package/src/security/credential-vault/store.test.ts +569 -0
- package/src/security/credential-vault/store.ts +284 -0
- package/src/security/encryption.test.ts +114 -0
- package/src/security/encryption.ts +65 -0
- package/src/security/host-validation.test.ts +136 -0
- package/src/security/host-validation.ts +116 -0
- package/src/security/index.ts +59 -0
- package/src/security/key-audit.test.ts +57 -0
- package/src/security/key-audit.ts +45 -0
- package/src/security/key-injection.test.ts +131 -0
- package/src/security/key-injection.ts +71 -0
- package/src/security/key-validation.test.ts +111 -0
- package/src/security/key-validation.ts +84 -0
- package/src/security/redirect-allowlist.test.ts +70 -0
- package/src/security/redirect-allowlist.ts +35 -0
- package/src/security/tenant-keys/capability-settings-store.test.ts +98 -0
- package/src/security/tenant-keys/capability-settings-store.ts +53 -0
- package/src/security/tenant-keys/index.ts +10 -0
- package/src/security/tenant-keys/key-resolution-repository.test.ts +95 -0
- package/src/security/tenant-keys/key-resolution-repository.ts +31 -0
- package/src/security/tenant-keys/key-resolution.test.ts +173 -0
- package/src/security/tenant-keys/key-resolution.ts +87 -0
- package/src/security/tenant-keys/org-key-resolution.test.ts +217 -0
- package/src/security/tenant-keys/org-key-resolution.ts +76 -0
- package/src/security/tenant-keys/tenant-key-repository.test.ts +143 -0
- package/src/security/tenant-keys/tenant-key-repository.ts +130 -0
- package/src/security/types.ts +43 -0
- package/src/tenancy/drizzle-org-repository.ts +169 -0
- package/src/tenancy/index.ts +6 -0
- package/src/tenancy/org-member-repository.ts +159 -0
- package/src/tenancy/org-repository.test.ts +172 -0
- package/src/tenancy/org-service.test.ts +634 -0
- package/src/tenancy/org-service.ts +290 -0
- package/src/test/db.ts +97 -0
- package/src/trpc/index.ts +11 -0
- package/src/trpc/init.test.ts +196 -0
- package/src/trpc/init.ts +138 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import { TRPCError } from "@trpc/server";
|
|
2
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { DrizzleOrgMemberRepository } from "./org-member-repository.js";
|
|
4
|
+
import { createTestDb, truncateAllTables } from "../test/db.js";
|
|
5
|
+
import { DrizzleOrgRepository } from "./drizzle-org-repository.js";
|
|
6
|
+
import { OrgService } from "./org-service.js";
|
|
7
|
+
async function setup(db) {
|
|
8
|
+
const orgRepo = new DrizzleOrgRepository(db);
|
|
9
|
+
const memberRepo = new DrizzleOrgMemberRepository(db);
|
|
10
|
+
const service = new OrgService(orgRepo, memberRepo, db);
|
|
11
|
+
return { service, orgRepo, memberRepo };
|
|
12
|
+
}
|
|
13
|
+
describe("OrgService", () => {
|
|
14
|
+
let db;
|
|
15
|
+
let pool;
|
|
16
|
+
let service;
|
|
17
|
+
const userId = "user-1";
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
({ db, pool } = await createTestDb());
|
|
20
|
+
});
|
|
21
|
+
afterAll(async () => {
|
|
22
|
+
await pool.close().catch(() => { });
|
|
23
|
+
});
|
|
24
|
+
beforeEach(async () => {
|
|
25
|
+
await truncateAllTables(pool);
|
|
26
|
+
({ service } = await setup(db));
|
|
27
|
+
});
|
|
28
|
+
describe("getOrCreatePersonalOrg", () => {
|
|
29
|
+
it("creates personal org and owner member on first call", async () => {
|
|
30
|
+
const org = await service.getOrCreatePersonalOrg(userId, "Alice");
|
|
31
|
+
expect(org.type).toBe("personal");
|
|
32
|
+
expect(org.ownerId).toBe(userId);
|
|
33
|
+
expect(org.members).toHaveLength(1);
|
|
34
|
+
expect(org.members[0].role).toBe("owner");
|
|
35
|
+
});
|
|
36
|
+
it("is idempotent — second call returns the same org", async () => {
|
|
37
|
+
await service.getOrCreatePersonalOrg(userId, "Alice");
|
|
38
|
+
const org = await service.getOrCreatePersonalOrg(userId, "Alice");
|
|
39
|
+
expect(org.members).toHaveLength(1);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe("updateOrg", () => {
|
|
43
|
+
it("persists name change to DB", async () => {
|
|
44
|
+
// Create an org, then update it
|
|
45
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
46
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
47
|
+
const owner = "owner-1";
|
|
48
|
+
const org = await orgRepo.createOrg(owner, "Original", "original-slug");
|
|
49
|
+
await memberRepo.addMember({
|
|
50
|
+
id: "m1",
|
|
51
|
+
orgId: org.id,
|
|
52
|
+
userId: owner,
|
|
53
|
+
role: "owner",
|
|
54
|
+
joinedAt: Date.now(),
|
|
55
|
+
});
|
|
56
|
+
const updated = await svc.updateOrg(org.id, owner, { name: "Updated Name" });
|
|
57
|
+
expect(updated.name).toBe("Updated Name");
|
|
58
|
+
expect(updated.slug).toBe("original-slug"); // unchanged
|
|
59
|
+
// Verify persisted
|
|
60
|
+
const fetched = await orgRepo.getById(org.id);
|
|
61
|
+
expect(fetched?.name).toBe("Updated Name");
|
|
62
|
+
});
|
|
63
|
+
it("persists slug change to DB", async () => {
|
|
64
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
65
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
66
|
+
const owner = "owner-2";
|
|
67
|
+
const org = await orgRepo.createOrg(owner, "My Org", "my-org");
|
|
68
|
+
await memberRepo.addMember({
|
|
69
|
+
id: "m2",
|
|
70
|
+
orgId: org.id,
|
|
71
|
+
userId: owner,
|
|
72
|
+
role: "owner",
|
|
73
|
+
joinedAt: Date.now(),
|
|
74
|
+
});
|
|
75
|
+
const updated = await svc.updateOrg(org.id, owner, { slug: "new-slug-ab" });
|
|
76
|
+
expect(updated.slug).toBe("new-slug-ab");
|
|
77
|
+
const fetched = await orgRepo.getById(org.id);
|
|
78
|
+
expect(fetched?.slug).toBe("new-slug-ab");
|
|
79
|
+
});
|
|
80
|
+
it("throws FORBIDDEN when non-admin tries to update", async () => {
|
|
81
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
82
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
83
|
+
const owner = "owner-3";
|
|
84
|
+
const org = await orgRepo.createOrg(owner, "Org", "org-slug");
|
|
85
|
+
await memberRepo.addMember({
|
|
86
|
+
id: "m3",
|
|
87
|
+
orgId: org.id,
|
|
88
|
+
userId: owner,
|
|
89
|
+
role: "owner",
|
|
90
|
+
joinedAt: Date.now(),
|
|
91
|
+
});
|
|
92
|
+
await expect(svc.updateOrg(org.id, "not-a-member", { name: "hack" })).rejects.toThrow();
|
|
93
|
+
});
|
|
94
|
+
it("throws CONFLICT when slug is already taken", async () => {
|
|
95
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
96
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
97
|
+
const owner = "owner-4";
|
|
98
|
+
await orgRepo.createOrg("other", "Other Org", "taken-slug");
|
|
99
|
+
const org = await orgRepo.createOrg(owner, "My Org", "mine-slug");
|
|
100
|
+
await memberRepo.addMember({
|
|
101
|
+
id: "m4",
|
|
102
|
+
orgId: org.id,
|
|
103
|
+
userId: owner,
|
|
104
|
+
role: "owner",
|
|
105
|
+
joinedAt: Date.now(),
|
|
106
|
+
});
|
|
107
|
+
await expect(svc.updateOrg(org.id, owner, { slug: "taken-slug" })).rejects.toThrow();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe("transferOwnership", () => {
|
|
111
|
+
it("updates member roles AND persists new ownerId to tenants table", async () => {
|
|
112
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
113
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
114
|
+
const owner = "owner-5";
|
|
115
|
+
const newOwner = "new-owner-5";
|
|
116
|
+
const org = await orgRepo.createOrg(owner, "Transfer Org", "transfer-org");
|
|
117
|
+
await memberRepo.addMember({
|
|
118
|
+
id: "m5",
|
|
119
|
+
orgId: org.id,
|
|
120
|
+
userId: owner,
|
|
121
|
+
role: "owner",
|
|
122
|
+
joinedAt: Date.now(),
|
|
123
|
+
});
|
|
124
|
+
await memberRepo.addMember({
|
|
125
|
+
id: "m6",
|
|
126
|
+
orgId: org.id,
|
|
127
|
+
userId: newOwner,
|
|
128
|
+
role: "member",
|
|
129
|
+
joinedAt: Date.now(),
|
|
130
|
+
});
|
|
131
|
+
await svc.transferOwnership(org.id, owner, newOwner);
|
|
132
|
+
const updatedOrg = await orgRepo.getById(org.id);
|
|
133
|
+
expect(updatedOrg?.ownerId).toBe(newOwner);
|
|
134
|
+
const newOwnerMember = await memberRepo.findMember(org.id, newOwner);
|
|
135
|
+
expect(newOwnerMember?.role).toBe("owner");
|
|
136
|
+
const oldOwnerMember = await memberRepo.findMember(org.id, owner);
|
|
137
|
+
expect(oldOwnerMember?.role).toBe("admin");
|
|
138
|
+
});
|
|
139
|
+
it("throws FORBIDDEN if non-owner tries to transfer", async () => {
|
|
140
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
141
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
142
|
+
const owner = "owner-6";
|
|
143
|
+
const other = "other-6";
|
|
144
|
+
const org = await orgRepo.createOrg(owner, "Org6", "org6");
|
|
145
|
+
await memberRepo.addMember({
|
|
146
|
+
id: "m7",
|
|
147
|
+
orgId: org.id,
|
|
148
|
+
userId: owner,
|
|
149
|
+
role: "owner",
|
|
150
|
+
joinedAt: Date.now(),
|
|
151
|
+
});
|
|
152
|
+
await memberRepo.addMember({
|
|
153
|
+
id: "m8",
|
|
154
|
+
orgId: org.id,
|
|
155
|
+
userId: other,
|
|
156
|
+
role: "admin",
|
|
157
|
+
joinedAt: Date.now(),
|
|
158
|
+
});
|
|
159
|
+
await expect(svc.transferOwnership(org.id, other, owner)).rejects.toThrow();
|
|
160
|
+
});
|
|
161
|
+
it("throws NOT_FOUND if target member does not exist", async () => {
|
|
162
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
163
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
164
|
+
const owner = "owner-7";
|
|
165
|
+
const org = await orgRepo.createOrg(owner, "Org7", "org7");
|
|
166
|
+
await memberRepo.addMember({
|
|
167
|
+
id: "m9",
|
|
168
|
+
orgId: org.id,
|
|
169
|
+
userId: owner,
|
|
170
|
+
role: "owner",
|
|
171
|
+
joinedAt: Date.now(),
|
|
172
|
+
});
|
|
173
|
+
await expect(svc.transferOwnership(org.id, owner, "nonexistent")).rejects.toThrow();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
describe("createOrg", () => {
|
|
177
|
+
it("happy path — creates org and owner membership", async () => {
|
|
178
|
+
const owner = "owner-co1";
|
|
179
|
+
const result = await service.createOrg(owner, "My Org", "my-org-co1");
|
|
180
|
+
expect(result.id).toEqual(expect.any(String));
|
|
181
|
+
expect(result.name).toBe("My Org");
|
|
182
|
+
expect(result.slug).toBe("my-org-co1");
|
|
183
|
+
// Verify member row was created
|
|
184
|
+
const { memberRepo } = await setup(db);
|
|
185
|
+
const member = await memberRepo.findMember(result.id, owner);
|
|
186
|
+
expect(member?.role).toBe("owner");
|
|
187
|
+
});
|
|
188
|
+
it("throws CONFLICT (TRPCError) when slug is already taken", async () => {
|
|
189
|
+
await service.createOrg("user-a", "Org A", "taken-slug-co");
|
|
190
|
+
const err = await service.createOrg("user-b", "Org B", "taken-slug-co").catch((e) => e);
|
|
191
|
+
expect(err).toBeInstanceOf(TRPCError);
|
|
192
|
+
expect(err.code).toBe("CONFLICT");
|
|
193
|
+
});
|
|
194
|
+
it("throws CONFLICT (TRPCError) when DB unique constraint fires (race condition path)", async () => {
|
|
195
|
+
// Simulate the race: getBySlug returns null (both concurrent requests passed the check),
|
|
196
|
+
// but the DB insert fails with a unique constraint violation.
|
|
197
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
198
|
+
await orgRepo.createOrg("user-race", "Race Org", "race-slug-co");
|
|
199
|
+
// Bypass the service-level slug check to reach the DB insert directly
|
|
200
|
+
vi.spyOn(orgRepo, "getBySlug").mockResolvedValueOnce(null);
|
|
201
|
+
const racingSvc = new OrgService(orgRepo, memberRepo, db);
|
|
202
|
+
// This reaches the DB insert directly with the duplicate slug
|
|
203
|
+
const err = await racingSvc.createOrg("user-race2", "Race Org 2", "race-slug-co").catch((e) => e);
|
|
204
|
+
expect(err).toBeInstanceOf(TRPCError);
|
|
205
|
+
expect(err.code).toBe("CONFLICT");
|
|
206
|
+
});
|
|
207
|
+
it("throws BAD_REQUEST for invalid slug format", async () => {
|
|
208
|
+
const err = await service.createOrg("user-c", "Org C", "INVALID_SLUG").catch((e) => e);
|
|
209
|
+
expect(err).toBeInstanceOf(TRPCError);
|
|
210
|
+
expect(err.code).toBe("BAD_REQUEST");
|
|
211
|
+
});
|
|
212
|
+
it("logs error when compensating delete also fails", async () => {
|
|
213
|
+
const { logger: loggerModule } = await import("../config/logger.js");
|
|
214
|
+
const errorSpy = vi.spyOn(loggerModule, "error");
|
|
215
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
216
|
+
const failingMemberRepo = {
|
|
217
|
+
...memberRepo,
|
|
218
|
+
addMember: async () => {
|
|
219
|
+
throw new Error("addMember boom");
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
const mockDb = {
|
|
223
|
+
delete: () => ({
|
|
224
|
+
where: () => Promise.reject(new Error("delete boom")),
|
|
225
|
+
}),
|
|
226
|
+
};
|
|
227
|
+
const svc = new OrgService(orgRepo, failingMemberRepo, mockDb);
|
|
228
|
+
await expect(svc.createOrg("user-e", "Org E", "double-fail")).rejects.toThrow("addMember boom");
|
|
229
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("Compensating org delete failed"), expect.objectContaining({ err: expect.any(Error) }));
|
|
230
|
+
errorSpy.mockRestore();
|
|
231
|
+
});
|
|
232
|
+
it("partial failure — throws when addMember fails, org creation rolled back", async () => {
|
|
233
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
234
|
+
const failingMemberRepo = {
|
|
235
|
+
...memberRepo,
|
|
236
|
+
addMember: async () => {
|
|
237
|
+
throw new Error("addMember boom");
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
const svc = new OrgService(orgRepo, failingMemberRepo, db);
|
|
241
|
+
await expect(svc.createOrg("user-d", "Org D", "partial-fail-co")).rejects.toThrow("addMember boom");
|
|
242
|
+
expect(await orgRepo.getBySlug("partial-fail-co")).toBeNull();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
describe("validateSlug", () => {
|
|
246
|
+
it("accepts a valid slug", async () => {
|
|
247
|
+
expect(() => service.validateSlug("valid-slug-12")).not.toThrow();
|
|
248
|
+
});
|
|
249
|
+
it("rejects a slug with invalid characters", async () => {
|
|
250
|
+
expect(() => service.validateSlug("UPPERCASE")).toThrow();
|
|
251
|
+
});
|
|
252
|
+
it("rejects a slug that is too short", async () => {
|
|
253
|
+
expect(() => service.validateSlug("ab")).toThrow();
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
describe("getOrg", () => {
|
|
257
|
+
it("returns org with members and invites", async () => {
|
|
258
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
259
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
260
|
+
const owner = "owner-g1";
|
|
261
|
+
const org = await orgRepo.createOrg(owner, "Get Org", "get-org");
|
|
262
|
+
await memberRepo.addMember({
|
|
263
|
+
id: "mg1",
|
|
264
|
+
orgId: org.id,
|
|
265
|
+
userId: owner,
|
|
266
|
+
role: "owner",
|
|
267
|
+
joinedAt: Date.now(),
|
|
268
|
+
});
|
|
269
|
+
const result = await svc.getOrg(org.id);
|
|
270
|
+
expect(result.id).toBe(org.id);
|
|
271
|
+
expect(result.members).toHaveLength(1);
|
|
272
|
+
expect(result.invites).toHaveLength(0);
|
|
273
|
+
});
|
|
274
|
+
it("throws NOT_FOUND for unknown org", async () => {
|
|
275
|
+
await expect(service.getOrg("nonexistent")).rejects.toThrow();
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
describe("deleteOrg", () => {
|
|
279
|
+
it("deletes org when called by owner", async () => {
|
|
280
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
281
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
282
|
+
const owner = "owner-d1";
|
|
283
|
+
const org = await orgRepo.createOrg(owner, "Del Org", "del-org");
|
|
284
|
+
await memberRepo.addMember({
|
|
285
|
+
id: "md1",
|
|
286
|
+
orgId: org.id,
|
|
287
|
+
userId: owner,
|
|
288
|
+
role: "owner",
|
|
289
|
+
joinedAt: Date.now(),
|
|
290
|
+
});
|
|
291
|
+
await svc.deleteOrg(org.id, owner);
|
|
292
|
+
// Org should be gone
|
|
293
|
+
const found = await orgRepo.getById(org.id);
|
|
294
|
+
expect(found).toBeNull();
|
|
295
|
+
// Members should be gone
|
|
296
|
+
const members = await memberRepo.listMembers(org.id);
|
|
297
|
+
expect(members).toHaveLength(0);
|
|
298
|
+
});
|
|
299
|
+
it("deletes org with members and invites", async () => {
|
|
300
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
301
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
302
|
+
const owner = "owner-d3";
|
|
303
|
+
const member = "member-d3";
|
|
304
|
+
const org = await orgRepo.createOrg(owner, "Del Org 3", "del-org-3");
|
|
305
|
+
await memberRepo.addMember({
|
|
306
|
+
id: "md4",
|
|
307
|
+
orgId: org.id,
|
|
308
|
+
userId: owner,
|
|
309
|
+
role: "owner",
|
|
310
|
+
joinedAt: Date.now(),
|
|
311
|
+
});
|
|
312
|
+
await memberRepo.addMember({
|
|
313
|
+
id: "md5",
|
|
314
|
+
orgId: org.id,
|
|
315
|
+
userId: member,
|
|
316
|
+
role: "member",
|
|
317
|
+
joinedAt: Date.now(),
|
|
318
|
+
});
|
|
319
|
+
await memberRepo.createInvite({
|
|
320
|
+
id: "inv-d3",
|
|
321
|
+
orgId: org.id,
|
|
322
|
+
email: "invited@example.com",
|
|
323
|
+
role: "member",
|
|
324
|
+
invitedBy: owner,
|
|
325
|
+
token: "tok-d3",
|
|
326
|
+
expiresAt: Date.now() + 86400000,
|
|
327
|
+
createdAt: Date.now(),
|
|
328
|
+
});
|
|
329
|
+
await svc.deleteOrg(org.id, owner);
|
|
330
|
+
expect(await orgRepo.getById(org.id)).toBeNull();
|
|
331
|
+
expect(await memberRepo.listMembers(org.id)).toHaveLength(0);
|
|
332
|
+
});
|
|
333
|
+
it("throws FORBIDDEN when non-owner tries to delete", async () => {
|
|
334
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
335
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
336
|
+
const owner = "owner-d1b";
|
|
337
|
+
const other = "other-d1b";
|
|
338
|
+
const org = await orgRepo.createOrg(owner, "Del Org B", "del-org-b");
|
|
339
|
+
await memberRepo.addMember({
|
|
340
|
+
id: "md1b",
|
|
341
|
+
orgId: org.id,
|
|
342
|
+
userId: owner,
|
|
343
|
+
role: "owner",
|
|
344
|
+
joinedAt: Date.now(),
|
|
345
|
+
});
|
|
346
|
+
await memberRepo.addMember({
|
|
347
|
+
id: "md2b",
|
|
348
|
+
orgId: org.id,
|
|
349
|
+
userId: other,
|
|
350
|
+
role: "admin",
|
|
351
|
+
joinedAt: Date.now(),
|
|
352
|
+
});
|
|
353
|
+
await expect(svc.deleteOrg(org.id, other)).rejects.toThrow();
|
|
354
|
+
});
|
|
355
|
+
it("throws NOT_FOUND for nonexistent org", async () => {
|
|
356
|
+
await expect(service.deleteOrg("nonexistent", userId)).rejects.toThrow();
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
describe("inviteMember", () => {
|
|
360
|
+
it("creates an invite and returns it", async () => {
|
|
361
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
362
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
363
|
+
const owner = "owner-i1";
|
|
364
|
+
const org = await orgRepo.createOrg(owner, "Invite Org", "invite-org");
|
|
365
|
+
await memberRepo.addMember({
|
|
366
|
+
id: "mi1",
|
|
367
|
+
orgId: org.id,
|
|
368
|
+
userId: owner,
|
|
369
|
+
role: "owner",
|
|
370
|
+
joinedAt: Date.now(),
|
|
371
|
+
});
|
|
372
|
+
const invite = await svc.inviteMember(org.id, owner, "new@example.com", "member");
|
|
373
|
+
expect(invite.email).toBe("new@example.com");
|
|
374
|
+
expect(invite.role).toBe("member");
|
|
375
|
+
});
|
|
376
|
+
it("throws FORBIDDEN when non-admin invites", async () => {
|
|
377
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
378
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
379
|
+
const owner = "owner-i2";
|
|
380
|
+
const org = await orgRepo.createOrg(owner, "Org I2", "org-i2");
|
|
381
|
+
await memberRepo.addMember({
|
|
382
|
+
id: "mi2",
|
|
383
|
+
orgId: org.id,
|
|
384
|
+
userId: owner,
|
|
385
|
+
role: "owner",
|
|
386
|
+
joinedAt: Date.now(),
|
|
387
|
+
});
|
|
388
|
+
await expect(svc.inviteMember(org.id, "stranger", "x@x.com", "member")).rejects.toThrow();
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
describe("revokeInvite", () => {
|
|
392
|
+
it("revokes an existing invite", async () => {
|
|
393
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
394
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
395
|
+
const owner = "owner-r1";
|
|
396
|
+
const org = await orgRepo.createOrg(owner, "Revoke Org", "revoke-org");
|
|
397
|
+
await memberRepo.addMember({
|
|
398
|
+
id: "mr1",
|
|
399
|
+
orgId: org.id,
|
|
400
|
+
userId: owner,
|
|
401
|
+
role: "owner",
|
|
402
|
+
joinedAt: Date.now(),
|
|
403
|
+
});
|
|
404
|
+
const invite = await svc.inviteMember(org.id, owner, "rev@example.com", "member");
|
|
405
|
+
await expect(svc.revokeInvite(org.id, owner, invite.id)).resolves.not.toThrow();
|
|
406
|
+
});
|
|
407
|
+
it("throws NOT_FOUND for unknown invite", async () => {
|
|
408
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
409
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
410
|
+
const owner = "owner-r2";
|
|
411
|
+
const org = await orgRepo.createOrg(owner, "Revoke Org 2", "revoke-org-2");
|
|
412
|
+
await memberRepo.addMember({
|
|
413
|
+
id: "mr2",
|
|
414
|
+
orgId: org.id,
|
|
415
|
+
userId: owner,
|
|
416
|
+
role: "owner",
|
|
417
|
+
joinedAt: Date.now(),
|
|
418
|
+
});
|
|
419
|
+
await expect(svc.revokeInvite(org.id, owner, "nonexistent-invite")).rejects.toThrow();
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
describe("changeRole", () => {
|
|
423
|
+
it("changes a member's role", async () => {
|
|
424
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
425
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
426
|
+
const owner = "owner-c1";
|
|
427
|
+
const member = "member-c1";
|
|
428
|
+
const org = await orgRepo.createOrg(owner, "Change Org", "change-org");
|
|
429
|
+
await memberRepo.addMember({
|
|
430
|
+
id: "mc1",
|
|
431
|
+
orgId: org.id,
|
|
432
|
+
userId: owner,
|
|
433
|
+
role: "owner",
|
|
434
|
+
joinedAt: Date.now(),
|
|
435
|
+
});
|
|
436
|
+
await memberRepo.addMember({
|
|
437
|
+
id: "mc2",
|
|
438
|
+
orgId: org.id,
|
|
439
|
+
userId: member,
|
|
440
|
+
role: "member",
|
|
441
|
+
joinedAt: Date.now(),
|
|
442
|
+
});
|
|
443
|
+
await expect(svc.changeRole(org.id, owner, member, "admin")).resolves.not.toThrow();
|
|
444
|
+
expect((await memberRepo.findMember(org.id, member))?.role).toBe("admin");
|
|
445
|
+
});
|
|
446
|
+
it("throws BAD_REQUEST when trying to change owner's role", async () => {
|
|
447
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
448
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
449
|
+
const owner = "owner-c2";
|
|
450
|
+
const org = await orgRepo.createOrg(owner, "Change Org 2", "change-org-2");
|
|
451
|
+
await memberRepo.addMember({
|
|
452
|
+
id: "mc3",
|
|
453
|
+
orgId: org.id,
|
|
454
|
+
userId: owner,
|
|
455
|
+
role: "owner",
|
|
456
|
+
joinedAt: Date.now(),
|
|
457
|
+
});
|
|
458
|
+
await expect(svc.changeRole(org.id, owner, owner, "admin")).rejects.toThrow();
|
|
459
|
+
});
|
|
460
|
+
it("throws NOT_FOUND when target member does not exist", async () => {
|
|
461
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
462
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
463
|
+
const owner = "owner-c3";
|
|
464
|
+
const org = await orgRepo.createOrg(owner, "Change Org 3", "change-org-3");
|
|
465
|
+
await memberRepo.addMember({
|
|
466
|
+
id: "mc4",
|
|
467
|
+
orgId: org.id,
|
|
468
|
+
userId: owner,
|
|
469
|
+
role: "owner",
|
|
470
|
+
joinedAt: Date.now(),
|
|
471
|
+
});
|
|
472
|
+
await expect(svc.changeRole(org.id, owner, "nobody", "admin")).rejects.toThrow();
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
describe("removeMember", () => {
|
|
476
|
+
it("removes a regular member", async () => {
|
|
477
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
478
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
479
|
+
const owner = "owner-rm1";
|
|
480
|
+
const member = "member-rm1";
|
|
481
|
+
const org = await orgRepo.createOrg(owner, "Remove Org", "remove-org");
|
|
482
|
+
await memberRepo.addMember({
|
|
483
|
+
id: "mrm1",
|
|
484
|
+
orgId: org.id,
|
|
485
|
+
userId: owner,
|
|
486
|
+
role: "owner",
|
|
487
|
+
joinedAt: Date.now(),
|
|
488
|
+
});
|
|
489
|
+
await memberRepo.addMember({
|
|
490
|
+
id: "mrm2",
|
|
491
|
+
orgId: org.id,
|
|
492
|
+
userId: member,
|
|
493
|
+
role: "member",
|
|
494
|
+
joinedAt: Date.now(),
|
|
495
|
+
});
|
|
496
|
+
await expect(svc.removeMember(org.id, owner, member)).resolves.not.toThrow();
|
|
497
|
+
});
|
|
498
|
+
it("throws BAD_REQUEST when trying to remove the owner", async () => {
|
|
499
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
500
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
501
|
+
const owner = "owner-rm2";
|
|
502
|
+
const org = await orgRepo.createOrg(owner, "Remove Org 2", "remove-org-2");
|
|
503
|
+
await memberRepo.addMember({
|
|
504
|
+
id: "mrm3",
|
|
505
|
+
orgId: org.id,
|
|
506
|
+
userId: owner,
|
|
507
|
+
role: "owner",
|
|
508
|
+
joinedAt: Date.now(),
|
|
509
|
+
});
|
|
510
|
+
await expect(svc.removeMember(org.id, owner, owner)).rejects.toThrow();
|
|
511
|
+
});
|
|
512
|
+
it("throws NOT_FOUND when target member does not exist", async () => {
|
|
513
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
514
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
515
|
+
const owner = "owner-rm3";
|
|
516
|
+
const org = await orgRepo.createOrg(owner, "Remove Org 3", "remove-org-3");
|
|
517
|
+
await memberRepo.addMember({
|
|
518
|
+
id: "mrm4",
|
|
519
|
+
orgId: org.id,
|
|
520
|
+
userId: owner,
|
|
521
|
+
role: "owner",
|
|
522
|
+
joinedAt: Date.now(),
|
|
523
|
+
});
|
|
524
|
+
await expect(svc.removeMember(org.id, owner, "nobody")).rejects.toThrow();
|
|
525
|
+
});
|
|
526
|
+
it("allows removing an admin when the owner is still present (count > 1)", async () => {
|
|
527
|
+
const { orgRepo, memberRepo } = await setup(db);
|
|
528
|
+
const svc = new OrgService(orgRepo, memberRepo, db);
|
|
529
|
+
const owner = "owner-rm4";
|
|
530
|
+
const admin = "admin-rm4";
|
|
531
|
+
const org = await orgRepo.createOrg(owner, "Remove Org 4", "remove-org-4");
|
|
532
|
+
await memberRepo.addMember({
|
|
533
|
+
id: "mrm5",
|
|
534
|
+
orgId: org.id,
|
|
535
|
+
userId: owner,
|
|
536
|
+
role: "owner",
|
|
537
|
+
joinedAt: Date.now(),
|
|
538
|
+
});
|
|
539
|
+
await memberRepo.addMember({
|
|
540
|
+
id: "mrm6",
|
|
541
|
+
orgId: org.id,
|
|
542
|
+
userId: admin,
|
|
543
|
+
role: "admin",
|
|
544
|
+
joinedAt: Date.now(),
|
|
545
|
+
});
|
|
546
|
+
// Owner counts as admin/owner too (count = 2), so removing the admin succeeds
|
|
547
|
+
await expect(svc.removeMember(org.id, owner, admin)).resolves.not.toThrow();
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
2
|
+
import type { PlatformDb } from "../db/index.js";
|
|
3
|
+
export declare function createTestDb(): Promise<{
|
|
4
|
+
db: PlatformDb;
|
|
5
|
+
pool: PGlite;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function beginTestTransaction(pool: PGlite): Promise<void>;
|
|
8
|
+
export declare function rollbackTestTransaction(pool: PGlite): Promise<void>;
|
|
9
|
+
export declare function endTestTransaction(pool: PGlite): Promise<void>;
|
|
10
|
+
export declare function truncateAllTables(pool: PGlite): Promise<void>;
|
|
11
|
+
export interface SeedMeterEventOptions {
|
|
12
|
+
id: string;
|
|
13
|
+
tenant: string;
|
|
14
|
+
charge: number;
|
|
15
|
+
timestamp: number;
|
|
16
|
+
capability?: string;
|
|
17
|
+
provider?: string;
|
|
18
|
+
cost?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function seedMeterEvent(db: PlatformDb, opts: SeedMeterEventOptions): Promise<void>;
|
|
21
|
+
export interface SeedUsageSummaryOptions {
|
|
22
|
+
id: string;
|
|
23
|
+
tenant: string;
|
|
24
|
+
totalCharge: number;
|
|
25
|
+
windowStart: number;
|
|
26
|
+
windowEnd: number;
|
|
27
|
+
capability?: string;
|
|
28
|
+
provider?: string;
|
|
29
|
+
eventCount?: number;
|
|
30
|
+
totalCost?: number;
|
|
31
|
+
totalDuration?: number;
|
|
32
|
+
}
|
|
33
|
+
export declare function seedUsageSummary(db: PlatformDb, opts: SeedUsageSummaryOptions): Promise<void>;
|
package/dist/test/db.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { PGlite } from "@electric-sql/pglite";
|
|
2
|
+
import { drizzle } from "drizzle-orm/pglite";
|
|
3
|
+
import { migrate } from "drizzle-orm/pglite/migrator";
|
|
4
|
+
import * as schema from "../db/schema/index.js";
|
|
5
|
+
import { meterEvents, usageSummaries } from "../db/schema/meter-events.js";
|
|
6
|
+
let migratedSnapshot = null;
|
|
7
|
+
async function getSnapshot() {
|
|
8
|
+
if (migratedSnapshot)
|
|
9
|
+
return migratedSnapshot;
|
|
10
|
+
const pool = new PGlite();
|
|
11
|
+
await migrate(drizzle(pool, { schema }), { migrationsFolder: "./drizzle/migrations" });
|
|
12
|
+
migratedSnapshot = await pool.dumpDataDir("auto");
|
|
13
|
+
await pool.close();
|
|
14
|
+
return migratedSnapshot;
|
|
15
|
+
}
|
|
16
|
+
export async function createTestDb() {
|
|
17
|
+
const snapshot = await getSnapshot();
|
|
18
|
+
const pool = new PGlite({ loadDataDir: snapshot });
|
|
19
|
+
const db = drizzle(pool, { schema });
|
|
20
|
+
return { db, pool };
|
|
21
|
+
}
|
|
22
|
+
export async function beginTestTransaction(pool) {
|
|
23
|
+
await pool.query("BEGIN");
|
|
24
|
+
await pool.query("SAVEPOINT test_tx");
|
|
25
|
+
}
|
|
26
|
+
export async function rollbackTestTransaction(pool) {
|
|
27
|
+
await pool.query("ROLLBACK TO SAVEPOINT test_tx");
|
|
28
|
+
await pool.query("RELEASE SAVEPOINT test_tx");
|
|
29
|
+
await pool.query("SAVEPOINT test_tx");
|
|
30
|
+
}
|
|
31
|
+
export async function endTestTransaction(pool) {
|
|
32
|
+
await pool.query("ROLLBACK");
|
|
33
|
+
}
|
|
34
|
+
export async function truncateAllTables(pool) {
|
|
35
|
+
const result = await pool.query(`SELECT tablename FROM pg_tables WHERE schemaname = 'public'`);
|
|
36
|
+
const tables = result.rows.map((r) => `"${r.tablename}"`).join(", ");
|
|
37
|
+
if (tables) {
|
|
38
|
+
await pool.query(`TRUNCATE ${tables} RESTART IDENTITY CASCADE`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function seedMeterEvent(db, opts) {
|
|
42
|
+
await db.insert(meterEvents).values({
|
|
43
|
+
id: opts.id,
|
|
44
|
+
tenant: opts.tenant,
|
|
45
|
+
cost: opts.cost ?? 0,
|
|
46
|
+
charge: opts.charge,
|
|
47
|
+
capability: opts.capability ?? "llm",
|
|
48
|
+
provider: opts.provider ?? "openai",
|
|
49
|
+
timestamp: opts.timestamp,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export async function seedUsageSummary(db, opts) {
|
|
53
|
+
await db.insert(usageSummaries).values({
|
|
54
|
+
id: opts.id,
|
|
55
|
+
tenant: opts.tenant,
|
|
56
|
+
capability: opts.capability ?? "tts",
|
|
57
|
+
provider: opts.provider ?? "kokoro",
|
|
58
|
+
eventCount: opts.eventCount ?? 1,
|
|
59
|
+
totalCost: opts.totalCost ?? 0,
|
|
60
|
+
totalCharge: opts.totalCharge,
|
|
61
|
+
totalDuration: opts.totalDuration ?? 0,
|
|
62
|
+
windowStart: opts.windowStart,
|
|
63
|
+
windowEnd: opts.windowEnd,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { router, createCallerFactory, publicProcedure, protectedProcedure, adminProcedure, tenantProcedure, orgMemberProcedure, setTrpcOrgMemberRepo, type TRPCContext, } from "./init.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { router, createCallerFactory, publicProcedure, protectedProcedure, adminProcedure, tenantProcedure, orgMemberProcedure, setTrpcOrgMemberRepo, } from "./init.js";
|