@nehorai/credits 1.0.2 → 1.3.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.
Files changed (134) hide show
  1. package/dist/adapters/index.cjs +463 -0
  2. package/dist/adapters/index.cjs.map +1 -0
  3. package/dist/adapters/index.d.cts +66 -0
  4. package/dist/adapters/index.d.ts +66 -3
  5. package/dist/adapters/index.js +435 -1
  6. package/dist/adapters/index.js.map +1 -1
  7. package/dist/auth/index.cjs +96 -0
  8. package/dist/auth/index.cjs.map +1 -0
  9. package/dist/auth/{api-key.d.ts → index.d.cts} +9 -6
  10. package/dist/auth/index.d.ts +37 -5
  11. package/dist/auth/index.js +65 -7
  12. package/dist/auth/index.js.map +1 -1
  13. package/dist/core/index.cjs +522 -0
  14. package/dist/core/index.cjs.map +1 -0
  15. package/dist/core/index.d.cts +119 -0
  16. package/dist/core/index.d.ts +115 -25
  17. package/dist/core/index.js +475 -27
  18. package/dist/core/index.js.map +1 -1
  19. package/dist/{core/deferred.d.ts → deferred-C7rrRfkm.d.cts} +7 -6
  20. package/dist/{core/deferred.js → deferred-C7rrRfkm.d.ts} +23 -29
  21. package/dist/index.cjs +2506 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.cts +437 -0
  24. package/dist/index.d.ts +429 -49
  25. package/dist/index.js +2399 -62
  26. package/dist/index.js.map +1 -1
  27. package/dist/repository/index.cjs +816 -0
  28. package/dist/repository/index.cjs.map +1 -0
  29. package/dist/repository/{memory/index.d.ts → index.d.cts} +35 -5
  30. package/dist/repository/index.d.ts +97 -7
  31. package/dist/repository/index.js +783 -10
  32. package/dist/repository/index.js.map +1 -1
  33. package/dist/sdk/index.cjs +608 -0
  34. package/dist/sdk/index.cjs.map +1 -0
  35. package/dist/sdk/index.d.cts +495 -0
  36. package/dist/sdk/index.d.ts +491 -9
  37. package/dist/sdk/index.js +568 -12
  38. package/dist/sdk/index.js.map +1 -1
  39. package/dist/service/index.cjs +670 -0
  40. package/dist/service/index.cjs.map +1 -0
  41. package/dist/service/{credits-service.d.ts → index.d.cts} +9 -7
  42. package/dist/service/index.d.ts +141 -3
  43. package/dist/service/index.js +641 -1
  44. package/dist/service/index.js.map +1 -1
  45. package/dist/{core/types.d.ts → types-BBxIWHK2.d.cts} +25 -35
  46. package/dist/types-BBxIWHK2.d.ts +277 -0
  47. package/dist/types-D1vdeBcA.d.cts +305 -0
  48. package/dist/{auth/types.d.ts → types-OZU3I5OM.d.cts} +5 -4
  49. package/dist/types-OZU3I5OM.d.ts +32 -0
  50. package/dist/{repository/types.d.ts → types-aJsyv8df.d.ts} +35 -13
  51. package/package.json +14 -13
  52. package/dist/adapters/generic.d.ts +0 -18
  53. package/dist/adapters/generic.d.ts.map +0 -1
  54. package/dist/adapters/generic.js +0 -147
  55. package/dist/adapters/generic.js.map +0 -1
  56. package/dist/adapters/index.d.ts.map +0 -1
  57. package/dist/adapters/types.d.ts +0 -45
  58. package/dist/adapters/types.d.ts.map +0 -1
  59. package/dist/adapters/types.js +0 -8
  60. package/dist/adapters/types.js.map +0 -1
  61. package/dist/auth/api-key.d.ts.map +0 -1
  62. package/dist/auth/api-key.js +0 -94
  63. package/dist/auth/api-key.js.map +0 -1
  64. package/dist/auth/index.d.ts.map +0 -1
  65. package/dist/auth/types.d.ts.map +0 -1
  66. package/dist/auth/types.js +0 -2
  67. package/dist/auth/types.js.map +0 -1
  68. package/dist/config/costs.d.ts +0 -61
  69. package/dist/config/costs.d.ts.map +0 -1
  70. package/dist/config/costs.js +0 -86
  71. package/dist/config/costs.js.map +0 -1
  72. package/dist/config/index.d.ts +0 -165
  73. package/dist/config/index.d.ts.map +0 -1
  74. package/dist/config/index.js +0 -286
  75. package/dist/config/index.js.map +0 -1
  76. package/dist/core/deferred.d.ts.map +0 -1
  77. package/dist/core/deferred.js.map +0 -1
  78. package/dist/core/errors.d.ts +0 -78
  79. package/dist/core/errors.d.ts.map +0 -1
  80. package/dist/core/errors.js +0 -110
  81. package/dist/core/errors.js.map +0 -1
  82. package/dist/core/index.d.ts.map +0 -1
  83. package/dist/core/operations.d.ts +0 -36
  84. package/dist/core/operations.d.ts.map +0 -1
  85. package/dist/core/operations.js +0 -87
  86. package/dist/core/operations.js.map +0 -1
  87. package/dist/core/types.d.ts.map +0 -1
  88. package/dist/core/types.js +0 -93
  89. package/dist/core/types.js.map +0 -1
  90. package/dist/index.d.ts.map +0 -1
  91. package/dist/notifications/index.d.ts +0 -76
  92. package/dist/notifications/index.d.ts.map +0 -1
  93. package/dist/notifications/index.js +0 -182
  94. package/dist/notifications/index.js.map +0 -1
  95. package/dist/repository/index.d.ts.map +0 -1
  96. package/dist/repository/memory/index.d.ts.map +0 -1
  97. package/dist/repository/memory/index.js +0 -518
  98. package/dist/repository/memory/index.js.map +0 -1
  99. package/dist/repository/types.d.ts.map +0 -1
  100. package/dist/repository/types.js +0 -39
  101. package/dist/repository/types.js.map +0 -1
  102. package/dist/repository/utils.d.ts +0 -22
  103. package/dist/repository/utils.d.ts.map +0 -1
  104. package/dist/repository/utils.js +0 -40
  105. package/dist/repository/utils.js.map +0 -1
  106. package/dist/sdk/admin-client.d.ts +0 -146
  107. package/dist/sdk/admin-client.d.ts.map +0 -1
  108. package/dist/sdk/admin-client.js +0 -196
  109. package/dist/sdk/admin-client.js.map +0 -1
  110. package/dist/sdk/client.d.ts +0 -144
  111. package/dist/sdk/client.d.ts.map +0 -1
  112. package/dist/sdk/client.js +0 -247
  113. package/dist/sdk/client.js.map +0 -1
  114. package/dist/sdk/errors.d.ts +0 -91
  115. package/dist/sdk/errors.d.ts.map +0 -1
  116. package/dist/sdk/errors.js +0 -185
  117. package/dist/sdk/errors.js.map +0 -1
  118. package/dist/sdk/index.d.ts.map +0 -1
  119. package/dist/sdk/types.d.ts +0 -110
  120. package/dist/sdk/types.d.ts.map +0 -1
  121. package/dist/sdk/types.js +0 -20
  122. package/dist/sdk/types.js.map +0 -1
  123. package/dist/service/credits-service.d.ts.map +0 -1
  124. package/dist/service/credits-service.js +0 -372
  125. package/dist/service/credits-service.js.map +0 -1
  126. package/dist/service/index.d.ts.map +0 -1
  127. package/dist/utils/error-utils.d.ts +0 -48
  128. package/dist/utils/error-utils.d.ts.map +0 -1
  129. package/dist/utils/error-utils.js +0 -57
  130. package/dist/utils/error-utils.js.map +0 -1
  131. package/dist/utils/index.d.ts +0 -54
  132. package/dist/utils/index.d.ts.map +0 -1
  133. package/dist/utils/index.js +0 -72
  134. package/dist/utils/index.js.map +0 -1
