@wopr-network/platform-core 1.0.0 → 1.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.
Files changed (87) hide show
  1. package/coverage/coverage-summary.json +119 -0
  2. package/dist/admin/index.d.ts +1 -1
  3. package/dist/auth/better-auth.d.ts +45 -0
  4. package/dist/auth/better-auth.js +70 -47
  5. package/dist/auth/index.d.ts +12 -0
  6. package/dist/auth/index.js +7 -0
  7. package/dist/billing/drizzle-webhook-seen-repository.d.ts +1 -1
  8. package/dist/billing/index.d.ts +4 -4
  9. package/dist/billing/index.js +4 -4
  10. package/dist/billing/payram/webhook.test.js +1 -1
  11. package/dist/billing/stripe/index.d.ts +5 -5
  12. package/dist/billing/stripe/index.js +2 -2
  13. package/dist/billing/stripe/stripe-payment-processor.js +1 -1
  14. package/dist/credits/auto-topup-charge.d.ts +2 -2
  15. package/dist/credits/auto-topup-charge.test.js +1 -1
  16. package/dist/credits/auto-topup-schedule.d.ts +1 -1
  17. package/dist/credits/auto-topup-schedule.test.js +1 -1
  18. package/dist/credits/auto-topup-settings-repository.test.js +1 -1
  19. package/dist/credits/auto-topup-usage.d.ts +1 -1
  20. package/dist/credits/auto-topup-usage.test.js +1 -1
  21. package/dist/credits/index.d.ts +1 -1
  22. package/dist/credits/index.js +1 -1
  23. package/dist/db/schema/index.d.ts +1 -1
  24. package/dist/db/schema/index.js +1 -1
  25. package/dist/email/index.d.ts +1 -1
  26. package/dist/index.d.ts +4 -4
  27. package/dist/index.js +4 -3
  28. package/dist/metering/aggregator.test.js +1 -1
  29. package/dist/metering/emitter.test.js +1 -1
  30. package/dist/metering/load-test.bench.js +1 -1
  31. package/dist/metering/metering.test.js +1 -1
  32. package/dist/metering/reconciliation-cron.test.js +2 -2
  33. package/dist/metering/reconciliation-repository.test.js +1 -1
  34. package/dist/middleware/index.d.ts +3 -3
  35. package/dist/middleware/index.js +2 -2
  36. package/dist/security/credential-vault/index.d.ts +2 -2
  37. package/dist/security/index.d.ts +7 -7
  38. package/dist/security/index.js +7 -7
  39. package/dist/security/redirect-allowlist.js +10 -8
  40. package/dist/security/tenant-keys/index.d.ts +6 -6
  41. package/dist/security/tenant-keys/index.js +3 -3
  42. package/dist/tenancy/index.d.ts +3 -3
  43. package/dist/tenancy/org-service.d.ts +1 -1
  44. package/dist/tenancy/org-service.test.js +1 -1
  45. package/dist/trpc/index.d.ts +1 -1
  46. package/dist/trpc/index.js +1 -1
  47. package/dist/trpc/init.test.js +3 -5
  48. package/package.json +2 -1
  49. package/src/admin/index.ts +1 -1
  50. package/src/auth/better-auth.ts +129 -48
  51. package/src/auth/index.ts +31 -0
  52. package/src/billing/drizzle-webhook-seen-repository.ts +1 -1
  53. package/src/billing/index.ts +11 -13
  54. package/src/billing/payram/webhook.test.ts +1 -1
  55. package/src/billing/stripe/index.ts +17 -5
  56. package/src/billing/stripe/stripe-payment-processor.test.ts +2 -3
  57. package/src/billing/stripe/stripe-payment-processor.ts +1 -1
  58. package/src/credits/auto-topup-charge.test.ts +2 -2
  59. package/src/credits/auto-topup-charge.ts +2 -2
  60. package/src/credits/auto-topup-schedule.test.ts +1 -1
  61. package/src/credits/auto-topup-schedule.ts +1 -1
  62. package/src/credits/auto-topup-settings-repository.test.ts +1 -1
  63. package/src/credits/auto-topup-usage.test.ts +1 -1
  64. package/src/credits/auto-topup-usage.ts +1 -1
  65. package/src/credits/index.ts +1 -1
  66. package/src/db/schema/index.ts +1 -1
  67. package/src/email/index.ts +3 -3
  68. package/src/index.ts +13 -17
  69. package/src/metering/aggregator.test.ts +1 -1
  70. package/src/metering/emitter.test.ts +1 -1
  71. package/src/metering/load-test.bench.ts +1 -1
  72. package/src/metering/metering.test.ts +1 -1
  73. package/src/metering/reconciliation-cron.test.ts +2 -2
  74. package/src/metering/reconciliation-repository.test.ts +2 -2
  75. package/src/middleware/index.ts +5 -5
  76. package/src/middleware/rate-limit.test.ts +1 -1
  77. package/src/middleware/rate-limit.ts +1 -1
  78. package/src/security/credential-vault/index.ts +2 -2
  79. package/src/security/index.ts +43 -38
  80. package/src/security/redirect-allowlist.ts +11 -8
  81. package/src/security/tenant-keys/index.ts +10 -6
  82. package/src/tenancy/index.ts +3 -3
  83. package/src/tenancy/org-service.test.ts +1 -1
  84. package/src/tenancy/org-service.ts +1 -1
  85. package/src/trpc/index.ts +5 -5
  86. package/src/trpc/init.test.ts +8 -10
  87. package/vitest.config.ts +4 -0
