@palbase/backend 10.1.0 → 10.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.
package/dist/index.cjs CHANGED
@@ -49,6 +49,7 @@ __export(src_exports, {
49
49
  Patch: () => Patch,
50
50
  PolicyBuilder: () => PolicyBuilder,
51
51
  Post: () => Post,
52
+ Purchases: () => Purchases,
52
53
  Put: () => Put,
53
54
  Query: () => Query,
54
55
  QueryParams: () => QueryParams,
@@ -57,8 +58,10 @@ __export(src_exports, {
57
58
  Realtime: () => Realtime,
58
59
  Req: () => Req,
59
60
  RequestId: () => RequestId,
61
+ RequireEntitlement: () => RequireEntitlement,
60
62
  Resource: () => Resource,
61
63
  STORAGE_CONFIG_KIND: () => STORAGE_CONFIG_KIND,
64
+ Spend: () => Spend,
62
65
  Storage: () => Storage,
63
66
  TEST_USERS_CONFIG_KIND: () => TEST_USERS_CONFIG_KIND,
64
67
  TooManyRequests: () => TooManyRequests,
@@ -91,6 +94,7 @@ __export(src_exports, {
91
94
  defineWebhook: () => defineWebhook,
92
95
  defineWorker: () => defineWorker,
93
96
  documents: () => documents,
97
+ entitlementFor: () => entitlementFor,
94
98
  enumType: () => enumType,
95
99
  flag: () => flag,
96
100
  getErrorRegistry: () => getErrorRegistry,
@@ -99,6 +103,7 @@ __export(src_exports, {
99
103
  isPalbaseExtension: () => isPalbaseExtension,
100
104
  jsonb: () => jsonb,
101
105
  makeEnvDts: () => makeEnvDts,
106
+ makePurchasesDts: () => makePurchasesDts,
102
107
  makeTypedDB: () => makeTypedDB,
103
108
  numeric: () => numeric,
104
109
  parseFileSizeLimit: () => parseFileSizeLimit,
@@ -106,6 +111,7 @@ __export(src_exports, {
106
111
  raw: () => raw,
107
112
  recordThrows: () => recordThrows,
108
113
  reservedSecretKey: () => reservedSecretKey,
114
+ spendFor: () => spendFor,
109
115
  storage: () => storage,
110
116
  testUser: () => testUser,
111
117
  text: () => text,
@@ -200,6 +206,7 @@ var Cache = makeServiceProxy("Cache");
200
206
  var Queue = makeServiceProxy("Queue");
201
207
  var Log = makeServiceProxy("Log");
202
208
  var Notifications = makeServiceProxy("Notifications");
209
+ var Purchases = makeServiceProxy("Purchases");
203
210
  var rawFlags = makeServiceProxy("Flags");
204
211
  var Flags = Object.assign(
205
212
  {
@@ -230,6 +237,276 @@ var Flags = Object.assign(
230
237
  );
231
238
  var Realtime = makeServiceProxy("Realtime");
232
239
 
240
+ // src/errors.ts
241
+ var HttpError = class extends Error {
242
+ status;
243
+ error;
244
+ errorDescription;
245
+ data;
246
+ constructor(status, error, errorDescription, data) {
247
+ super(errorDescription);
248
+ this.name = "HttpError";
249
+ this.status = status;
250
+ this.error = error;
251
+ this.errorDescription = errorDescription;
252
+ if (data !== void 0) {
253
+ this.data = data;
254
+ }
255
+ }
256
+ /**
257
+ * Serialize to the standard Palbase error response format.
258
+ * The `requestId` is injected by the runtime layer from the request context.
259
+ * When called without arguments (e.g. JSON.stringify), request_id is omitted.
260
+ * When `data` is set, it is appended as a strict-superset field.
261
+ */
262
+ toJSON(requestId) {
263
+ const result = {
264
+ error: this.error,
265
+ error_description: this.errorDescription,
266
+ status: this.status
267
+ };
268
+ if (requestId) {
269
+ result.request_id = requestId;
270
+ }
271
+ if (this.data !== void 0) {
272
+ result.data = this.data;
273
+ }
274
+ return result;
275
+ }
276
+ };
277
+ var PalError = class extends HttpError {
278
+ constructor(status, code, description, data) {
279
+ super(status, code, description, data);
280
+ this.name = "PalError";
281
+ }
282
+ };
283
+ var NamedHttpError = class extends HttpError {
284
+ constructor(status, defaultCode, name, message, code, data) {
285
+ super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
286
+ this.name = name;
287
+ }
288
+ };
289
+ function defaultMessage(name) {
290
+ const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
291
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
292
+ }
293
+ var BadRequest = class extends NamedHttpError {
294
+ constructor(data, message) {
295
+ super(400, "bad_request", "BadRequest", message, void 0, data);
296
+ }
297
+ };
298
+ var Unauthorized = class extends NamedHttpError {
299
+ constructor(message, code, data) {
300
+ super(401, "unauthorized", "Unauthorized", message, code, data);
301
+ }
302
+ };
303
+ var Forbidden = class extends NamedHttpError {
304
+ constructor(message, code, data) {
305
+ super(403, "forbidden", "Forbidden", message, code, data);
306
+ }
307
+ };
308
+ var NotFound = class extends NamedHttpError {
309
+ constructor(message, code, data) {
310
+ super(404, "not_found", "NotFound", message, code, data);
311
+ }
312
+ };
313
+ var Conflict = class extends NamedHttpError {
314
+ constructor(message, code, data) {
315
+ super(409, "conflict", "Conflict", message, code, data);
316
+ }
317
+ };
318
+ var TooManyRequests = class extends NamedHttpError {
319
+ constructor(data, message) {
320
+ super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
321
+ }
322
+ };
323
+
324
+ // src/purchases/errors.ts
325
+ function asPalstoreError(err) {
326
+ if (!(err instanceof Error)) return null;
327
+ const e = err;
328
+ if (typeof e.code !== "string" || typeof e.status !== "number") return null;
329
+ return { code: e.code, status: e.status, message: e.message };
330
+ }
331
+ function toHttpError(err) {
332
+ if (err instanceof HttpError) return err;
333
+ const p = asPalstoreError(err);
334
+ if (!p) return err;
335
+ switch (p.code) {
336
+ case "entitlement_required": {
337
+ const entitlement = err.entitlement ?? null;
338
+ return new PalError(403, "entitlement_required", p.message, { entitlement });
339
+ }
340
+ case "quota_exceeded":
341
+ case "credit_insufficient": {
342
+ const state = err.state;
343
+ return new PalError(429, p.code, p.message, state);
344
+ }
345
+ default:
346
+ return err;
347
+ }
348
+ }
349
+
350
+ // src/purchases/registry.ts
351
+ var ENTITLEMENTS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.entitlements");
352
+ var SPENDS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.spends");
353
+ function carrierOf(target) {
354
+ return typeof target === "function" ? target : target.constructor ?? target;
355
+ }
356
+ function own(carrier, slot) {
357
+ if (!Object.prototype.hasOwnProperty.call(carrier, slot)) {
358
+ carrier[slot] = {};
359
+ }
360
+ return carrier[slot];
361
+ }
362
+ function recordEntitlement(target, fnName, key) {
363
+ own(carrierOf(target), ENTITLEMENTS)[fnName] = key;
364
+ }
365
+ function recordSpend(target, fnName, meta) {
366
+ own(carrierOf(target), SPENDS)[fnName] = meta;
367
+ }
368
+ function entitlementFor(ctor, fnName) {
369
+ return carrierOf(ctor)[ENTITLEMENTS]?.[fnName];
370
+ }
371
+ function spendFor(ctor, fnName) {
372
+ return carrierOf(ctor)[SPENDS]?.[fnName];
373
+ }
374
+
375
+ // src/purchases/subject.ts
376
+ var subjectByRequest = /* @__PURE__ */ new WeakMap();
377
+ function storeEnv() {
378
+ return process.env.PALSTORE_STORE_ENV === "sandbox" ? "sandbox" : "production";
379
+ }
380
+ async function currentSubjectId() {
381
+ const store = __requestALS.getStore();
382
+ if (!store) {
383
+ throw new Error(
384
+ "Purchases decorators used outside a request scope. @RequireEntitlement/@Spend resolve the subject from the request's authenticated user, so they only work inside an endpoint handler."
385
+ );
386
+ }
387
+ const memoized = subjectByRequest.get(store);
388
+ if (memoized) return memoized;
389
+ const userId = store.userId;
390
+ if (!userId) {
391
+ throw new Unauthorized(
392
+ "This endpoint requires an authenticated user: purchases entitlements and quota are per-user, and the subject is resolved from the request's identity."
393
+ );
394
+ }
395
+ const resolving = Purchases.resolveSubject({ userRef: userId, storeEnv: storeEnv() }).then(
396
+ (r) => r.subjectId
397
+ );
398
+ subjectByRequest.set(store, resolving);
399
+ resolving.catch(() => subjectByRequest.delete(store));
400
+ return resolving;
401
+ }
402
+
403
+ // src/purchases/decorators.ts
404
+ var WRAPPED = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.wrapped");
405
+ function idempotencyKeyFor(key) {
406
+ const store = __requestALS.getStore();
407
+ const base = store?.idempotencyKey || store?.requestId;
408
+ if (!base) {
409
+ throw new Error(
410
+ "@Spend needs a request-scoped idempotency key, and the runtime supplied neither an Idempotency-Key header nor a request id. Spending without one would let a retried request charge the user twice."
411
+ );
412
+ }
413
+ return `${base}:${key}`;
414
+ }
415
+ function ensureWrapper(target, fnName, descriptor) {
416
+ const original = descriptor.value;
417
+ if (typeof original !== "function") {
418
+ throw new Error(
419
+ `@RequireEntitlement/@Spend can only decorate a method; "${fnName}" is not one.`
420
+ );
421
+ }
422
+ if (original[WRAPPED]) return;
423
+ const wrapper = async function(...args) {
424
+ const ctor = this.constructor;
425
+ const entitlement = entitlementFor(ctor, fnName);
426
+ const spend = spendFor(ctor, fnName);
427
+ let subjectId;
428
+ try {
429
+ subjectId = await currentSubjectId();
430
+ } catch (err) {
431
+ throw toHttpError(err);
432
+ }
433
+ if (entitlement !== void 0) {
434
+ try {
435
+ await Purchases.require(subjectId, entitlement);
436
+ } catch (err) {
437
+ throw toHttpError(err);
438
+ }
439
+ }
440
+ if (spend === void 0) {
441
+ return original.apply(this, args);
442
+ }
443
+ let handlerFailure;
444
+ try {
445
+ return await Purchases.withSpend(
446
+ subjectId,
447
+ spend.key,
448
+ { count: spend.count, idempotencyKey: idempotencyKeyFor(spend.key) },
449
+ async () => {
450
+ try {
451
+ return await original.apply(this, args);
452
+ } catch (err) {
453
+ handlerFailure = { err };
454
+ throw err;
455
+ }
456
+ }
457
+ );
458
+ } catch (err) {
459
+ if (handlerFailure !== void 0 && handlerFailure.err === err) throw err;
460
+ throw toHttpError(err);
461
+ }
462
+ };
463
+ wrapper[WRAPPED] = true;
464
+ descriptor.value = wrapper;
465
+ }
466
+ function RequireEntitlement(key) {
467
+ return function(target, propertyKey, descriptor) {
468
+ const fnName = String(propertyKey);
469
+ recordEntitlement(target, fnName, key);
470
+ ensureWrapper(target, fnName, descriptor);
471
+ };
472
+ }
473
+ function Spend(key) {
474
+ return function(target, propertyKey, descriptor) {
475
+ const fnName = String(propertyKey);
476
+ recordSpend(target, fnName, { key, count: 1 });
477
+ ensureWrapper(target, fnName, descriptor);
478
+ };
479
+ }
480
+
481
+ // src/purchases/keys-gen.ts
482
+ function liveKeys(entries) {
483
+ return Object.entries(entries ?? {}).filter(([, def]) => def?.removed !== true).map(([key]) => key).sort();
484
+ }
485
+ function memberName(key) {
486
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
487
+ }
488
+ function members(keys) {
489
+ if (keys.length === 0) return "";
490
+ return `
491
+ ${keys.map((k) => ` ${memberName(k)}: true;`).join("\n")}
492
+ `;
493
+ }
494
+ function makePurchasesDts(manifest) {
495
+ const entitlements = liveKeys(manifest.entitlements);
496
+ const limits = liveKeys({ ...manifest.limits, ...manifest.credits });
497
+ return `// palbase-purchases.d.ts \u2014 GENERATED by @palbase/backend. Do not edit.
498
+ // Source: the project's palstore catalog revision.
499
+
500
+ declare module "@palbase/backend/purchases" {
501
+ interface Entitlements {${members(entitlements)}}
502
+
503
+ interface Limits {${members(limits)}}
504
+ }
505
+
506
+ export {};
507
+ `;
508
+ }
509
+
233
510
  // src/db/policy.ts
234
511
  var PolicyBuilder = class {
235
512
  _def;
@@ -631,7 +908,7 @@ function makeEnvDts(schema) {
631
908
  ${blocks.join("\n")}
632
909
  ` : "";
633
910
  return `// AUTO-GENERATED by @palbase/backend \u2014 DO NOT EDIT.
634
- // Regenerated from db/schema.ts on every \`palbase serve\` / deploy.
911
+ // Regenerated from db/schema.ts on every \`palbase db types\` / deploy.
635
912
  // Augments the @palbase/backend/env \`Tables\` interface so \`Database.tables.*\`
636
913
  // is typed with no import and no generic.
637
914
 
@@ -977,6 +1254,8 @@ function valueMatchesType(type, value) {
977
1254
  return typeof value === "number" && Number.isFinite(value);
978
1255
  case "string":
979
1256
  return typeof value === "string";
1257
+ case "json":
1258
+ return typeof value === "object" && value !== null && !Array.isArray(value);
980
1259
  }
981
1260
  }
982
1261
  function flag(opts) {
@@ -984,8 +1263,8 @@ function flag(opts) {
984
1263
  throw new Error("flag() expects { type, default, variants?, description? }");
985
1264
  }
986
1265
  const { type } = opts;
987
- if (type !== "boolean" && type !== "number" && type !== "string") {
988
- throw new Error(`flag type must be "boolean", "number", or "string", got ${JSON.stringify(type)}`);
1266
+ if (type !== "boolean" && type !== "number" && type !== "string" && type !== "json") {
1267
+ throw new Error(`flag type must be "boolean", "number", "string", or "json", got ${JSON.stringify(type)}`);
989
1268
  }
990
1269
  if (!valueMatchesType(type, opts.default)) {
991
1270
  throw new Error(
@@ -994,7 +1273,7 @@ function flag(opts) {
994
1273
  }
995
1274
  let variants = null;
996
1275
  if (opts.variants !== void 0) {
997
- if (type !== "string") {
1276
+ if (opts.type !== "string") {
998
1277
  throw new Error(`flag variants are only valid for type "string" (got type "${type}")`);
999
1278
  }
1000
1279
  if (!Array.isArray(opts.variants)) {
@@ -1079,7 +1358,7 @@ var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
1079
1358
  var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
1080
1359
  var RETURN_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.returnBuffer");
1081
1360
  var THROWS_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.throwsBuffer");
1082
- function carrierOf(target) {
1361
+ function carrierOf2(target) {
1083
1362
  const ctor = typeof target === "function" ? target : target.constructor ?? target;
1084
1363
  return ctor;
1085
1364
  }
@@ -1096,7 +1375,7 @@ function ownParamBuffer(carrier) {
1096
1375
  return carrier[PARAM_BUFFER];
1097
1376
  }
1098
1377
  function recordRoute(target, fnName, method, subpath, options) {
1099
- const carrier = carrierOf(target);
1378
+ const carrier = carrierOf2(target);
1100
1379
  const routes = ownRoutes(carrier);
1101
1380
  const buffer = ownParamBuffer(carrier);
1102
1381
  const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);
@@ -1112,7 +1391,7 @@ function recordRoute(target, fnName, method, subpath, options) {
1112
1391
  routes.push(route);
1113
1392
  }
1114
1393
  function recordParam(target, fnName, meta) {
1115
- const carrier = carrierOf(target);
1394
+ const carrier = carrierOf2(target);
1116
1395
  const buffer = ownParamBuffer(carrier);
1117
1396
  (buffer[fnName] ??= []).push(meta);
1118
1397
  const routes = carrier[ROUTES];
@@ -1125,7 +1404,7 @@ function recordParam(target, fnName, meta) {
1125
1404
  }
1126
1405
  }
1127
1406
  function recordThrows(target, fnName, throws) {
1128
- const carrier = carrierOf(target);
1407
+ const carrier = carrierOf2(target);
1129
1408
  const routes = carrier[ROUTES];
1130
1409
  const route = routes?.find((r) => r.fnName === fnName);
1131
1410
  if (route) {
@@ -1139,7 +1418,7 @@ function recordThrows(target, fnName, throws) {
1139
1418
  if (throwsBuffer) throwsBuffer[fnName] = throws;
1140
1419
  }
1141
1420
  function getRoutes(ctor) {
1142
- const carrier = carrierOf(ctor);
1421
+ const carrier = carrierOf2(ctor);
1143
1422
  const routes = carrier[ROUTES] ?? [];
1144
1423
  const returnBuffer = carrier[RETURN_BUFFER];
1145
1424
  if (returnBuffer) {
@@ -1275,90 +1554,6 @@ function defineMiddleware(fn) {
1275
1554
  return fn;
1276
1555
  }
1277
1556
 
1278
- // src/errors.ts
1279
- var HttpError = class extends Error {
1280
- status;
1281
- error;
1282
- errorDescription;
1283
- data;
1284
- constructor(status, error, errorDescription, data) {
1285
- super(errorDescription);
1286
- this.name = "HttpError";
1287
- this.status = status;
1288
- this.error = error;
1289
- this.errorDescription = errorDescription;
1290
- if (data !== void 0) {
1291
- this.data = data;
1292
- }
1293
- }
1294
- /**
1295
- * Serialize to the standard Palbase error response format.
1296
- * The `requestId` is injected by the runtime layer from the request context.
1297
- * When called without arguments (e.g. JSON.stringify), request_id is omitted.
1298
- * When `data` is set, it is appended as a strict-superset field.
1299
- */
1300
- toJSON(requestId) {
1301
- const result = {
1302
- error: this.error,
1303
- error_description: this.errorDescription,
1304
- status: this.status
1305
- };
1306
- if (requestId) {
1307
- result.request_id = requestId;
1308
- }
1309
- if (this.data !== void 0) {
1310
- result.data = this.data;
1311
- }
1312
- return result;
1313
- }
1314
- };
1315
- var PalError = class extends HttpError {
1316
- constructor(status, code, description, data) {
1317
- super(status, code, description, data);
1318
- this.name = "PalError";
1319
- }
1320
- };
1321
- var NamedHttpError = class extends HttpError {
1322
- constructor(status, defaultCode, name, message, code, data) {
1323
- super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
1324
- this.name = name;
1325
- }
1326
- };
1327
- function defaultMessage(name) {
1328
- const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
1329
- return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
1330
- }
1331
- var BadRequest = class extends NamedHttpError {
1332
- constructor(data, message) {
1333
- super(400, "bad_request", "BadRequest", message, void 0, data);
1334
- }
1335
- };
1336
- var Unauthorized = class extends NamedHttpError {
1337
- constructor(message, code, data) {
1338
- super(401, "unauthorized", "Unauthorized", message, code, data);
1339
- }
1340
- };
1341
- var Forbidden = class extends NamedHttpError {
1342
- constructor(message, code, data) {
1343
- super(403, "forbidden", "Forbidden", message, code, data);
1344
- }
1345
- };
1346
- var NotFound = class extends NamedHttpError {
1347
- constructor(message, code, data) {
1348
- super(404, "not_found", "NotFound", message, code, data);
1349
- }
1350
- };
1351
- var Conflict = class extends NamedHttpError {
1352
- constructor(message, code, data) {
1353
- super(409, "conflict", "Conflict", message, code, data);
1354
- }
1355
- };
1356
- var TooManyRequests = class extends NamedHttpError {
1357
- constructor(data, message) {
1358
- super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
1359
- }
1360
- };
1361
-
1362
1557
  // src/error-registry.ts
1363
1558
  var import_zod_to_openapi = require("@asteasolutions/zod-to-openapi");
1364
1559
  var import_zod = require("zod");
@@ -1368,7 +1563,27 @@ var BUILTIN_DATA_SCHEMAS = {
1368
1563
  bad_request: import_zod.z.object({
1369
1564
  fields: import_zod.z.array(import_zod.z.object({ field: import_zod.z.string(), message: import_zod.z.string() }))
1370
1565
  }),
1371
- too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() })
1566
+ too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() }),
1567
+ // Purchases (@RequireEntitlement / @Spend). Their payloads are what make a
1568
+ // paywall renderable on the client: WHICH entitlement was missing, and for a
1569
+ // spend the real ceiling and reset time rather than a guessed retry delay.
1570
+ entitlement_required: import_zod.z.object({ entitlement: import_zod.z.string().nullable() }),
1571
+ quota_exceeded: import_zod.z.object({
1572
+ key: import_zod.z.string(),
1573
+ scope: import_zod.z.string(),
1574
+ window: import_zod.z.string(),
1575
+ used: import_zod.z.number().int(),
1576
+ reserved: import_zod.z.number().int(),
1577
+ max: import_zod.z.number().int(),
1578
+ remaining: import_zod.z.number().int(),
1579
+ resetAt: import_zod.z.string()
1580
+ }),
1581
+ credit_insufficient: import_zod.z.object({
1582
+ key: import_zod.z.string(),
1583
+ balance: import_zod.z.number().int(),
1584
+ reserved: import_zod.z.number().int(),
1585
+ remaining: import_zod.z.number().int()
1586
+ })
1372
1587
  };
1373
1588
  function getErrorRegistry() {
1374
1589
  const g = globalThis;
@@ -1380,7 +1595,13 @@ function getErrorRegistry() {
1380
1595
  ["forbidden", 403, "Forbidden"],
1381
1596
  ["not_found", 404, "NotFound"],
1382
1597
  ["conflict", 409, "Conflict"],
1383
- ["too_many_requests", 429, "TooManyRequests"]
1598
+ ["too_many_requests", 429, "TooManyRequests"],
1599
+ // Thrown by the purchases decorators, so they are pre-seeded like the
1600
+ // other built-ins: a project never declares them, but every gated or
1601
+ // metered route must surface them as TYPED errors on the client.
1602
+ ["entitlement_required", 403, "EntitlementRequired"],
1603
+ ["quota_exceeded", 429, "QuotaExceeded"],
1604
+ ["credit_insufficient", 429, "CreditInsufficient"]
1384
1605
  ]) {
1385
1606
  const dataSchema = BUILTIN_DATA_SCHEMAS[code];
1386
1607
  m.set(code, {
@@ -1793,6 +2014,7 @@ var import_zod2 = require("zod");
1793
2014
  Patch,
1794
2015
  PolicyBuilder,
1795
2016
  Post,
2017
+ Purchases,
1796
2018
  Put,
1797
2019
  Query,
1798
2020
  QueryParams,
@@ -1801,8 +2023,10 @@ var import_zod2 = require("zod");
1801
2023
  Realtime,
1802
2024
  Req,
1803
2025
  RequestId,
2026
+ RequireEntitlement,
1804
2027
  Resource,
1805
2028
  STORAGE_CONFIG_KIND,
2029
+ Spend,
1806
2030
  Storage,
1807
2031
  TEST_USERS_CONFIG_KIND,
1808
2032
  TooManyRequests,
@@ -1835,6 +2059,7 @@ var import_zod2 = require("zod");
1835
2059
  defineWebhook,
1836
2060
  defineWorker,
1837
2061
  documents,
2062
+ entitlementFor,
1838
2063
  enumType,
1839
2064
  flag,
1840
2065
  getErrorRegistry,
@@ -1843,6 +2068,7 @@ var import_zod2 = require("zod");
1843
2068
  isPalbaseExtension,
1844
2069
  jsonb,
1845
2070
  makeEnvDts,
2071
+ makePurchasesDts,
1846
2072
  makeTypedDB,
1847
2073
  numeric,
1848
2074
  parseFileSizeLimit,
@@ -1850,6 +2076,7 @@ var import_zod2 = require("zod");
1850
2076
  raw,
1851
2077
  recordThrows,
1852
2078
  reservedSecretKey,
2079
+ spendFor,
1853
2080
  storage,
1854
2081
  testUser,
1855
2082
  text,