@@ -1,2 +1,436 @@
1
- export { createGenericAdapter } from "./generic.js";
1
+ // src/config/index.ts
2
+ import { z } from "zod";
3
+
4
+ // src/config/provider.ts
5
+ var configProvider = null;
6
+ function getConfigProvider() {
7
+ return configProvider;
8
+ }
9
+
10
+ // src/config/index.ts
11
+ var tierConfigSchema = z.object({
12
+ tier: z.enum(["free", "basic", "premium", "unlimited"]),
13
+ monthlyCredits: z.number().min(0),
14
+ priceUsd: z.number().min(0),
15
+ features: z.array(z.string())
16
+ });
17
+ var creditSystemConfigSchema = z.object({
18
+ /**
19
+ * Operation costs in credits
20
+ * Keys are operation type names (any non-empty string)
21
+ * Values are positive numbers representing credit cost
22
+ */
23
+ operationCosts: z.record(
24
+ z.string().min(1),
25
+ z.number().positive()
26
+ ),
27
+ /**
28
+ * Display labels for operation types (English canonical labels).
29
+ * Falls back to auto-generating from snake_case keys if not provided.
30
+ */
31
+ operationLabels: z.record(
32
+ z.string().min(1),
33
+ z.string().min(1)
34
+ ).optional().default({}),
35
+ /**
36
+ * Tier configurations
37
+ */
38
+ tierConfigs: z.record(
39
+ z.enum(["free", "basic", "premium", "unlimited"]),
40
+ tierConfigSchema
41
+ ),
42
+ /**
43
+ * Reservation expiry time in milliseconds
44
+ */
45
+ reservationExpiryMs: z.number().positive().default(5 * 60 * 1e3),
46
+ /**
47
+ * Default credits for new users (free tier)
48
+ */
49
+ defaultFreeCredits: z.number().positive().default(25),
50
+ /**
51
+ * Grace period in days after subscription expires before downgrade
52
+ */
53
+ subscriptionGracePeriodDays: z.number().min(0).default(3),
54
+ /**
55
+ * Low balance notification threshold
56
+ */
57
+ lowBalanceThreshold: z.number().min(0).default(10),
58
+ /**
59
+ * Cooldown in hours between low balance notifications
60
+ */
61
+ lowBalanceNotificationCooldownHours: z.number().positive().default(24),
62
+ /**
63
+ * Feature flags for optional features
64
+ */
65
+ features: z.object({
66
+ journalEntries: z.boolean().default(true),
67
+ notifications: z.boolean().default(true),
68
+ subscriptionExpiry: z.boolean().default(true),
69
+ usageHistory: z.boolean().default(true)
70
+ }).default({
71
+ journalEntries: true,
72
+ notifications: true,
73
+ subscriptionExpiry: true,
74
+ usageHistory: true
75
+ })
76
+ });
77
+ var DEFAULT_CONFIG = {
78
+ operationCosts: {
79
+ story_generation: 5,
80
+ conversation: 2,
81
+ image_generation: 10,
82
+ template_generation: 10
83
+ },
84
+ operationLabels: {
85
+ story_generation: "Story generation",
86
+ conversation: "Conversation",
87
+ image_generation: "Image generation",
88
+ template_generation: "Template generation"
89
+ },
90
+ tierConfigs: {
91
+ free: {
92
+ tier: "free",
93
+ monthlyCredits: 25,
94
+ priceUsd: 0,
95
+ features: [
96
+ "25 credits per month",
97
+ "Basic story generation",
98
+ "Standard image quality"
99
+ ]
100
+ },
101
+ basic: {
102
+ tier: "basic",
103
+ monthlyCredits: 200,
104
+ priceUsd: 9.99,
105
+ features: [
106
+ "200 credits per month",
107
+ "All story features",
108
+ "High quality images",
109
+ "Priority support"
110
+ ]
111
+ },
112
+ premium: {
113
+ tier: "premium",
114
+ monthlyCredits: 500,
115
+ priceUsd: 19.99,
116
+ features: [
117
+ "500 credits per month",
118
+ "All features",
119
+ "Highest quality images",
120
+ "Early access to new features",
121
+ "Priority support"
122
+ ]
123
+ },
124
+ unlimited: {
125
+ tier: "unlimited",
126
+ monthlyCredits: 0,
127
+ // 0 = unlimited
128
+ priceUsd: 49.99,
129
+ features: [
130
+ "Unlimited credits",
131
+ "All features",
132
+ "Highest quality images",
133
+ "Early access to new features",
134
+ "Dedicated support"
135
+ ]
136
+ }
137
+ },
138
+ reservationExpiryMs: 5 * 60 * 1e3,
139
+ // 5 minutes
140
+ defaultFreeCredits: 25,
141
+ subscriptionGracePeriodDays: 3,
142
+ lowBalanceThreshold: 10,
143
+ lowBalanceNotificationCooldownHours: 24,
144
+ features: {
145
+ journalEntries: true,
146
+ notifications: true,
147
+ subscriptionExpiry: true,
148
+ usageHistory: true
149
+ }
150
+ };
151
+ var currentConfig = DEFAULT_CONFIG;
152
+ function loadConfigFromEnv() {
153
+ const envConfig = {};
154
+ const operationCostsEnv = process.env.CREDITS_OPERATION_COSTS;
155
+ if (operationCostsEnv) {
156
+ try {
157
+ const parsed = JSON.parse(operationCostsEnv);
158
+ if (typeof parsed === "object" && parsed !== null) {
159
+ envConfig.operationCosts = {
160
+ ...DEFAULT_CONFIG.operationCosts,
161
+ ...parsed
162
+ };
163
+ }
164
+ } catch {
165
+ console.warn("[Credits Config] Failed to parse CREDITS_OPERATION_COSTS env var as JSON");
166
+ }
167
+ }
168
+ const reservationExpiry = process.env.CREDITS_RESERVATION_EXPIRY_MS;
169
+ if (reservationExpiry) {
170
+ envConfig.reservationExpiryMs = parseInt(reservationExpiry, 10);
171
+ }
172
+ const defaultCredits = process.env.CREDITS_DEFAULT_FREE;
173
+ if (defaultCredits) {
174
+ envConfig.defaultFreeCredits = parseInt(defaultCredits, 10);
175
+ }
176
+ const gracePeriod = process.env.CREDITS_GRACE_PERIOD_DAYS;
177
+ if (gracePeriod) {
178
+ envConfig.subscriptionGracePeriodDays = parseInt(gracePeriod, 10);
179
+ }
180
+ const lowBalanceThreshold = process.env.CREDITS_LOW_BALANCE_THRESHOLD;
181
+ if (lowBalanceThreshold) {
182
+ envConfig.lowBalanceThreshold = parseInt(lowBalanceThreshold, 10);
183
+ }
184
+ const featuresEnv = process.env.CREDITS_FEATURES;
185
+ if (featuresEnv) {
186
+ try {
187
+ const features = JSON.parse(featuresEnv);
188
+ envConfig.features = {
189
+ ...DEFAULT_CONFIG.features,
190
+ ...features
191
+ };
192
+ } catch {
193
+ console.warn("[Credits Config] Failed to parse CREDITS_FEATURES env var");
194
+ }
195
+ }
196
+ return envConfig;
197
+ }
198
+ function initializeConfig(overrides) {
199
+ const envConfig = loadConfigFromEnv();
200
+ const mergedConfig = {
201
+ ...DEFAULT_CONFIG,
202
+ ...envConfig,
203
+ ...overrides,
204
+ operationCosts: {
205
+ ...DEFAULT_CONFIG.operationCosts,
206
+ ...envConfig.operationCosts,
207
+ ...overrides?.operationCosts
208
+ },
209
+ operationLabels: {
210
+ ...DEFAULT_CONFIG.operationLabels,
211
+ ...envConfig.operationLabels,
212
+ ...overrides?.operationLabels
213
+ },
214
+ tierConfigs: {
215
+ ...DEFAULT_CONFIG.tierConfigs,
216
+ ...envConfig.tierConfigs,
217
+ ...overrides?.tierConfigs
218
+ },
219
+ features: {
220
+ ...DEFAULT_CONFIG.features,
221
+ ...envConfig.features,
222
+ ...overrides?.features
223
+ }
224
+ };
225
+ const result = creditSystemConfigSchema.safeParse(mergedConfig);
226
+ if (!result.success) {
227
+ console.error("[Credits Config] Invalid configuration:", result.error.issues);
228
+ throw new Error("Invalid credit system configuration");
229
+ }
230
+ currentConfig = result.data;
231
+ return currentConfig;
232
+ }
233
+ function getConfig() {
234
+ const provider = getConfigProvider();
235
+ if (provider) {
236
+ return provider.getConfig();
237
+ }
238
+ return currentConfig;
239
+ }
240
+ function snakeCaseToTitleCase(str) {
241
+ return str.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
242
+ }
243
+ function getOperationLabel(operationType) {
244
+ const labels = getConfig().operationLabels;
245
+ if (labels && operationType in labels) {
246
+ return labels[operationType];
247
+ }
248
+ return snakeCaseToTitleCase(operationType);
249
+ }
250
+ initializeConfig();
251
+
252
+ // src/core/operations.ts
253
+ async function commitReservationWithJournal(repository, userId, reservationId) {
254
+ const reservation = await repository.getReservation(userId, reservationId);
255
+ if (!reservation) {
256
+ throw new Error(`Reservation ${reservationId} not found`);
257
+ }
258
+ await repository.commitReservationAtomic(userId, reservationId);
259
+ const credits = await repository.getUserCredits(userId);
260
+ if (credits) {
261
+ await repository.createJournalEntry({
262
+ userId,
263
+ entryType: "debit",
264
+ amount: reservation.amount,
265
+ balanceAfter: credits.balance,
266
+ source: "operation_commit",
267
+ referenceId: reservationId,
268
+ referenceType: "reservation",
269
+ description: `Committed ${reservation.amount} credits for ${getOperationLabel(reservation.operationType)}`,
270
+ metadata: {
271
+ operationType: reservation.operationType
272
+ }
273
+ });
274
+ }
275
+ }
276
+ async function releaseReservationWithJournal(repository, userId, reservationId) {
277
+ const reservation = await repository.getReservation(userId, reservationId);
278
+ await repository.releaseReservationAtomic(userId, reservationId);
279
+ if (reservation?.status === "reserved") {
280
+ const credits = await repository.getUserCredits(userId);
281
+ if (credits) {
282
+ await repository.createJournalEntry({
283
+ userId,
284
+ entryType: "credit",
285
+ amount: 0,
286
+ // No actual credits returned (they were reserved, not spent)
287
+ balanceAfter: credits.balance,
288
+ source: "operation_release",
289
+ referenceId: reservationId,
290
+ referenceType: "reservation",
291
+ description: `Released ${reservation.amount} reserved credits for ${getOperationLabel(reservation.operationType)}`,
292
+ metadata: {
293
+ operationType: reservation.operationType,
294
+ amount: reservation.amount
295
+ }
296
+ });
297
+ }
298
+ }
299
+ }
300
+ async function reserveCreditsForOperation(repository, userId, amount, operationType, expiryMs = 5 * 60 * 1e3) {
301
+ const expiresAt = new Date(Date.now() + expiryMs);
302
+ return repository.reserveCreditsAtomic(userId, amount, operationType, expiresAt);
303
+ }
304
+
305
+ // src/adapters/generic.ts
306
+ function generateRequestId() {
307
+ const timestamp = Date.now();
308
+ const randomPart = Math.random().toString(36).substring(2, 14);
309
+ return `req_${timestamp}_${randomPart}`;
310
+ }
311
+ function createGenericAdapter(config) {
312
+ const { repository, authProvider, deferred, operationCosts } = config;
313
+ function getOperationCost(operationType) {
314
+ const cost = operationCosts[operationType];
315
+ if (cost === void 0) {
316
+ throw new Error(
317
+ `Unknown operation type: "${operationType}". Configured types: ${Object.keys(operationCosts).join(", ")}`
318
+ );
319
+ }
320
+ return cost;
321
+ }
322
+ function logUsage(params) {
323
+ deferred.defer(async () => {
324
+ await repository.logUsage({
325
+ userId: params.userId,
326
+ operationType: params.operationType,
327
+ provider: "gemini",
328
+ creditsUsed: params.creditsUsed,
329
+ success: params.success,
330
+ errorMessage: params.errorMessage,
331
+ resourceId: params.resourceId,
332
+ resourceType: params.resourceType,
333
+ requestId: params.requestId
334
+ });
335
+ });
336
+ }
337
+ return {
338
+ withCredits(options, handler) {
339
+ return async (data) => {
340
+ const requestId = generateRequestId();
341
+ const user = await authProvider.getCurrentUser();
342
+ if (!user?.id) {
343
+ return { success: false, error: "Authentication required" };
344
+ }
345
+ if (isPreviewMode(data)) {
346
+ const dummyReservation = createDummyReservation(user.id, options.operationType);
347
+ return handler(user, data, dummyReservation);
348
+ }
349
+ let reservation;
350
+ try {
351
+ const cost = options.customCost ?? getOperationCost(options.operationType);
352
+ reservation = await reserveCreditsForOperation(
353
+ repository,
354
+ user.id,
355
+ cost,
356
+ options.operationType
357
+ );
358
+ } catch (error) {
359
+ const message = error instanceof Error ? error.message : "Failed to reserve credits";
360
+ logUsage({
361
+ userId: user.id,
362
+ operationType: options.operationType,
363
+ creditsUsed: 0,
364
+ success: false,
365
+ errorMessage: message,
366
+ resourceId: options.resourceId,
367
+ resourceType: options.resourceType,
368
+ requestId
369
+ });
370
+ return { success: false, error: message };
371
+ }
372
+ try {
373
+ const result = await handler(user, data, reservation);
374
+ if (result.success) {
375
+ await commitReservationWithJournal(repository, user.id, reservation.id);
376
+ logUsage({
377
+ userId: user.id,
378
+ operationType: options.operationType,
379
+ creditsUsed: reservation.amount,
380
+ success: true,
381
+ resourceId: options.resourceId,
382
+ resourceType: options.resourceType,
383
+ requestId
384
+ });
385
+ } else {
386
+ await releaseReservationWithJournal(repository, user.id, reservation.id);
387
+ logUsage({
388
+ userId: user.id,
389
+ operationType: options.operationType,
390
+ creditsUsed: 0,
391
+ success: false,
392
+ errorMessage: result.error,
393
+ resourceId: options.resourceId,
394
+ resourceType: options.resourceType,
395
+ requestId
396
+ });
397
+ }
398
+ return result;
399
+ } catch (error) {
400
+ const message = error instanceof Error ? error.message : "An unexpected error occurred";
401
+ await releaseReservationWithJournal(repository, user.id, reservation.id);
402
+ logUsage({
403
+ userId: user.id,
404
+ operationType: options.operationType,
405
+ creditsUsed: 0,
406
+ success: false,
407
+ errorMessage: message,
408
+ resourceId: options.resourceId,
409
+ resourceType: options.resourceType,
410
+ requestId
411
+ });
412
+ console.error("Action error:", error);
413
+ return { success: false, error: message };
414
+ }
415
+ };
416
+ }
417
+ };
418
+ }
419
+ function isPreviewMode(data) {
420
+ return typeof data === "object" && data !== null && "preview" in data && data.preview === true;
421
+ }
422
+ function createDummyReservation(userId, operationType) {
423
+ return {
424
+ id: "preview-mode",
425
+ userId,
426
+ amount: 0,
427
+ operationType,
428
+ status: "released",
429
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
430
+ expiresAt: (/* @__PURE__ */ new Date()).toISOString()
431
+ };
432
+ }
433
+ export {
434
+ createGenericAdapter
435
+ };
2
436
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"sources":["../../src/config/index.ts","../../src/config/provider.ts","../../src/core/operations.ts","../../src/adapters/generic.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { SubscriptionTier, TierConfig } from \"../core/types.js\";\nimport { getConfigProvider } from \"./provider.js\";\n\n/**\n * Schema for tier configuration\n */\nconst tierConfigSchema = z.object({\n tier: z.enum([\"free\", \"basic\", \"premium\", \"unlimited\"]),\n monthlyCredits: z.number().min(0),\n priceUsd: z.number().min(0),\n features: z.array(z.string()),\n});\n\n/**\n * Schema for credit system configuration\n */\nconst creditSystemConfigSchema = z.object({\n /**\n * Operation costs in credits\n * Keys are operation type names (any non-empty string)\n * Values are positive numbers representing credit cost\n */\n operationCosts: z.record(\n z.string().min(1),\n z.number().positive()\n ),\n\n /**\n * Display labels for operation types (English canonical labels).\n * Falls back to auto-generating from snake_case keys if not provided.\n */\n operationLabels: z.record(\n z.string().min(1),\n z.string().min(1)\n ).optional().default({}),\n\n /**\n * Tier configurations\n */\n tierConfigs: z.record(\n z.enum([\"free\", \"basic\", \"premium\", \"unlimited\"]),\n tierConfigSchema\n ),\n\n /**\n * Reservation expiry time in milliseconds\n */\n reservationExpiryMs: z.number().positive().default(5 * 60 * 1000),\n\n /**\n * Default credits for new users (free tier)\n */\n defaultFreeCredits: z.number().positive().default(25),\n\n /**\n * Grace period in days after subscription expires before downgrade\n */\n subscriptionGracePeriodDays: z.number().min(0).default(3),\n\n /**\n * Low balance notification threshold\n */\n lowBalanceThreshold: z.number().min(0).default(10),\n\n /**\n * Cooldown in hours between low balance notifications\n */\n lowBalanceNotificationCooldownHours: z.number().positive().default(24),\n\n /**\n * Feature flags for optional features\n */\n features: z.object({\n journalEntries: z.boolean().default(true),\n notifications: z.boolean().default(true),\n subscriptionExpiry: z.boolean().default(true),\n usageHistory: z.boolean().default(true),\n }).default({\n journalEntries: true,\n notifications: true,\n subscriptionExpiry: true,\n usageHistory: true,\n }),\n});\n\n/**\n * Credit system configuration type\n */\nexport type CreditSystemConfig = z.infer<typeof creditSystemConfigSchema>;\n\n/**\n * Default configuration\n */\nconst DEFAULT_CONFIG: CreditSystemConfig = {\n operationCosts: {\n story_generation: 5,\n conversation: 2,\n image_generation: 10,\n template_generation: 10,\n },\n operationLabels: {\n story_generation: \"Story generation\",\n conversation: \"Conversation\",\n image_generation: \"Image generation\",\n template_generation: \"Template generation\",\n },\n tierConfigs: {\n free: {\n tier: \"free\",\n monthlyCredits: 25,\n priceUsd: 0,\n features: [\n \"25 credits per month\",\n \"Basic story generation\",\n \"Standard image quality\",\n ],\n },\n basic: {\n tier: \"basic\",\n monthlyCredits: 200,\n priceUsd: 9.99,\n features: [\n \"200 credits per month\",\n \"All story features\",\n \"High quality images\",\n \"Priority support\",\n ],\n },\n premium: {\n tier: \"premium\",\n monthlyCredits: 500,\n priceUsd: 19.99,\n features: [\n \"500 credits per month\",\n \"All features\",\n \"Highest quality images\",\n \"Early access to new features\",\n \"Priority support\",\n ],\n },\n unlimited: {\n tier: \"unlimited\",\n monthlyCredits: 0, // 0 = unlimited\n priceUsd: 49.99,\n features: [\n \"Unlimited credits\",\n \"All features\",\n \"Highest quality images\",\n \"Early access to new features\",\n \"Dedicated support\",\n ],\n },\n },\n reservationExpiryMs: 5 * 60 * 1000, // 5 minutes\n defaultFreeCredits: 25,\n subscriptionGracePeriodDays: 3,\n lowBalanceThreshold: 10,\n lowBalanceNotificationCooldownHours: 24,\n features: {\n journalEntries: true,\n notifications: true,\n subscriptionExpiry: true,\n usageHistory: true,\n },\n};\n\n/**\n * Current configuration (can be overridden)\n */\nlet currentConfig: CreditSystemConfig = DEFAULT_CONFIG;\n\n/**\n * Load configuration from environment variables\n * Can be extended to load from other sources (file, remote config, etc.)\n *\n * Operation costs are loaded from CREDITS_OPERATION_COSTS as JSON:\n * CREDITS_OPERATION_COSTS={\"story_generation\":5,\"image_generation\":10}\n */\nexport function loadConfigFromEnv(): Partial<CreditSystemConfig> {\n const envConfig: Partial<CreditSystemConfig> = {};\n\n // Load operation costs from env as JSON\n const operationCostsEnv = process.env.CREDITS_OPERATION_COSTS;\n if (operationCostsEnv) {\n try {\n const parsed = JSON.parse(operationCostsEnv);\n if (typeof parsed === \"object\" && parsed !== null) {\n envConfig.operationCosts = {\n ...DEFAULT_CONFIG.operationCosts,\n ...parsed,\n };\n }\n } catch {\n console.warn(\"[Credits Config] Failed to parse CREDITS_OPERATION_COSTS env var as JSON\");\n }\n }\n\n // Load other settings from env\n const reservationExpiry = process.env.CREDITS_RESERVATION_EXPIRY_MS;\n if (reservationExpiry) {\n envConfig.reservationExpiryMs = parseInt(reservationExpiry, 10);\n }\n\n const defaultCredits = process.env.CREDITS_DEFAULT_FREE;\n if (defaultCredits) {\n envConfig.defaultFreeCredits = parseInt(defaultCredits, 10);\n }\n\n const gracePeriod = process.env.CREDITS_GRACE_PERIOD_DAYS;\n if (gracePeriod) {\n envConfig.subscriptionGracePeriodDays = parseInt(gracePeriod, 10);\n }\n\n const lowBalanceThreshold = process.env.CREDITS_LOW_BALANCE_THRESHOLD;\n if (lowBalanceThreshold) {\n envConfig.lowBalanceThreshold = parseInt(lowBalanceThreshold, 10);\n }\n\n // Load feature flags\n const featuresEnv = process.env.CREDITS_FEATURES;\n if (featuresEnv) {\n try {\n const features = JSON.parse(featuresEnv);\n envConfig.features = {\n ...DEFAULT_CONFIG.features,\n ...features,\n };\n } catch {\n console.warn(\"[Credits Config] Failed to parse CREDITS_FEATURES env var\");\n }\n }\n\n return envConfig;\n}\n\n/**\n * Initialize configuration\n * Merges default config with environment overrides\n */\nexport function initializeConfig(overrides?: Partial<CreditSystemConfig>): CreditSystemConfig {\n const envConfig = loadConfigFromEnv();\n\n const mergedConfig = {\n ...DEFAULT_CONFIG,\n ...envConfig,\n ...overrides,\n operationCosts: {\n ...DEFAULT_CONFIG.operationCosts,\n ...envConfig.operationCosts,\n ...overrides?.operationCosts,\n },\n operationLabels: {\n ...DEFAULT_CONFIG.operationLabels,\n ...envConfig.operationLabels,\n ...overrides?.operationLabels,\n },\n tierConfigs: {\n ...DEFAULT_CONFIG.tierConfigs,\n ...envConfig.tierConfigs,\n ...overrides?.tierConfigs,\n },\n features: {\n ...DEFAULT_CONFIG.features,\n ...envConfig.features,\n ...overrides?.features,\n },\n };\n\n // Validate configuration\n const result = creditSystemConfigSchema.safeParse(mergedConfig);\n if (!result.success) {\n console.error(\"[Credits Config] Invalid configuration:\", result.error.issues);\n throw new Error(\"Invalid credit system configuration\");\n }\n\n currentConfig = result.data;\n return currentConfig;\n}\n\n/**\n * Get the current configuration.\n * If an external provider is registered, delegates to it.\n * Otherwise falls back to the local currentConfig.\n */\nexport function getConfig(): CreditSystemConfig {\n const provider = getConfigProvider();\n if (provider) {\n return provider.getConfig();\n }\n return currentConfig;\n}\n\n/**\n * Get all valid operation types from configuration\n * Returns the keys of the operationCosts object\n */\nexport function getValidOperationTypes(): string[] {\n return Object.keys(getConfig().operationCosts);\n}\n\n/**\n * Check if an operation type is valid (configured in the system)\n * @param type - The operation type to check\n * @returns true if the operation type is configured\n */\nexport function isValidOperationType(type: string): boolean {\n return type in getConfig().operationCosts;\n}\n\n/**\n * Get operation cost from configuration\n * @throws Error if the operation type is not configured\n */\nexport function getConfigOperationCost(operationType: string): number {\n const cost = getConfig().operationCosts[operationType];\n if (cost === undefined) {\n throw new Error(\n `Unknown operation type: ${operationType}. Valid types: ${getValidOperationTypes().join(\", \")}`\n );\n }\n return cost;\n}\n\n/**\n * Get tier configuration from configuration\n */\nexport function getConfigTierConfig(tier: SubscriptionTier): TierConfig {\n return getConfig().tierConfigs[tier]!;\n}\n\n/**\n * Get monthly limit for a tier from configuration\n * Returns Infinity for unlimited tier\n */\nexport function getConfigMonthlyLimit(tier: SubscriptionTier): number {\n const config = getConfig().tierConfigs[tier]!;\n return config.monthlyCredits === 0 ? Infinity : config.monthlyCredits;\n}\n\n/**\n * Check if a feature is enabled\n */\nexport function isFeatureEnabled(feature: keyof CreditSystemConfig[\"features\"]): boolean {\n return getConfig().features[feature];\n}\n\n/**\n * Convert a snake_case string to Title Case.\n * E.g., \"story_generation\" -> \"Story Generation\"\n */\nfunction snakeCaseToTitleCase(str: string): string {\n return str\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/**\n * Get the display label for an operation type.\n * Falls back to converting snake_case to Title Case if no label is configured.\n */\nexport function getOperationLabel(operationType: string): string {\n const labels = getConfig().operationLabels;\n if (labels && operationType in labels) {\n return labels[operationType];\n }\n return snakeCaseToTitleCase(operationType);\n}\n\n/**\n * Get all operation labels as a record.\n * For any operation in operationCosts that lacks a label, generates one from the key.\n */\nexport function getOperationLabels(): Record<string, string> {\n const config = getConfig();\n const result: Record<string, string> = {};\n for (const key of Object.keys(config.operationCosts)) {\n result[key] = config.operationLabels?.[key]\n ?? snakeCaseToTitleCase(key);\n }\n return result;\n}\n\n/**\n * Reset configuration to defaults (for testing)\n */\nexport function resetConfig(): void {\n currentConfig = DEFAULT_CONFIG;\n}\n\n// Initialize configuration on module load\ninitializeConfig();\n","import type { CreditSystemConfig } from \"./index.js\";\n\n/** Interface for external config providers (e.g. Firestore-backed) */\nexport interface ICreditConfigProvider {\n getConfig(): CreditSystemConfig;\n}\n\nlet configProvider: ICreditConfigProvider | null = null;\n\n/** Register an external config provider. Called once at app startup. */\nexport function registerConfigProvider(provider: ICreditConfigProvider): void {\n configProvider = provider;\n}\n\n/** Get the registered provider, or null if none registered. */\nexport function getConfigProvider(): ICreditConfigProvider | null {\n return configProvider;\n}\n\n/** Clear registered provider (for testing). */\nexport function clearConfigProvider(): void {\n configProvider = null;\n}\n","/**\n * Core credit operations - framework agnostic\n *\n * Contains the business logic for credit operations that can be\n * used by any adapter or service implementation.\n */\n\nimport type { ICreditRepository } from \"../repository/types.js\";\nimport type { PortableReservation } from \"./types.js\";\nimport { getOperationLabel } from \"../config/index.js\";\n\n/**\n * Commit a reservation with journal entry\n *\n * @param repository - The credit repository\n * @param userId - User ID\n * @param reservationId - Reservation to commit\n */\nexport async function commitReservationWithJournal(\n repository: ICreditRepository,\n userId: string,\n reservationId: string\n): Promise<void> {\n // Get the reservation to know the amount\n const reservation = await repository.getReservation(userId, reservationId);\n if (!reservation) {\n throw new Error(`Reservation ${reservationId} not found`);\n }\n\n // Commit the reservation atomically\n await repository.commitReservationAtomic(userId, reservationId);\n\n // Create journal entry\n const credits = await repository.getUserCredits(userId);\n if (credits) {\n await repository.createJournalEntry({\n userId,\n entryType: \"debit\",\n amount: reservation.amount,\n balanceAfter: credits.balance,\n source: \"operation_commit\",\n referenceId: reservationId,\n referenceType: \"reservation\",\n description: `Committed ${reservation.amount} credits for ${getOperationLabel(reservation.operationType)}`,\n metadata: {\n operationType: reservation.operationType,\n },\n });\n }\n}\n\n/**\n * Release a reservation with journal entry\n *\n * @param repository - The credit repository\n * @param userId - User ID\n * @param reservationId - Reservation to release\n */\nexport async function releaseReservationWithJournal(\n repository: ICreditRepository,\n userId: string,\n reservationId: string\n): Promise<void> {\n // Get the reservation to check its state\n const reservation = await repository.getReservation(userId, reservationId);\n\n // Release the reservation atomically\n await repository.releaseReservationAtomic(userId, reservationId);\n\n // Create journal entry only if reservation was in reserved state\n if (reservation?.status === \"reserved\") {\n const credits = await repository.getUserCredits(userId);\n if (credits) {\n await repository.createJournalEntry({\n userId,\n entryType: \"credit\",\n amount: 0, // No actual credits returned (they were reserved, not spent)\n balanceAfter: credits.balance,\n source: \"operation_release\",\n referenceId: reservationId,\n referenceType: \"reservation\",\n description: `Released ${reservation.amount} reserved credits for ${getOperationLabel(reservation.operationType)}`,\n metadata: {\n operationType: reservation.operationType,\n amount: reservation.amount,\n },\n });\n }\n }\n}\n\n/**\n * Reserve credits for an operation\n *\n * @param repository - The credit repository\n * @param userId - User ID\n * @param amount - Credits to reserve\n * @param operationType - Type of operation\n * @param expiryMs - Reservation expiry time in milliseconds\n * @returns The reservation\n */\nexport async function reserveCreditsForOperation(\n repository: ICreditRepository,\n userId: string,\n amount: number,\n operationType: string,\n expiryMs: number = 5 * 60 * 1000\n): Promise<PortableReservation> {\n const expiresAt = new Date(Date.now() + expiryMs);\n return repository.reserveCreditsAtomic(userId, amount, operationType, expiresAt);\n}\n","/**\n * Generic credits adapter - framework agnostic\n *\n * This adapter uses standard JavaScript APIs and works in any\n * JavaScript environment (Node.js, browser, etc.).\n */\n\nimport type { PortableReservation, WithCreditsOptions } from \"../core/types.js\";\nimport type { ActionResult, CreditsAdapter, CreditsAdapterConfig, CreditActionHandler } from \"./types.js\";\nimport {\n commitReservationWithJournal,\n releaseReservationWithJournal,\n reserveCreditsForOperation,\n} from \"../core/operations.js\";\n\n/**\n * Generate a unique request ID\n */\nfunction generateRequestId(): string {\n const timestamp = Date.now();\n const randomPart = Math.random().toString(36).substring(2, 14);\n return `req_${timestamp}_${randomPart}`;\n}\n\n/**\n * Create a generic credits adapter\n *\n * This adapter:\n * - Authenticates users via the provided auth provider\n * - Reserves credits before action execution\n * - Commits credits on success, releases on failure\n * - Logs usage asynchronously via the deferred executor\n */\nexport function createGenericAdapter(config: CreditsAdapterConfig): CreditsAdapter {\n const { repository, authProvider, deferred, operationCosts } = config;\n\n function getOperationCost(operationType: string): number {\n const cost = operationCosts[operationType];\n if (cost === undefined) {\n throw new Error(\n `Unknown operation type: \"${operationType}\". ` +\n `Configured types: ${Object.keys(operationCosts).join(\", \")}`\n );\n }\n return cost;\n }\n\n function logUsage(params: {\n userId: string;\n operationType: string;\n creditsUsed: number;\n success: boolean;\n errorMessage?: string;\n resourceId?: string;\n resourceType?: string;\n requestId: string;\n }): void {\n deferred.defer(async () => {\n await repository.logUsage({\n userId: params.userId,\n operationType: params.operationType,\n provider: \"gemini\",\n creditsUsed: params.creditsUsed,\n success: params.success,\n errorMessage: params.errorMessage,\n resourceId: params.resourceId,\n resourceType: params.resourceType,\n requestId: params.requestId,\n });\n });\n }\n\n return {\n withCredits<TInput, TOutput>(\n options: WithCreditsOptions,\n handler: CreditActionHandler<TInput, TOutput>\n ): (data: TInput) => Promise<ActionResult<TOutput>> {\n return async (data: TInput): Promise<ActionResult<TOutput>> => {\n const requestId = generateRequestId();\n\n // Authenticate\n const user = await authProvider.getCurrentUser();\n if (!user?.id) {\n return { success: false, error: \"Authentication required\" };\n }\n\n // Handle preview mode - skip credit handling entirely\n if (isPreviewMode(data)) {\n const dummyReservation = createDummyReservation(user.id, options.operationType);\n return handler(user, data, dummyReservation);\n }\n\n // Calculate cost and reserve credits\n let reservation: PortableReservation;\n try {\n const cost = options.customCost ?? getOperationCost(options.operationType);\n reservation = await reserveCreditsForOperation(\n repository,\n user.id,\n cost,\n options.operationType\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to reserve credits\";\n logUsage({\n userId: user.id,\n operationType: options.operationType,\n creditsUsed: 0,\n success: false,\n errorMessage: message,\n resourceId: options.resourceId,\n resourceType: options.resourceType,\n requestId,\n });\n return { success: false, error: message };\n }\n\n // Execute action\n try {\n const result = await handler(user, data, reservation);\n\n if (result.success) {\n await commitReservationWithJournal(repository, user.id, reservation.id);\n logUsage({\n userId: user.id,\n operationType: options.operationType,\n creditsUsed: reservation.amount,\n success: true,\n resourceId: options.resourceId,\n resourceType: options.resourceType,\n requestId,\n });\n } else {\n await releaseReservationWithJournal(repository, user.id, reservation.id);\n logUsage({\n userId: user.id,\n operationType: options.operationType,\n creditsUsed: 0,\n success: false,\n errorMessage: result.error,\n resourceId: options.resourceId,\n resourceType: options.resourceType,\n requestId,\n });\n }\n\n return result;\n } catch (error) {\n const message = error instanceof Error ? error.message : \"An unexpected error occurred\";\n await releaseReservationWithJournal(repository, user.id, reservation.id);\n logUsage({\n userId: user.id,\n operationType: options.operationType,\n creditsUsed: 0,\n success: false,\n errorMessage: message,\n resourceId: options.resourceId,\n resourceType: options.resourceType,\n requestId,\n });\n console.error(\"Action error:\", error);\n return { success: false, error: message };\n }\n };\n },\n };\n}\n\nfunction isPreviewMode(data: unknown): boolean {\n return (\n typeof data === \"object\" &&\n data !== null &&\n \"preview\" in data &&\n (data as Record<string, unknown>).preview === true\n );\n}\n\nfunction createDummyReservation(userId: string, operationType: string): PortableReservation {\n return {\n id: \"preview-mode\",\n userId,\n amount: 0,\n operationType,\n status: \"released\",\n createdAt: new Date().toISOString(),\n expiresAt: new Date().toISOString(),\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/auth/index.ts
21
+ var auth_exports = {};
22
+ __export(auth_exports, {
23
+ ApiKeyCreditsProvider: () => ApiKeyCreditsProvider,
24
+ createApiKeyProvider: () => createApiKeyProvider,
25
+ verifyAdminApiKey: () => verifyAdminApiKey,
26
+ verifyBearerToken: () => verifyBearerToken
27
+ });
28
+ module.exports = __toCommonJS(auth_exports);
29
+
30
+ // src/auth/api-key.ts
31
+ function timingSafeEqual(a, b) {
32
+ if (!a || !b) {
33
+ return false;
34
+ }
35
+ const maxLength = Math.max(a.length, b.length);
36
+ const paddedA = a.padEnd(maxLength);
37
+ const paddedB = b.padEnd(maxLength);
38
+ let result = 0;
39
+ for (let i = 0; i < maxLength; i++) {
40
+ result |= paddedA.charCodeAt(i) ^ paddedB.charCodeAt(i);
41
+ }
42
+ return result === 0 && a.length === b.length;
43
+ }
44
+ function verifyBearerToken(header, secret) {
45
+ if (!header || !secret) {
46
+ return false;
47
+ }
48
+ const token = header.replace(/^Bearer\s+/i, "");
49
+ return timingSafeEqual(token, secret);
50
+ }
51
+ var ApiKeyCreditsProvider = class {
52
+ apiKey;
53
+ isValidated = false;
54
+ constructor(authHeader, apiKey) {
55
+ this.apiKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;
56
+ if (authHeader && this.apiKey) {
57
+ this.isValidated = verifyBearerToken(authHeader, this.apiKey);
58
+ }
59
+ }
60
+ async getCurrentUser() {
61
+ if (!this.isValidated) {
62
+ return null;
63
+ }
64
+ return {
65
+ id: "api-admin",
66
+ email: "api@admin.internal",
67
+ name: "API Admin"
68
+ };
69
+ }
70
+ async verifyAdminAccess() {
71
+ return this.isValidated;
72
+ }
73
+ };
74
+ function verifyAdminApiKey(authHeader, apiKey) {
75
+ if (!authHeader) {
76
+ return false;
77
+ }
78
+ const expectedKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;
79
+ if (!expectedKey) {
80
+ console.warn("CREDITS_ADMIN_API_KEY not configured");
81
+ return false;
82
+ }
83
+ return verifyBearerToken(authHeader, expectedKey);
84
+ }
85
+ function createApiKeyProvider(request, apiKey) {
86
+ const authHeader = request.headers.get("Authorization");
87
+ return new ApiKeyCreditsProvider(authHeader, apiKey);
88
+ }
89
+ // Annotate the CommonJS export names for ESM import in node:
90
+ 0 && (module.exports = {
91
+ ApiKeyCreditsProvider,
92
+ createApiKeyProvider,
93
+ verifyAdminApiKey,
94
+ verifyBearerToken
95
+ });
96
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/auth/index.ts","../../src/auth/api-key.ts"],"sourcesContent":["/**\n * Credits auth providers - barrel exports\n *\n * Provides authentication abstraction for credits system\n */\n\n// Types and interfaces\nexport type {\n ICreditsAuthProvider,\n CreditsAuthProviderFactory,\n CreditsUser,\n} from \"./types.js\";\n\n// API Key implementation (for admin routes)\nexport {\n ApiKeyCreditsProvider,\n verifyAdminApiKey,\n createApiKeyProvider,\n verifyBearerToken,\n} from \"./api-key.js\";\n","import type { ICreditsAuthProvider, CreditsUser } from \"./types.js\";\n\n/**\n * Timing-safe string comparison to prevent timing attacks\n */\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (!a || !b) {\n return false;\n }\n\n // Pad strings to same length to prevent length-based timing attacks\n const maxLength = Math.max(a.length, b.length);\n const paddedA = a.padEnd(maxLength);\n const paddedB = b.padEnd(maxLength);\n\n let result = 0;\n for (let i = 0; i < maxLength; i++) {\n result |= paddedA.charCodeAt(i) ^ paddedB.charCodeAt(i);\n }\n\n // Also check that lengths were originally equal\n return result === 0 && a.length === b.length;\n}\n\n/**\n * Verify a Bearer token from an Authorization header\n *\n * @param header - The Authorization header value (e.g., \"Bearer secret123\")\n * @param secret - The expected secret value\n * @returns True if token is valid\n */\nexport function verifyBearerToken(header: string | null, secret: string): boolean {\n if (!header || !secret) {\n return false;\n }\n\n // Extract token from \"Bearer {token}\" format\n const token = header.replace(/^Bearer\\s+/i, \"\");\n\n // Use timing-safe comparison\n return timingSafeEqual(token, secret);\n}\n\n/**\n * API Key auth provider for admin routes\n *\n * Uses Bearer token authentication for external API access\n * The API key is stored in CREDITS_ADMIN_API_KEY environment variable\n */\nexport class ApiKeyCreditsProvider implements ICreditsAuthProvider {\n private readonly apiKey: string | undefined;\n private isValidated = false;\n\n constructor(authHeader?: string | null, apiKey?: string) {\n this.apiKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;\n\n // Validate the provided auth header using timing-safe comparison\n if (authHeader && this.apiKey) {\n this.isValidated = verifyBearerToken(authHeader, this.apiKey);\n }\n }\n\n async getCurrentUser(): Promise<CreditsUser | null> {\n if (!this.isValidated) {\n return null;\n }\n\n // API key auth returns a special \"admin\" user\n return {\n id: \"api-admin\",\n email: \"api@admin.internal\",\n name: \"API Admin\",\n };\n }\n\n async verifyAdminAccess(): Promise<boolean> {\n return this.isValidated;\n }\n}\n\n/**\n * Verify an API key from a request header\n * @param authHeader - Authorization header value\n * @param apiKey - Optional API key to use (defaults to CREDITS_ADMIN_API_KEY env var)\n * @returns True if API key is valid\n */\nexport function verifyAdminApiKey(authHeader: string | null, apiKey?: string): boolean {\n if (!authHeader) {\n return false;\n }\n\n const expectedKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;\n if (!expectedKey) {\n console.warn(\"CREDITS_ADMIN_API_KEY not configured\");\n return false;\n }\n\n // Use timing-safe comparison to prevent timing attacks\n return verifyBearerToken(authHeader, expectedKey);\n}\n\n/**\n * Create an API key auth provider from a request\n * @param request - Request object with headers\n * @param apiKey - Optional API key to use\n * @returns API key auth provider\n */\nexport function createApiKeyProvider(request: Request, apiKey?: string): ApiKeyCreditsProvider {\n const authHeader = request.headers.get(\"Authorization\");\n return new ApiKeyCreditsProvider(authHeader, apiKey);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,SAAS,gBAAgB,GAAW,GAAoB;AACtD,MAAI,CAAC,KAAK,CAAC,GAAG;AACZ,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM;AAC7C,QAAM,UAAU,EAAE,OAAO,SAAS;AAClC,QAAM,UAAU,EAAE,OAAO,SAAS;AAElC,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,cAAU,QAAQ,WAAW,CAAC,IAAI,QAAQ,WAAW,CAAC;AAAA,EACxD;AAGA,SAAO,WAAW,KAAK,EAAE,WAAW,EAAE;AACxC;AASO,SAAS,kBAAkB,QAAuB,QAAyB;AAChF,MAAI,CAAC,UAAU,CAAC,QAAQ;AACtB,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,OAAO,QAAQ,eAAe,EAAE;AAG9C,SAAO,gBAAgB,OAAO,MAAM;AACtC;AAQO,IAAM,wBAAN,MAA4D;AAAA,EAChD;AAAA,EACT,cAAc;AAAA,EAEtB,YAAY,YAA4B,QAAiB;AACvD,SAAK,SAAS,UAAU,QAAQ,IAAI;AAGpC,QAAI,cAAc,KAAK,QAAQ;AAC7B,WAAK,cAAc,kBAAkB,YAAY,KAAK,MAAM;AAAA,IAC9D;AAAA,EACF;AAAA,EAEA,MAAM,iBAA8C;AAClD,QAAI,CAAC,KAAK,aAAa;AACrB,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,oBAAsC;AAC1C,WAAO,KAAK;AAAA,EACd;AACF;AAQO,SAAS,kBAAkB,YAA2B,QAA0B;AACrF,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,UAAU,QAAQ,IAAI;AAC1C,MAAI,CAAC,aAAa;AAChB,YAAQ,KAAK,sCAAsC;AACnD,WAAO;AAAA,EACT;AAGA,SAAO,kBAAkB,YAAY,WAAW;AAClD;AAQO,SAAS,qBAAqB,SAAkB,QAAwC;AAC7F,QAAM,aAAa,QAAQ,QAAQ,IAAI,eAAe;AACtD,SAAO,IAAI,sBAAsB,YAAY,MAAM;AACrD;","names":[]}
@@ -1,4 +1,6 @@
1
- import type { ICreditsAuthProvider, CreditsUser } from "./types.js";
1
+ import { I as ICreditsAuthProvider, a as CreditsUser } from '../types-OZU3I5OM.cjs';
2
+ export { C as CreditsAuthProviderFactory } from '../types-OZU3I5OM.cjs';
3
+
2
4
  /**
3
5
  * Verify a Bearer token from an Authorization header
4
6
  *
@@ -6,14 +8,14 @@ import type { ICreditsAuthProvider, CreditsUser } from "./types.js";
6
8
  * @param secret - The expected secret value
7
9
  * @returns True if token is valid
8
10
  */
9
- export declare function verifyBearerToken(header: string | null, secret: string): boolean;
11
+ declare function verifyBearerToken(header: string | null, secret: string): boolean;
10
12
  /**
11
13
  * API Key auth provider for admin routes
12
14
  *
13
15
  * Uses Bearer token authentication for external API access
14
16
  * The API key is stored in CREDITS_ADMIN_API_KEY environment variable
15
17
  */
16
- export declare class ApiKeyCreditsProvider implements ICreditsAuthProvider {
18
+ declare class ApiKeyCreditsProvider implements ICreditsAuthProvider {
17
19
  private readonly apiKey;
18
20
  private isValidated;
19
21
  constructor(authHeader?: string | null, apiKey?: string);
@@ -26,12 +28,13 @@ export declare class ApiKeyCreditsProvider implements ICreditsAuthProvider {
26
28
  * @param apiKey - Optional API key to use (defaults to CREDITS_ADMIN_API_KEY env var)
27
29
  * @returns True if API key is valid
28
30
  */
29
- export declare function verifyAdminApiKey(authHeader: string | null, apiKey?: string): boolean;
31
+ declare function verifyAdminApiKey(authHeader: string | null, apiKey?: string): boolean;
30
32
  /**
31
33
  * Create an API key auth provider from a request
32
34
  * @param request - Request object with headers
33
35
  * @param apiKey - Optional API key to use
34
36
  * @returns API key auth provider
35
37
  */
36
- export declare function createApiKeyProvider(request: Request, apiKey?: string): ApiKeyCreditsProvider;
37
- //# sourceMappingURL=api-key.d.ts.map
38
+ declare function createApiKeyProvider(request: Request, apiKey?: string): ApiKeyCreditsProvider;
39
+
40
+ export { ApiKeyCreditsProvider, CreditsUser, ICreditsAuthProvider, createApiKeyProvider, verifyAdminApiKey, verifyBearerToken };