@@ -11,25 +11,84 @@
11
11
  import { type BetterAuthOptions, betterAuth } from "better-auth";
12
12
  import { twoFactor } from "better-auth/plugins";
13
13
  import type { Pool } from "pg";
14
- import type { PlatformDb } from "../db/index.js";
15
14
  import { RoleStore } from "../admin/role-store.js";
16
15
  import { logger } from "../config/logger.js";
17
16
  import { initTwoFactorSchema } from "../db/auth-user-repository.js";
17
+ import type { PlatformDb } from "../db/index.js";
18
18
  import { getEmailClient } from "../email/client.js";
19
19
  import { passwordResetEmailTemplate, verifyEmailTemplate } from "../email/templates.js";
20
20
  import { generateVerificationToken, initVerificationSchema, PgEmailVerifier } from "../email/verification.js";
21
21
  import { createUserCreator, type IUserCreator } from "./user-creator.js";
22
22
 
23
+ /** OAuth provider credentials. */
24
+ export interface OAuthProvider {
25
+ clientId: string;
26
+ clientSecret: string;
27
+ }
28
+
29
+ /** Rate limit rule for a specific auth endpoint. */
30
+ export interface AuthRateLimitRule {
31
+ window: number;
32
+ max: number;
33
+ }
34
+
23
35
  /** Configuration for initializing Better Auth in platform-core. */
24
36
  export interface BetterAuthConfig {
25
37
  pool: Pool;
26
38
  db: PlatformDb;
39
+
40
+ // --- Required ---
41
+ /** HMAC secret for session tokens. Falls back to BETTER_AUTH_SECRET env var. */
42
+ secret?: string;
43
+ /** Base URL for OAuth callbacks. Falls back to BETTER_AUTH_URL env var. */
44
+ baseURL?: string;
45
+
46
+ // --- Auth features ---
47
+ /** Route prefix. Default: "/api/auth" */
48
+ basePath?: string;
49
+ /** Email+password config. Default: enabled with 12-char min. */
50
+ emailAndPassword?: { enabled: boolean; minPasswordLength?: number };
51
+ /** OAuth providers. Default: reads GITHUB/DISCORD/GOOGLE env vars. */
52
+ socialProviders?: {
53
+ github?: OAuthProvider;
54
+ discord?: OAuthProvider;
55
+ google?: OAuthProvider;
56
+ };
57
+ /** Trusted providers for account linking. Default: ["github", "google"] */
58
+ trustedProviders?: string[];
59
+ /** Enable 2FA plugin. Default: true */
60
+ twoFactor?: boolean;
61
+
62
+ // --- Session & cookies ---
63
+ /** Cookie cache max age in seconds. Default: 300 (5 min) */
64
+ sessionCacheMaxAge?: number;
65
+ /** Cookie prefix. Default: "better-auth" */
66
+ cookiePrefix?: string;
67
+ /** Cookie domain (e.g., ".wopr.bot"). Falls back to COOKIE_DOMAIN env var. */
68
+ cookieDomain?: string;
69
+
70
+ // --- Rate limiting ---
71
+ /** Global rate limit window in seconds. Default: 60 */
72
+ rateLimitWindow?: number;
73
+ /** Global rate limit max requests. Default: 100 */
74
+ rateLimitMax?: number;
75
+ /** Per-endpoint rate limit overrides. Default: sign-in/sign-up/reset limits. */
76
+ rateLimitRules?: Record<string, AuthRateLimitRule>;
77
+
78
+ // --- Origins ---
79
+ /** Trusted origins for CORS. Falls back to UI_ORIGIN env var. */
80
+ trustedOrigins?: string[];
81
+
82
+ // --- Lifecycle hooks ---
27
83
  /** Called after a new user signs up (e.g., create personal tenant). */
28
84
  onUserCreated?: (userId: string, userName: string, email: string) => Promise<void>;
29
85
  }
30
86
 
