@develemit/billing 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/dist/index.cjs ADDED
@@ -0,0 +1,625 @@
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/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ BillingApiError: () => BillingApiError,
24
+ checkEntitlement: () => checkEntitlement,
25
+ createClient: () => createClient
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // ../shared-types/src/admin.ts
30
+ var import_zod5 = require("zod");
31
+
32
+ // ../shared-types/src/event-log.ts
33
+ var import_zod = require("zod");
34
+ var eventStatusSchema = import_zod.z.enum([
35
+ "received",
36
+ "processed",
37
+ "failed",
38
+ "dead_letter",
39
+ "discarded"
40
+ ]);
41
+ var subscriptionStateSchema = import_zod.z.enum([
42
+ "trialing",
43
+ "active",
44
+ "past_due",
45
+ "canceled",
46
+ "unpaid"
47
+ ]);
48
+ var adminEventSummarySchema = import_zod.z.object({
49
+ id: import_zod.z.string(),
50
+ projectId: import_zod.z.string(),
51
+ providerEventId: import_zod.z.string(),
52
+ type: import_zod.z.string(),
53
+ status: eventStatusSchema,
54
+ attempts: import_zod.z.number(),
55
+ lastError: import_zod.z.string().nullable(),
56
+ errorSignature: import_zod.z.string().nullable(),
57
+ receivedAt: import_zod.z.iso.datetime(),
58
+ processedAt: import_zod.z.iso.datetime().nullable(),
59
+ // event_log has no subject/customer reference column (sprint 24's
60
+ // repo-drizzle-subjects.ts documents the same gap the other direction) —
61
+ // derived from the raw Stripe payload's well-known `data.object.customer`
62
+ // path, so this is null for any non-Stripe-shaped payload.
63
+ subjectRef: import_zod.z.string().nullable()
64
+ });
65
+ var adminEventDetailSchema = adminEventSummarySchema.extend({
66
+ payload: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()),
67
+ discardReason: import_zod.z.string().nullable(),
68
+ discardedBy: import_zod.z.string().nullable(),
69
+ discardedAt: import_zod.z.iso.datetime().nullable()
70
+ });
71
+ var signatureCountSchema = import_zod.z.object({
72
+ signature: import_zod.z.string(),
73
+ count: import_zod.z.number()
74
+ });
75
+ var listEventsResponseSchema = import_zod.z.object({
76
+ events: import_zod.z.array(adminEventSummarySchema),
77
+ total: import_zod.z.number(),
78
+ limit: import_zod.z.number(),
79
+ offset: import_zod.z.number(),
80
+ signatureCounts: import_zod.z.array(signatureCountSchema)
81
+ });
82
+ var wouldApplySchema = import_zod.z.discriminatedUnion("kind", [
83
+ import_zod.z.object({ kind: import_zod.z.literal("create"), to: subscriptionStateSchema }),
84
+ import_zod.z.object({
85
+ kind: import_zod.z.literal("transition"),
86
+ from: subscriptionStateSchema,
87
+ to: subscriptionStateSchema
88
+ }),
89
+ import_zod.z.object({ kind: import_zod.z.literal("stale") }),
90
+ import_zod.z.object({
91
+ kind: import_zod.z.literal("invalid"),
92
+ from: subscriptionStateSchema,
93
+ to: subscriptionStateSchema
94
+ }),
95
+ import_zod.z.object({ kind: import_zod.z.literal("unknown_subscription") }),
96
+ import_zod.z.object({ kind: import_zod.z.literal("noop"), reason: import_zod.z.string() }),
97
+ import_zod.z.object({ kind: import_zod.z.literal("error"), reason: import_zod.z.string() })
98
+ ]);
99
+ var dryRunReplayResponseSchema = import_zod.z.object({
100
+ event: adminEventDetailSchema,
101
+ current: import_zod.z.object({
102
+ state: subscriptionStateSchema,
103
+ lastEventAt: import_zod.z.iso.datetime().nullable()
104
+ }).nullable(),
105
+ wouldApply: wouldApplySchema
106
+ });
107
+ var replayOutcomeSchema = import_zod.z.enum([
108
+ "created",
109
+ "transitioned",
110
+ "stale",
111
+ "noop",
112
+ "invalid",
113
+ "unknown_subscription",
114
+ "already_processed",
115
+ "error"
116
+ ]);
117
+ var replayResponseSchema = import_zod.z.object({
118
+ outcome: replayOutcomeSchema
119
+ });
120
+ var discardEventRequestSchema = import_zod.z.object({
121
+ actor: import_zod.z.string().min(1),
122
+ reason: import_zod.z.string()
123
+ });
124
+ var replayGroupCursorSchema = import_zod.z.object({
125
+ receivedAt: import_zod.z.iso.datetime(),
126
+ id: import_zod.z.string()
127
+ });
128
+ var replayGroupRequestSchema = import_zod.z.object({
129
+ projectId: import_zod.z.string().min(1),
130
+ cursor: replayGroupCursorSchema.optional(),
131
+ limit: import_zod.z.number().int().positive().optional()
132
+ }).and(
133
+ import_zod.z.union([
134
+ import_zod.z.object({ errorSignature: import_zod.z.string().min(1) }),
135
+ import_zod.z.object({ all: import_zod.z.literal(true) })
136
+ ])
137
+ );
138
+ var replayGroupOutcomeEntrySchema = import_zod.z.object({
139
+ eventId: import_zod.z.string(),
140
+ outcome: replayOutcomeSchema
141
+ });
142
+ var replayGroupResponseSchema = import_zod.z.object({
143
+ outcomes: import_zod.z.array(replayGroupOutcomeEntrySchema),
144
+ // Keyed by ProcessOutcome, but zod v4's record needs .partial() semantics
145
+ // for an exhaustive enum key type, so this stays a plain string map — the
146
+ // set of possible keys is still exactly replayOutcomeSchema's members.
147
+ counts: import_zod.z.record(import_zod.z.string(), import_zod.z.number()),
148
+ // This call's batch (its own limit, or BULK_REPLAY_CAP by default) hit its
149
+ // ceiling: more matching events remain beyond what was just replayed.
150
+ // Paging with nextCursor continues; a non-paging caller reads this the
151
+ // same way it always has, as "replay again to finish draining it."
152
+ truncated: import_zod.z.boolean(),
153
+ nextCursor: replayGroupCursorSchema.nullable()
154
+ });
155
+
156
+ // ../shared-types/src/entitlements.ts
157
+ var import_zod3 = require("zod");
158
+
159
+ // ../shared-types/src/subscriptions.ts
160
+ var import_zod2 = require("zod");
161
+ var subscriptionStatusSchema = import_zod2.z.enum([
162
+ "trialing",
163
+ "active",
164
+ "past_due",
165
+ "grace",
166
+ "canceled",
167
+ "unpaid",
168
+ "none"
169
+ ]);
170
+ var activeSubscriptionViewSchema = import_zod2.z.object({
171
+ state: subscriptionStatusSchema.exclude(["none"]),
172
+ planKey: import_zod2.z.string(),
173
+ currentPeriodEnd: import_zod2.z.iso.datetime(),
174
+ cancelAtPeriodEnd: import_zod2.z.boolean()
175
+ });
176
+ var noSubscriptionViewSchema = import_zod2.z.object({
177
+ state: import_zod2.z.literal("none")
178
+ });
179
+ var subscriptionViewSchema = import_zod2.z.discriminatedUnion("state", [
180
+ activeSubscriptionViewSchema,
181
+ noSubscriptionViewSchema
182
+ ]);
183
+
184
+ // ../shared-types/src/entitlements.ts
185
+ var entitlementSourcesSchema = import_zod3.z.object({
186
+ features: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.enum(["subscription", "override"])),
187
+ limits: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.enum(["subscription", "override"]))
188
+ });
189
+ var entitlementsResponseSchema = import_zod3.z.object({
190
+ plan: import_zod3.z.string().nullable(),
191
+ features: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.boolean()),
192
+ limits: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.number()),
193
+ status: subscriptionStatusSchema,
194
+ source: import_zod3.z.enum(["subscription", "override", "none"]),
195
+ sources: entitlementSourcesSchema
196
+ });
197
+
198
+ // ../shared-types/src/overrides.ts
199
+ var import_zod4 = require("zod");
200
+ var overrideKindSchema = import_zod4.z.enum(["comp", "extend", "revoke"]);
201
+ var overrideTargetSchema = import_zod4.z.discriminatedUnion("type", [
202
+ import_zod4.z.object({ type: import_zod4.z.literal("feature"), feature: import_zod4.z.string().min(1) }),
203
+ import_zod4.z.object({
204
+ type: import_zod4.z.literal("limit"),
205
+ limit: import_zod4.z.string().min(1),
206
+ value: import_zod4.z.number()
207
+ })
208
+ ]);
209
+ var createOverrideRequestSchema = import_zod4.z.object({
210
+ subjectId: import_zod4.z.string().min(1),
211
+ kind: overrideKindSchema,
212
+ target: overrideTargetSchema,
213
+ actor: import_zod4.z.string().min(1),
214
+ reason: import_zod4.z.string().min(4),
215
+ expiresAt: import_zod4.z.iso.datetime().optional()
216
+ });
217
+ var createBulkOverrideRequestSchema = import_zod4.z.object({
218
+ subjectId: import_zod4.z.string().min(1),
219
+ kind: overrideKindSchema,
220
+ targets: import_zod4.z.array(overrideTargetSchema).min(1),
221
+ actor: import_zod4.z.string().min(1),
222
+ reason: import_zod4.z.string().min(4),
223
+ expiresAt: import_zod4.z.iso.datetime().optional()
224
+ });
225
+ var overrideRecordSchema = import_zod4.z.object({
226
+ id: import_zod4.z.string(),
227
+ subjectId: import_zod4.z.string(),
228
+ kind: overrideKindSchema,
229
+ target: overrideTargetSchema,
230
+ actor: import_zod4.z.string(),
231
+ reason: import_zod4.z.string(),
232
+ expiresAt: import_zod4.z.iso.datetime().nullable(),
233
+ createdAt: import_zod4.z.iso.datetime()
234
+ });
235
+ var bulkOverrideResponseSchema = import_zod4.z.object({
236
+ overrides: import_zod4.z.array(overrideRecordSchema)
237
+ });
238
+ var listOverridesResponseSchema = import_zod4.z.object({
239
+ overrides: import_zod4.z.array(overrideRecordSchema),
240
+ total: import_zod4.z.number(),
241
+ limit: import_zod4.z.number(),
242
+ offset: import_zod4.z.number()
243
+ });
244
+
245
+ // ../shared-types/src/admin.ts
246
+ var stateCountsSchema = import_zod5.z.object({
247
+ trialing: import_zod5.z.number(),
248
+ active: import_zod5.z.number(),
249
+ past_due: import_zod5.z.number(),
250
+ grace: import_zod5.z.number(),
251
+ canceled: import_zod5.z.number(),
252
+ unpaid: import_zod5.z.number()
253
+ });
254
+ var dashboardMetricsSchema = import_zod5.z.object({
255
+ mrrCents: import_zod5.z.number(),
256
+ mrrPrevCents: import_zod5.z.number(),
257
+ subjectsCount: import_zod5.z.number(),
258
+ states: stateCountsSchema,
259
+ deadLetterDepth: import_zod5.z.number(),
260
+ oldestUnprocessedSec: import_zod5.z.number().nullable(),
261
+ driftCount: import_zod5.z.number()
262
+ });
263
+ var projectMetricsSchema = dashboardMetricsSchema.extend({
264
+ projectId: import_zod5.z.string(),
265
+ projectKey: import_zod5.z.string(),
266
+ projectName: import_zod5.z.string(),
267
+ subjectType: import_zod5.z.string(),
268
+ environment: import_zod5.z.enum(["test", "live"]),
269
+ envLocked: import_zod5.z.boolean(),
270
+ mrrSeries: import_zod5.z.array(import_zod5.z.number())
271
+ });
272
+ var adminMetricsResponseSchema = import_zod5.z.object({
273
+ ecosystem: dashboardMetricsSchema,
274
+ // Shared x-axis labels for every project's mrrSeries (same calendar
275
+ // months across the ecosystem) — empty when no project has snapshots yet.
276
+ monthLabels: import_zod5.z.array(import_zod5.z.string()),
277
+ projects: import_zod5.z.array(projectMetricsSchema)
278
+ });
279
+ var adminEventsListResponseSchema = import_zod5.z.object({
280
+ events: import_zod5.z.array(adminEventSummarySchema),
281
+ nextCursor: import_zod5.z.string().nullable(),
282
+ approxTotal: import_zod5.z.number(),
283
+ signatureCounts: import_zod5.z.array(signatureCountSchema).optional()
284
+ });
285
+ var adminEventDetailResponseSchema = import_zod5.z.object({
286
+ event: adminEventDetailSchema,
287
+ transition: import_zod5.z.object({
288
+ current: import_zod5.z.object({
289
+ state: subscriptionStatusSchema.exclude(["grace", "none"]),
290
+ lastEventAt: import_zod5.z.iso.datetime().nullable()
291
+ }).nullable(),
292
+ wouldApply: wouldApplySchema
293
+ })
294
+ });
295
+ var adminSubjectSchema = import_zod5.z.object({
296
+ id: import_zod5.z.string(),
297
+ subjectType: import_zod5.z.string(),
298
+ externalId: import_zod5.z.string(),
299
+ providerCustomerId: import_zod5.z.string().nullable()
300
+ });
301
+ var featureGrantsSchema = import_zod5.z.record(
302
+ import_zod5.z.string(),
303
+ import_zod5.z.union([import_zod5.z.boolean(), import_zod5.z.number()])
304
+ );
305
+ var adminSubjectSubscriptionPlanSchema = import_zod5.z.object({
306
+ key: import_zod5.z.string(),
307
+ featureGrants: featureGrantsSchema,
308
+ priceInterval: import_zod5.z.enum(["month", "year"]).nullable()
309
+ });
310
+ var adminSubjectLookupResponseSchema = import_zod5.z.object({
311
+ subject: adminSubjectSchema,
312
+ entitlements: entitlementsResponseSchema,
313
+ recentEvents: import_zod5.z.array(adminEventSummarySchema),
314
+ activeOverrides: import_zod5.z.array(overrideRecordSchema),
315
+ subscriptionPlan: adminSubjectSubscriptionPlanSchema.nullable()
316
+ });
317
+ var adminPriceSchema = import_zod5.z.object({
318
+ id: import_zod5.z.string(),
319
+ interval: import_zod5.z.enum(["month", "year"]),
320
+ currency: import_zod5.z.string(),
321
+ unitAmountCents: import_zod5.z.number(),
322
+ syncStatus: import_zod5.z.enum(["synced", "pending", "drifted"]),
323
+ syncedUnitAmountCents: import_zod5.z.number().nullable()
324
+ });
325
+ var adminPlanSchema = import_zod5.z.object({
326
+ id: import_zod5.z.string(),
327
+ key: import_zod5.z.string(),
328
+ name: import_zod5.z.string(),
329
+ featureGrants: featureGrantsSchema,
330
+ activeSubscriptions: import_zod5.z.number(),
331
+ prices: import_zod5.z.array(adminPriceSchema)
332
+ });
333
+ var adminPlansResponseSchema = import_zod5.z.object({
334
+ project: import_zod5.z.object({
335
+ statementDescriptor: import_zod5.z.string().nullable(),
336
+ statementDescriptorPrefix: import_zod5.z.string().nullable()
337
+ }),
338
+ plans: import_zod5.z.array(adminPlanSchema)
339
+ });
340
+ var adminProviderResponseSchema = import_zod5.z.object({
341
+ projectId: import_zod5.z.string(),
342
+ providerKind: import_zod5.z.string(),
343
+ environment: import_zod5.z.enum(["test", "live"]),
344
+ envLocked: import_zod5.z.boolean(),
345
+ secretKeyMasked: import_zod5.z.string().nullable(),
346
+ publishableKeyMasked: import_zod5.z.string().nullable(),
347
+ webhookEndpoint: import_zod5.z.string(),
348
+ webhookSigningConfigured: import_zod5.z.boolean(),
349
+ defaultPortalReturnUrl: import_zod5.z.string().nullable()
350
+ });
351
+ var adminAuditEntrySchema = overrideRecordSchema.extend({
352
+ projectId: import_zod5.z.string()
353
+ });
354
+ var adminAuditResponseSchema = import_zod5.z.object({
355
+ entries: import_zod5.z.array(adminAuditEntrySchema),
356
+ nextCursor: import_zod5.z.string().nullable(),
357
+ approxTotal: import_zod5.z.number()
358
+ });
359
+ var adminThroughputResponseSchema = import_zod5.z.object({
360
+ days: import_zod5.z.array(import_zod5.z.string()),
361
+ processed: import_zod5.z.array(import_zod5.z.number()),
362
+ failed: import_zod5.z.array(import_zod5.z.number())
363
+ });
364
+ var adminPlanMixResponseSchema = import_zod5.z.object({
365
+ months: import_zod5.z.array(import_zod5.z.string()),
366
+ plans: import_zod5.z.array(import_zod5.z.object({ key: import_zod5.z.string(), name: import_zod5.z.string() })),
367
+ stacks: import_zod5.z.array(import_zod5.z.array(import_zod5.z.number()))
368
+ });
369
+ var adminDeadLetterGroupSchema = import_zod5.z.object({
370
+ projectId: import_zod5.z.string(),
371
+ signature: import_zod5.z.string(),
372
+ count: import_zod5.z.number(),
373
+ sampleEventId: import_zod5.z.string(),
374
+ firstReceivedAt: import_zod5.z.iso.datetime(),
375
+ lastReceivedAt: import_zod5.z.iso.datetime()
376
+ });
377
+ var adminDeadLetterGroupsResponseSchema = import_zod5.z.object({
378
+ groups: import_zod5.z.array(adminDeadLetterGroupSchema)
379
+ });
380
+ var adminProjectDashboardResponseSchema = import_zod5.z.object({
381
+ metrics: projectMetricsSchema,
382
+ monthLabels: import_zod5.z.array(import_zod5.z.string()),
383
+ providerKind: import_zod5.z.string(),
384
+ plans: import_zod5.z.array(adminPlanSchema)
385
+ });
386
+
387
+ // ../shared-types/src/checkout.ts
388
+ var import_zod6 = require("zod");
389
+ var checkoutRequestSchema = import_zod6.z.object({
390
+ planKey: import_zod6.z.string(),
391
+ subjectId: import_zod6.z.string(),
392
+ successUrl: import_zod6.z.url(),
393
+ cancelUrl: import_zod6.z.url(),
394
+ email: import_zod6.z.email().optional()
395
+ });
396
+ var checkoutResponseSchema = import_zod6.z.object({
397
+ url: import_zod6.z.url()
398
+ });
399
+ var portalRequestSchema = import_zod6.z.object({
400
+ subjectId: import_zod6.z.string(),
401
+ returnUrl: import_zod6.z.url()
402
+ });
403
+ var portalResponseSchema = import_zod6.z.object({
404
+ url: import_zod6.z.url()
405
+ });
406
+
407
+ // ../shared-types/src/projects.ts
408
+ var import_zod7 = require("zod");
409
+ var registerProjectRequestSchema = import_zod7.z.object({
410
+ key: import_zod7.z.string().trim().min(1, "is required"),
411
+ name: import_zod7.z.string().trim().min(1, "is required"),
412
+ subjectType: import_zod7.z.string().trim().min(1, "is required"),
413
+ environment: import_zod7.z.enum(["test", "live"], 'must be "test" or "live"'),
414
+ envLocked: import_zod7.z.boolean().optional(),
415
+ providerKind: import_zod7.z.string().trim().min(1, "is required"),
416
+ providerSecretKeyRef: import_zod7.z.string().optional(),
417
+ providerPublishableKeyRef: import_zod7.z.string().optional(),
418
+ providerPortalConfigRef: import_zod7.z.string().optional(),
419
+ statementDescriptor: import_zod7.z.string().optional(),
420
+ statementDescriptorPrefix: import_zod7.z.string().optional(),
421
+ graceDays: import_zod7.z.number().min(0, "must be >= 0").optional(),
422
+ defaultPortalReturnUrl: import_zod7.z.url().optional()
423
+ }).superRefine((input, ctx) => {
424
+ if (!input.statementDescriptor && !input.statementDescriptorPrefix) {
425
+ ctx.addIssue({
426
+ code: "custom",
427
+ path: ["statementDescriptor"],
428
+ message: "statementDescriptor or statementDescriptorPrefix is required"
429
+ });
430
+ }
431
+ });
432
+
433
+ // ../shared-types/src/errors.ts
434
+ var import_zod8 = require("zod");
435
+ var errorEnvelopeSchema = import_zod8.z.object({
436
+ error: import_zod8.z.object({
437
+ code: import_zod8.z.string(),
438
+ message: import_zod8.z.string()
439
+ })
440
+ });
441
+
442
+ // ../shared-types/src/webhook-secret.ts
443
+ var import_zod9 = require("zod");
444
+ var webhookSigningSecretSchema = import_zod9.z.string().regex(/^whsec_.+/, "must be a non-empty whsec_-prefixed string");
445
+
446
+ // ../shared-types/src/portal-return-url.ts
447
+ var import_zod10 = require("zod");
448
+ var portalReturnUrlSchema = import_zod10.z.url();
449
+
450
+ // src/cache.ts
451
+ var TtlCache = class {
452
+ constructor(now = Date.now) {
453
+ this.now = now;
454
+ }
455
+ now;
456
+ store = /* @__PURE__ */ new Map();
457
+ get(key) {
458
+ return this.store.get(key);
459
+ }
460
+ set(key, value) {
461
+ this.store.set(key, { value, fetchedAt: this.now() });
462
+ }
463
+ getFresh(key, ttlMs) {
464
+ const entry = this.store.get(key);
465
+ if (!entry) return void 0;
466
+ return this.now() - entry.fetchedAt < ttlMs ? entry.value : void 0;
467
+ }
468
+ getStale(key) {
469
+ return this.store.get(key)?.value;
470
+ }
471
+ };
472
+
473
+ // src/errors.ts
474
+ var BillingApiError = class extends Error {
475
+ status;
476
+ code;
477
+ constructor(status, code, message) {
478
+ super(message);
479
+ this.name = "BillingApiError";
480
+ this.status = status;
481
+ this.code = code;
482
+ }
483
+ };
484
+ async function errorFromResponse(response) {
485
+ let code = "unknown_error";
486
+ let message = `Request failed with status ${response.status}`;
487
+ try {
488
+ const body = await response.json();
489
+ const parsed = errorEnvelopeSchema.safeParse(body);
490
+ if (parsed.success) {
491
+ code = parsed.data.error.code;
492
+ message = parsed.data.error.message;
493
+ }
494
+ } catch {
495
+ }
496
+ return new BillingApiError(response.status, code, message);
497
+ }
498
+ function networkError(cause) {
499
+ const message = cause instanceof Error ? cause.message : "Network request failed";
500
+ return new BillingApiError(0, "network_error", message);
501
+ }
502
+ function invalidResponseError(message) {
503
+ return new BillingApiError(502, "invalid_response", message);
504
+ }
505
+
506
+ // src/client.ts
507
+ var DEFAULT_ENTITLEMENTS_TTL_MS = 5e3;
508
+ async function requestJson(fetchImpl, url, apiKey, init) {
509
+ let response;
510
+ try {
511
+ response = await fetchImpl(url, {
512
+ ...init,
513
+ headers: {
514
+ ...init?.headers,
515
+ Authorization: `Bearer ${apiKey}`,
516
+ ...init?.body ? { "Content-Type": "application/json" } : {}
517
+ }
518
+ });
519
+ } catch (cause) {
520
+ throw networkError(cause);
521
+ }
522
+ if (!response.ok) {
523
+ throw await errorFromResponse(response);
524
+ }
525
+ return response.json();
526
+ }
527
+ function parseOrThrow(schema, data) {
528
+ const parsed = schema.safeParse(data);
529
+ if (!parsed.success) {
530
+ throw invalidResponseError(
531
+ `Response failed validation: ${parsed.error.message}`
532
+ );
533
+ }
534
+ return parsed.data;
535
+ }
536
+ function createClient(options) {
537
+ const { apiKey, baseUrl } = options;
538
+ const fetchImpl = options.fetch ?? fetch;
539
+ const entitlementsTtlMs = options.entitlementsTtlMs ?? DEFAULT_ENTITLEMENTS_TTL_MS;
540
+ const entitlementsCache = new TtlCache();
541
+ async function getEntitlements(subjectId) {
542
+ const fresh = entitlementsCache.getFresh(subjectId, entitlementsTtlMs);
543
+ if (fresh) return fresh;
544
+ try {
545
+ const data = await requestJson(
546
+ fetchImpl,
547
+ `${baseUrl}/v1/entitlements/${encodeURIComponent(subjectId)}`,
548
+ apiKey
549
+ );
550
+ const parsed = parseOrThrow(entitlementsResponseSchema, data);
551
+ entitlementsCache.set(subjectId, parsed);
552
+ return parsed;
553
+ } catch (err) {
554
+ const isClientError = err instanceof BillingApiError && err.status >= 400 && err.status < 500;
555
+ if (isClientError) throw err;
556
+ const stale = entitlementsCache.getStale(subjectId);
557
+ if (stale) return { ...stale, stale: true };
558
+ throw err;
559
+ }
560
+ }
561
+ async function createCheckout(input) {
562
+ const data = await requestJson(
563
+ fetchImpl,
564
+ `${baseUrl}/v1/checkout`,
565
+ apiKey,
566
+ {
567
+ method: "POST",
568
+ body: JSON.stringify(input)
569
+ }
570
+ );
571
+ return parseOrThrow(checkoutResponseSchema, data);
572
+ }
573
+ async function portalUrl(input) {
574
+ const data = await requestJson(fetchImpl, `${baseUrl}/v1/portal`, apiKey, {
575
+ method: "POST",
576
+ body: JSON.stringify(input)
577
+ });
578
+ return parseOrThrow(portalResponseSchema, data);
579
+ }
580
+ async function getSubscription(subjectId) {
581
+ const data = await requestJson(
582
+ fetchImpl,
583
+ `${baseUrl}/v1/subscriptions/${encodeURIComponent(subjectId)}`,
584
+ apiKey
585
+ );
586
+ return parseOrThrow(subscriptionViewSchema, data);
587
+ }
588
+ return { getEntitlements, createCheckout, portalUrl, getSubscription };
589
+ }
590
+
591
+ // src/guard.ts
592
+ function isMisconfiguration(err) {
593
+ return err instanceof BillingApiError && err.status >= 400 && err.status < 500;
594
+ }
595
+ function decideOnContent(entitlements, feature, limit) {
596
+ if (feature !== void 0) {
597
+ return entitlements.features[feature] ? { decision: "allow", entitlements } : { decision: "deny", reason: `Feature "${feature}" is not entitled` };
598
+ }
599
+ if (limit !== void 0) {
600
+ const max = entitlements.limits[limit.key];
601
+ return max !== void 0 && limit.usage < max ? { decision: "allow", entitlements } : { decision: "deny", reason: `Limit "${limit.key}" has been reached` };
602
+ }
603
+ return { decision: "allow", entitlements };
604
+ }
605
+ async function checkEntitlement(input) {
606
+ const { client, subjectId, feature, limit, onUnavailable } = input;
607
+ let entitlements;
608
+ try {
609
+ entitlements = await client.getEntitlements(subjectId);
610
+ } catch (err) {
611
+ if (isMisconfiguration(err)) {
612
+ const message = err instanceof Error ? err.message : "Billing request was rejected";
613
+ return { decision: "deny", reason: message, misconfigured: true };
614
+ }
615
+ return onUnavailable === "open" ? { decision: "unavailable-allow" } : { decision: "unavailable-deny", reason: "Billing is unavailable" };
616
+ }
617
+ return decideOnContent(entitlements, feature, limit);
618
+ }
619
+ // Annotate the CommonJS export names for ESM import in node:
620
+ 0 && (module.exports = {
621
+ BillingApiError,
622
+ checkEntitlement,
623
+ createClient
624
+ });
625
+ //# sourceMappingURL=index.cjs.map