31
- const BETTER_AUTH_SECRET = process.env.BETTER_AUTH_SECRET || "";
32
- const BETTER_AUTH_URL = process.env.BETTER_AUTH_URL || "http://localhost:3100";
87
+ const DEFAULT_RATE_LIMIT_RULES: Record<string, AuthRateLimitRule> = {
88
+ "/sign-in/email": { window: 900, max: 5 },
89
+ "/sign-up/email": { window: 3600, max: 10 },
90
+ "/request-password-reset": { window: 3600, max: 3 },
91
+ };
33
92
 
34
93
  let _config: BetterAuthConfig | null = null;
35
94
  let _userCreator: IUserCreator | null = null;
@@ -47,32 +106,59 @@ async function getUserCreator(): Promise<IUserCreator> {
47
106
  return _userCreatorPromise;
48
107
  }
49
108
 
50
- function authOptions(pool: Pool): BetterAuthOptions {
109
+ /** Resolve OAuth providers from config or env vars. */
110
+ function resolveSocialProviders(cfg: BetterAuthConfig): BetterAuthOptions["socialProviders"] {
111
+ if (cfg.socialProviders) return cfg.socialProviders;
112
+ return {
113
+ ...(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET
114
+ ? { github: { clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET } }
115
+ : {}),
116
+ ...(process.env.DISCORD_CLIENT_ID && process.env.DISCORD_CLIENT_SECRET
117
+ ? { discord: { clientId: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET } }
118
+ : {}),
119
+ ...(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET
120
+ ? { google: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET } }
121
+ : {}),
122
+ };
123
+ }
124
+
125
+ function authOptions(cfg: BetterAuthConfig): BetterAuthOptions {
126
+ const pool = cfg.pool;
127
+ const secret = cfg.secret || process.env.BETTER_AUTH_SECRET;
128
+ if (!secret) {
129
+ if (process.env.NODE_ENV === "production") {
130
+ throw new Error("BETTER_AUTH_SECRET is required in production");
131
+ }
132
+ logger.warn("BetterAuth secret not configured — sessions may be insecure");
133
+ }
134
+ const baseURL = cfg.baseURL || process.env.BETTER_AUTH_URL || "http://localhost:3100";
135
+ const basePath = cfg.basePath || "/api/auth";
136
+ const cookieDomain = cfg.cookieDomain || process.env.COOKIE_DOMAIN;
137
+ const trustedOrigins =
138
+ cfg.trustedOrigins ||
139
+ (process.env.UI_ORIGIN || "http://localhost:3001")
140
+ .split(",")
141
+ .map((o) => o.trim())
142
+ .filter(Boolean);
143
+ // Default minPasswordLength: 12 — caller must explicitly override, not accidentally omit
144
+ const emailAndPassword = cfg.emailAndPassword
145
+ ? { minPasswordLength: 12, ...cfg.emailAndPassword }
146
+ : { enabled: true, minPasswordLength: 12 };
147
+
51
148
  return {
52
149
  database: pool,
53
- secret: BETTER_AUTH_SECRET,
54
- baseURL: BETTER_AUTH_URL,
55
- basePath: "/api/auth",
56
- socialProviders: {
57
- ...(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET
58
- ? { github: { clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET } }
59
- : {}),
60
- ...(process.env.DISCORD_CLIENT_ID && process.env.DISCORD_CLIENT_SECRET
61
- ? { discord: { clientId: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET } }
62
- : {}),
63
- ...(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET
64
- ? { google: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET } }
65
- : {}),
66
- },
150
+ secret: secret || "",
151
+ baseURL,
152
+ basePath,
153
+ socialProviders: resolveSocialProviders(cfg),
67
154
  account: {
68
155
  accountLinking: {
69
156
  enabled: true,
70
- trustedProviders: ["github", "google"],
157
+ trustedProviders: cfg.trustedProviders ?? ["github", "google"],
71
158
  },
72
159
  },
73
160
  emailAndPassword: {
74
- enabled: true,
75
- minPasswordLength: 12,
161
+ ...emailAndPassword,
76
162
  sendResetPassword: async ({ user, url }) => {
77
163
  try {
78
164
  const emailClient = getEmailClient();
@@ -99,12 +185,20 @@ function authOptions(pool: Pool): BetterAuthOptions {
99
185
  logger.error("Failed to run user creator:", error);
100
186
  }
101
187
 
188
+ if (cfg.onUserCreated) {
189
+ try {
190
+ await cfg.onUserCreated(user.id, user.name || user.email, user.email);
191
+ } catch (error) {
192
+ logger.error("Failed to run onUserCreated callback:", error);
193
+ }
194
+ }
195
+
102
196
  if (user.emailVerified) return;
103
197
 
104
198
  try {
105
199
  await initVerificationSchema(pool);
106
200
  const { token } = await generateVerificationToken(pool, user.id);
107
- const verifyUrl = `${BETTER_AUTH_URL}/auth/verify?token=${token}`;
201
+ const verifyUrl = `${baseURL}${basePath}/verify?token=${token}`;
108
202
  const emailClient = getEmailClient();
109
203
  const template = verifyEmailTemplate(verifyUrl, user.email);
110
204
  await emailClient.send({
@@ -116,45 +210,30 @@ function authOptions(pool: Pool): BetterAuthOptions {
116
210
  } catch (error) {
117
211
  logger.error("Failed to send verification email:", error);
118
212
  }
119
-
120
- // Delegate personal tenant creation to the consumer
121
- if (_config?.onUserCreated) {
122
- try {
123
- await _config.onUserCreated(user.id, user.name || user.email, user.email);
124
- } catch (error) {
125
- logger.error("Failed to run onUserCreated callback:", error);
126
- }
127
- }
128
213
  },
129
214
  },
130
215
  },
131
216
  },
132
217
  session: {
133
- cookieCache: { enabled: true, maxAge: 5 * 60 },
218
+ cookieCache: { enabled: true, maxAge: cfg.sessionCacheMaxAge ?? 300 },
134
219
  },
135
220
  advanced: {
136
- cookiePrefix: "better-auth",
221
+ cookiePrefix: cfg.cookiePrefix || "better-auth",
137
222
  cookies: {
138
223
  session_token: {
139
- attributes: {
140
- domain: process.env.COOKIE_DOMAIN || ".wopr.bot",
141
- },
224
+ attributes: cookieDomain ? { domain: cookieDomain } : {},
142
225
  },
143
226
  },
144
227
  },
145
- plugins: [twoFactor()],
228
+ plugins: cfg.twoFactor !== false ? [twoFactor()] : [],
146
229
  rateLimit: {
147
230
  enabled: true,
148
- window: 60,
149
- max: 100,
150
- customRules: {
151
- "/sign-in/email": { window: 900, max: 5 },
152
- "/sign-up/email": { window: 3600, max: 10 },
153
- "/request-password-reset": { window: 3600, max: 3 },
154
- },
231
+ window: cfg.rateLimitWindow ?? 60,
232
+ max: cfg.rateLimitMax ?? 100,
233
+ customRules: { ...DEFAULT_RATE_LIMIT_RULES, ...cfg.rateLimitRules },
155
234
  storage: "memory",
156
235
  },
157
- trustedOrigins: (process.env.UI_ORIGIN || "http://localhost:3001").split(","),
236
+ trustedOrigins,
158
237
  };
159
238
  }
160
239
 
@@ -174,9 +253,11 @@ export async function runAuthMigrations(): Promise<void> {
174
253
  if (!_config) throw new Error("BetterAuth not initialized — call initBetterAuth() first");
175
254
  type DbModule = { getMigrations: (opts: BetterAuthOptions) => Promise<{ runMigrations: () => Promise<void> }> };
176
255
  const { getMigrations } = (await import("better-auth/db")) as unknown as DbModule;
177
- const { runMigrations } = await getMigrations(authOptions(_config.pool));
256
+ const { runMigrations } = await getMigrations(authOptions(_config));
178
257
  await runMigrations();
179
- await initTwoFactorSchema(_config.pool);
258
+ if (_config.twoFactor !== false) {
259
+ await initTwoFactorSchema(_config.pool);
260
+ }
180
261
  }
181
262
 
182
263
  let _auth: Auth | null = null;
@@ -188,7 +269,7 @@ let _auth: Auth | null = null;
188
269
  export function getAuth(): Auth {
189
270
  if (!_auth) {
190
271
  if (!_config) throw new Error("BetterAuth not initialized — call initBetterAuth() first");
191
- _auth = betterAuth(authOptions(_config.pool));
272
+ _auth = betterAuth(authOptions(_config));
192
273
  }
193
274
  return _auth;
194
275
  }
package/src/auth/index.ts CHANGED
@@ -518,3 +518,34 @@ export async function validateTenantAccess(
518
518
  const member = await orgMemberRepo.findMember(requestedTenantId, userId);
519
519
  return member !== null;
520
520
  }
521
+
522
+ // ---------------------------------------------------------------------------
523
+ // Re-exports — Better Auth factory, middleware, and repositories
524
+ // ---------------------------------------------------------------------------
525
+
526
+ export type { IApiKeyRepository } from "./api-key-repository.js";
527
+ export { DrizzleApiKeyRepository } from "./api-key-repository.js";
528
+ export type {
529
+ Auth,
530
+ AuthRateLimitRule,
531
+ BetterAuthConfig,
532
+ OAuthProvider,
533
+ } from "./better-auth.js";
534
+ // Test utilities — do not call in production code
535
+ export {
536
+ getAuth,
537
+ getEmailVerifier,
538
+ initBetterAuth,
539
+ resetAuth,
540
+ resetUserCreator,
541
+ runAuthMigrations,
542
+ setAuth,
543
+ } from "./better-auth.js";
544
+ export type { ILoginHistoryRepository, LoginHistoryEntry } from "./login-history-repository.js";
545
+ export { BetterAuthLoginHistoryRepository } from "./login-history-repository.js";
546
+ export type { SessionAuthEnv } from "./middleware.js";
547
+ export { dualAuth, sessionAuth } from "./middleware.js";
548
+ export type { IUserCreator } from "./user-creator.js";
549
+ export { createUserCreator } from "./user-creator.js";
550
+ export type { IUserRoleRepository } from "./user-role-repository.js";
551
+ export { DrizzleUserRoleRepository } from "./user-role-repository.js";
@@ -1,7 +1,7 @@
1
1
  import { and, eq, lt } from "drizzle-orm";
2
+ import type { WebhookSeenEvent } from "../credits/repository-types.js";
2
3
  import type { PlatformDb } from "../db/index.js";
3
4
  import { webhookSeenEvents } from "../db/schema/index.js";
4
- import type { WebhookSeenEvent } from "../credits/repository-types.js";
5
5
  import type { IWebhookSeenRepository } from "./webhook-seen-repository.js";
6
6
 
7
7
  export class DrizzleWebhookSeenRepository implements IWebhookSeenRepository {
@@ -1,22 +1,20 @@
1
+ export { DrizzleWebhookSeenRepository } from "./drizzle-webhook-seen-repository.js";
1
2
  export type {
2
- SavedPaymentMethod,
3
- CheckoutOpts,
4
- CheckoutSession,
5
3
  ChargeOpts,
6
4
  ChargeResult,
7
- SetupResult,
5
+ CheckoutOpts,
6
+ CheckoutSession,
7
+ Invoice,
8
+ IPaymentProcessor,
8
9
  PortalOpts,
10
+ SavedPaymentMethod,
11
+ SetupResult,
9
12
  WebhookResult,
10
- IPaymentProcessor,
11
- Invoice,
12
13
  } from "./payment-processor.js";
13
14
  export { PaymentMethodOwnershipError } from "./payment-processor.js";
14
- export type { IWebhookSeenRepository } from "./webhook-seen-repository.js";
15
- export { noOpReplayGuard } from "./webhook-seen-repository.js";
16
- export { DrizzleWebhookSeenRepository } from "./drizzle-webhook-seen-repository.js";
17
-
18
- // Stripe
19
- export * from "./stripe/index.js";
20
-
21
15
  // PayRam
22
16
  export * from "./payram/index.js";
17
+ // Stripe
18
+ export * from "./stripe/index.js";
19
+ export type { IWebhookSeenRepository } from "./webhook-seen-repository.js";
20
+ export { noOpReplayGuard } from "./webhook-seen-repository.js";
@@ -7,9 +7,9 @@
7
7
 
8
8
  import type { PGlite } from "@electric-sql/pglite";
9
9
  import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
10
+ import { CreditLedger } from "../../credits/credit-ledger.js";
10
11
  import type { PlatformDb } from "../../db/index.js";
11
12
  import { createTestDb, truncateAllTables } from "../../test/db.js";
12
- import { CreditLedger } from "../../credits/credit-ledger.js";
13
13
  import { DrizzleWebhookSeenRepository } from "../drizzle-webhook-seen-repository.js";
14
14
  import { noOpReplayGuard } from "../webhook-seen-repository.js";
15
15
  import { PayRamChargeRepository } from "./charge-store.js";
@@ -1,14 +1,26 @@
1
1
  export { createCreditCheckoutSession, createVpsCheckoutSession } from "./checkout.js";
2
2
  export { createStripeClient, loadStripeConfig } from "./client.js";
3
- export type { CreditPricePoint, CreditPriceMap } from "./credit-prices.js";
4
- export { CREDIT_PRICE_POINTS, loadCreditPriceMap, getCreditAmountForPurchase, lookupCreditPrice, getConfiguredPriceIds } from "./credit-prices.js";
5
- export { detachPaymentMethod, detachAllPaymentMethods } from "./payment-methods.js";
3
+ export type { CreditPriceMap, CreditPricePoint } from "./credit-prices.js";
4
+ export {
5
+ CREDIT_PRICE_POINTS,
6
+ getConfiguredPriceIds,
7
+ getCreditAmountForPurchase,
8
+ loadCreditPriceMap,
9
+ lookupCreditPrice,
10
+ } from "./credit-prices.js";
6
11
  export type { DetachPaymentMethodOpts } from "./payment-methods.js";
12
+ export { detachAllPaymentMethods, detachPaymentMethod } from "./payment-methods.js";
7
13
  export { createPortalSession } from "./portal.js";
8
14
  export type { SetupIntentOpts } from "./setup-intent.js";
9
15
  export { createSetupIntent } from "./setup-intent.js";
10
- export type { StripeWebhookHandlerResult, StripePaymentProcessorDeps } from "./stripe-payment-processor.js";
16
+ export type { StripePaymentProcessorDeps, StripeWebhookHandlerResult } from "./stripe-payment-processor.js";
11
17
  export { StripePaymentProcessor } from "./stripe-payment-processor.js";
12
18
  export type { ITenantCustomerRepository } from "./tenant-store.js";
13
19
  export { DrizzleTenantCustomerRepository, TenantCustomerRepository } from "./tenant-store.js";
14
- export type { TenantCustomerRow, CreditCheckoutOpts, PortalSessionOpts, VpsCheckoutOpts, StripeBillingConfig } from "./types.js";
20
+ export type {
21
+ CreditCheckoutOpts,
22
+ PortalSessionOpts,
23
+ StripeBillingConfig,
24
+ TenantCustomerRow,
25
+ VpsCheckoutOpts,
26
+ } from "./types.js";
@@ -1,7 +1,7 @@
1
1
  import type Stripe from "stripe";
2
2
  import { beforeEach, describe, expect, it, vi } from "vitest";
3
- import { Credit } from "../../credits/credit.js";
4
3
  import type { IAutoTopupEventLogRepository } from "../../credits/auto-topup-event-log-repository.js";
4
+ import { Credit } from "../../credits/credit.js";
5
5
  import type { CreditTransaction, ICreditLedger } from "../../credits/credit-ledger.js";
6
6
  import { PaymentMethodOwnershipError } from "../payment-processor.js";
7
7
  import type { CreditPriceMap } from "./credit-prices.js";
@@ -309,7 +309,6 @@ describe("StripePaymentProcessor", () => {
309
309
  tenantRepo: mocks.tenantRepo,
310
310
  webhookSecret: "whsec_test",
311
311
  creditLedger: mocks.creditLedger,
312
-
313
312
  });
314
313
 
315
314
  await expect(
@@ -447,7 +446,7 @@ describe("StripePaymentProcessor", () => {
447
446
  tenantRepo: mocks.tenantRepo,
448
447
  webhookSecret: "whsec_test",
449
448
  creditLedger: mocks.creditLedger,
450
-
449
+
451
450
  priceMap,
452
451
  });
453
452
 
@@ -1,7 +1,7 @@
1
1
  import type Stripe from "stripe";
2
- import { Credit } from "../../credits/credit.js";
3
2
  import { chargeAutoTopup } from "../../credits/auto-topup-charge.js";
4
3
  import type { IAutoTopupEventLogRepository } from "../../credits/auto-topup-event-log-repository.js";
4
+ import { Credit } from "../../credits/credit.js";
5
5
  import type { ICreditLedger } from "../../credits/credit-ledger.js";
6
6
  import {
7
7
  type ChargeOpts,
@@ -5,11 +5,11 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vites
5
5
  import type { PlatformDb } from "../db/index.js";
6
6
  import { creditAutoTopup } from "../db/schema/credit-auto-topup.js";
7
7
  import { createTestDb, truncateAllTables } from "../test/db.js";
8
- import { Credit } from "./credit.js";
9
- import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
10
8
  import { type AutoTopupChargeDeps, chargeAutoTopup, MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
11
9
  import { DrizzleAutoTopupEventLogRepository } from "./auto-topup-event-log-repository.js";
10
+ import { Credit } from "./credit.js";
12
11
  import { CreditLedger } from "./credit-ledger.js";
12
+ import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
13
13
 
14
14
  function mockStripe(overrides?: {
15
15
  paymentIntentId?: string;
@@ -1,9 +1,9 @@
1
1
  import Stripe from "stripe";
2
2
  import { logger } from "../config/logger.js";
3
- import type { Credit } from "./credit.js";
4
- import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
5
3
  import type { IAutoTopupEventLogRepository } from "./auto-topup-event-log-repository.js";
4
+ import type { Credit } from "./credit.js";
6
5
  import type { ICreditLedger } from "./credit-ledger.js";
6
+ import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
7
7
 
8
8
  /** After this many consecutive Stripe failures, the auto-topup mode is disabled. */
9
9
  export const MAX_CONSECUTIVE_FAILURES = 3;
@@ -2,9 +2,9 @@ import type { PGlite } from "@electric-sql/pglite";
2
2
  import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
3
3
  import type { PlatformDb } from "../db/index.js";
4
4
  import { beginTestTransaction, createTestDb, endTestTransaction, rollbackTestTransaction } from "../test/db.js";
5
- import { Credit } from "./credit.js";
6
5
  import { runScheduledTopups, type ScheduleTopupDeps } from "./auto-topup-schedule.js";
7
6
  import { DrizzleAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
7
+ import { Credit } from "./credit.js";
8
8
 
9
9
  describe("runScheduledTopups", () => {
10
10
  let pool: PGlite;
@@ -1,8 +1,8 @@
1
1
  import { logger } from "../config/logger.js";
2
- import type { Credit } from "./credit.js";
3
2
  import type { AutoTopupChargeResult } from "./auto-topup-charge.js";
4
3
  import { MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
5
4
  import type { IAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
5
+ import type { Credit } from "./credit.js";
6
6
 
7
7
  export interface ScheduleTopupDeps {
8
8
  settingsRepo: IAutoTopupSettingsRepository;
@@ -2,8 +2,8 @@ import type { PGlite } from "@electric-sql/pglite";
2
2
  import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
3
3
  import type { PlatformDb } from "../db/index.js";
4
4
  import { beginTestTransaction, createTestDb, endTestTransaction, rollbackTestTransaction } from "../test/db.js";
5
- import { Credit } from "./credit.js";
6
5
  import { DrizzleAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
6
+ import { Credit } from "./credit.js";
7
7
 
8
8
  describe("DrizzleAutoTopupSettingsRepository", () => {
9
9
  let pool: PGlite;
@@ -2,9 +2,9 @@ import type { PGlite } from "@electric-sql/pglite";
2
2
  import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
3
3
  import type { PlatformDb } from "../db/index.js";
4
4
  import { createTestDb, truncateAllTables } from "../test/db.js";
5
- import { Credit } from "./credit.js";
6
5
  import { DrizzleAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
7
6
  import { maybeTriggerUsageTopup, type UsageTopupDeps } from "./auto-topup-usage.js";
7
+ import { Credit } from "./credit.js";
8
8
  import { CreditLedger } from "./credit-ledger.js";
9
9
 
10
10
  describe("maybeTriggerUsageTopup", () => {
@@ -1,8 +1,8 @@
1
1
  import { logger } from "../config/logger.js";
2
- import type { Credit } from "./credit.js";
3
2
  import type { AutoTopupChargeResult } from "./auto-topup-charge.js";
4
3
  import { MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
5
4
  import type { IAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
5
+ import type { Credit } from "./credit.js";
6
6
  import type { ICreditLedger } from "./credit-ledger.js";
7
7
 
8
8
  export interface UsageTopupDeps {
@@ -9,6 +9,7 @@ export {
9
9
  computeNextScheduleAt,
10
10
  DrizzleAutoTopupSettingsRepository,
11
11
  } from "./auto-topup-settings-repository.js";
12
+ export { Credit } from "./credit.js";
12
13
  export type { CreditExpiryCronConfig, CreditExpiryCronResult } from "./credit-expiry-cron.js";
13
14
  export { runCreditExpiryCron } from "./credit-expiry-cron.js";
14
15
  export type {
@@ -21,5 +22,4 @@ export type {
21
22
  } from "./credit-ledger.js";
22
23
  export { CreditLedger, DrizzleCreditLedger, InsufficientBalanceError } from "./credit-ledger.js";
23
24
  export { grantSignupCredits, SIGNUP_GRANT } from "./signup-grant.js";
24
- export { Credit } from "./credit.js";
25
25
  export type { ITenantCustomerRepository, TenantCustomerRow } from "./tenant-customer-repository.js";
@@ -5,9 +5,9 @@ export * from "./admin-users.js";
5
5
  export * from "./affiliate.js";
6
6
  export * from "./affiliate-fraud.js";
7
7
  export * from "./coupon-codes.js";
8
- export * from "./credits.js";
9
8
  export * from "./credit-auto-topup.js";
10
9
  export * from "./credit-auto-topup-settings.js";
10
+ export * from "./credits.js";
11
11
  export * from "./dividend-distributions.js";
12
12
  export * from "./email-notifications.js";
13
13
  export * from "./meter-events.js";
@@ -20,12 +20,12 @@ export { DrizzleNotificationPreferencesStore } from "./notification-preferences-
20
20
  export type { INotificationQueueRepository } from "./notification-queue-store.js";
21
21
  export { DrizzleNotificationQueueStore } from "./notification-queue-store.js";
22
22
  export type {
23
- NotificationPrefs,
24
- NotificationStatus,
25
- QueuedNotification,
26
23
  NotificationEmailType,
27
24
  NotificationInput,
25
+ NotificationPrefs,
28
26
  NotificationRow,
27
+ NotificationStatus,
28
+ QueuedNotification,
29
29
  } from "./notification-repository-types.js";
30
30
  export { NotificationService } from "./notification-service.js";
31
31
  export type { TemplateName as NotificationTemplateName } from "./notification-templates.js";
package/src/index.ts CHANGED
@@ -1,36 +1,32 @@
1
1
  // Database
2
- export type { PlatformDb, PlatformSchema } from "./db/index.js";
3
- export { createDb, schema } from "./db/index.js";
4
2
 
5
3
  // Admin
6
4
  export * from "./admin/index.js";
7
-
8
5
  // Auth
9
6
  export * from "./auth/index.js";
10
-
11
7
  // Billing (selective — ITenantCustomerRepository/TenantCustomerRow also in credits)
12
8
  export {
13
- PaymentMethodOwnershipError,
14
- noOpReplayGuard,
15
- DrizzleWebhookSeenRepository,
16
- type SavedPaymentMethod,
17
- type CheckoutOpts,
18
- type CheckoutSession,
19
9
  type ChargeOpts,
20
10
  type ChargeResult,
21
- type SetupResult,
22
- type PortalOpts,
23
- type WebhookResult,
24
- type IPaymentProcessor,
11
+ type CheckoutOpts,
12
+ type CheckoutSession,
13
+ DrizzleWebhookSeenRepository,
25
14
  type Invoice,
15
+ type IPaymentProcessor,
26
16
  type IWebhookSeenRepository,
17
+ noOpReplayGuard,
18
+ PaymentMethodOwnershipError,
19
+ type PortalOpts,
20
+ type SavedPaymentMethod,
21
+ type SetupResult,
22
+ type WebhookResult,
27
23
  } from "./billing/index.js";
28
-
29
24
  // Config
30
- export { config, billingConfigSchema, type PlatformConfig } from "./config/index.js";
31
-
25
+ export { billingConfigSchema, config, type PlatformConfig } from "./config/index.js";
32
26
  // Credits
33
27
  export * from "./credits/index.js";
28
+ export type { PlatformDb, PlatformSchema } from "./db/index.js";
29
+ export { createDb, schema } from "./db/index.js";
34
30
 
35
31
  // Email
36
32
  export * from "./email/index.js";
@@ -1,10 +1,10 @@
1
1
  import crypto from "node:crypto";
2
2
  import type { PGlite } from "@electric-sql/pglite";
3
3
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
4
+ import { Credit } from "../credits/credit.js";
4
5
  import type { PlatformDb } from "../db/index.js";
5
6
  import { meterEvents, usageSummaries } from "../db/schema/meter-events.js";
6
7
  import { createTestDb } from "../test/db.js";
7
- import { Credit } from "../credits/credit.js";
8
8
  import { DrizzleMeterAggregator } from "./aggregator.js";
9
9
  import { DrizzleUsageSummaryRepository } from "./drizzle-usage-summary-repository.js";
10
10
 
@@ -3,9 +3,9 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import type { PGlite } from "@electric-sql/pglite";
5
5
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+ import { Credit } from "../credits/credit.js";
6
7
  import type { PlatformDb } from "../db/index.js";
7
8
  import { beginTestTransaction, createTestDb, endTestTransaction, rollbackTestTransaction } from "../test/db.js";
8
- import { Credit } from "../credits/credit.js";
9
9
  import { MeterDLQ } from "./dlq.js";
10
10
  import { DrizzleMeterEmitter } from "./emitter.js";
11
11
  import { DrizzleMeterEventRepository } from "./meter-event-repository.js";
@@ -1,9 +1,9 @@
1
1
  import { unlinkSync } from "node:fs";
2
2
  import type { PGlite } from "@electric-sql/pglite";
3
3
  import { afterEach, beforeEach, bench, describe } from "vitest";
4
+ import { Credit } from "../credits/credit.js";
4
5
  import type { PlatformDb } from "../db/index.js";
5
6
  import { createTestDb } from "../test/db.js";
6
- import { Credit } from "../credits/credit.js";
7
7
  import { MeterAggregator } from "./aggregator.js";
8
8
  import { DrizzleUsageSummaryRepository } from "./drizzle-usage-summary-repository.js";
9
9
  import { MeterEmitter } from "./emitter.js";
@@ -2,10 +2,10 @@ import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import type { PGlite } from "@electric-sql/pglite";
3
3
  import { eq, sql } from "drizzle-orm";
4
4
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
5
+ import { Credit } from "../credits/credit.js";
5
6
  import type { PlatformDb } from "../db/index.js";
6
7
  import { meterEvents } from "../db/schema/meter-events.js";
7
8
  import { createTestDb, truncateAllTables } from "../test/db.js";
8
- import { Credit } from "../credits/credit.js";
9
9
  import { MeterAggregator } from "./aggregator.js";
10
10
  import { DrizzleUsageSummaryRepository } from "./drizzle-usage-summary-repository.js";
11
11
  import { MeterEmitter } from "./emitter.js";