@palbase/backend 10.2.0 → 11.0.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 (41) hide show
  1. package/dist/chunk-7LAXRLPG.js +418 -0
  2. package/dist/chunk-7LAXRLPG.js.map +1 -0
  3. package/dist/{chunk-4WOQWFUP.js → chunk-LUV36KQU.js} +26 -11
  4. package/dist/{chunk-4WOQWFUP.js.map → chunk-LUV36KQU.js.map} +1 -1
  5. package/dist/{chunk-RGQUB66H.js → chunk-XATG7BRC.js} +22 -2
  6. package/dist/chunk-XATG7BRC.js.map +1 -0
  7. package/dist/db/index.cjs +438 -10
  8. package/dist/db/index.cjs.map +1 -1
  9. package/dist/db/index.d.cts +2 -2
  10. package/dist/db/index.d.ts +2 -2
  11. package/dist/db/index.js +13 -1
  12. package/dist/{endpoint-Cn3ICGTf.d.cts → endpoint-Ck4hER_7.d.cts} +397 -11
  13. package/dist/{endpoint-Cn3ICGTf.d.ts → endpoint-Ck4hER_7.d.ts} +397 -11
  14. package/dist/{index-Bt7UHkAM.d.cts → index-BJAf1uPC.d.cts} +64 -34
  15. package/dist/{index-V0ealeY-.d.ts → index-l7DhBDtn.d.ts} +64 -34
  16. package/dist/index.cjs +776 -108
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +203 -9
  19. package/dist/index.d.ts +203 -9
  20. package/dist/index.js +325 -94
  21. package/dist/index.js.map +1 -1
  22. package/dist/purchases/keys.cjs +19 -0
  23. package/dist/purchases/keys.cjs.map +1 -0
  24. package/dist/purchases/keys.d.cts +42 -0
  25. package/dist/purchases/keys.d.ts +42 -0
  26. package/dist/purchases/keys.js +1 -0
  27. package/dist/purchases/keys.js.map +1 -0
  28. package/dist/test/index.cjs +559 -13
  29. package/dist/test/index.cjs.map +1 -1
  30. package/dist/test/index.d.cts +1 -1
  31. package/dist/test/index.d.ts +1 -1
  32. package/dist/test/index.js +166 -13
  33. package/dist/test/index.js.map +1 -1
  34. package/docs/README.md +7 -6
  35. package/docs/database.md +106 -16
  36. package/docs/getting-started.md +14 -12
  37. package/docs/llms-full.txt +140 -45
  38. package/docs/migrations.md +8 -8
  39. package/docs/schema.md +6 -4
  40. package/package.json +11 -1
  41. package/dist/chunk-RGQUB66H.js.map +0 -1
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,12 +58,16 @@ __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,
65
68
  TraceId: () => TraceId,
69
+ TxPlanError: () => TxPlanError,
70
+ TxRefError: () => TxRefError,
66
71
  Unauthorized: () => Unauthorized,
67
72
  Upload: () => Upload,
68
73
  UploadedObject: () => UploadedObject,
@@ -79,6 +84,7 @@ __export(src_exports, {
79
84
  boolean: () => boolean,
80
85
  bucket: () => bucket,
81
86
  buildProvider: () => buildProvider,
87
+ dec: () => dec,
82
88
  defineEgress: () => defineEgress,
83
89
  defineError: () => defineError,
84
90
  defineFlags: () => defineFlags,
@@ -91,21 +97,26 @@ __export(src_exports, {
91
97
  defineWebhook: () => defineWebhook,
92
98
  defineWorker: () => defineWorker,
93
99
  documents: () => documents,
100
+ entitlementFor: () => entitlementFor,
94
101
  enumType: () => enumType,
95
102
  flag: () => flag,
96
103
  getErrorRegistry: () => getErrorRegistry,
97
104
  getRoutes: () => getRoutes,
105
+ inc: () => inc,
98
106
  integer: () => integer,
99
107
  isPalbaseExtension: () => isPalbaseExtension,
100
108
  jsonb: () => jsonb,
101
109
  makeEnvDts: () => makeEnvDts,
110
+ makePurchasesDts: () => makePurchasesDts,
102
111
  makeTypedDB: () => makeTypedDB,
112
+ now: () => now,
103
113
  numeric: () => numeric,
104
114
  parseFileSizeLimit: () => parseFileSizeLimit,
105
115
  policy: () => policy,
106
116
  raw: () => raw,
107
117
  recordThrows: () => recordThrows,
108
118
  reservedSecretKey: () => reservedSecretKey,
119
+ spendFor: () => spendFor,
109
120
  storage: () => storage,
110
121
  testUser: () => testUser,
111
122
  text: () => text,
@@ -118,6 +129,416 @@ module.exports = __toCommonJS(src_exports);
118
129
 
119
130
  // src/runtime.ts
120
131
  var import_node_async_hooks = require("async_hooks");
132
+
133
+ // src/db/tx-plan.ts
134
+ var TxRefError = class extends Error {
135
+ constructor(message) {
136
+ super(message);
137
+ this.name = "TxRefError";
138
+ }
139
+ };
140
+ var TxPlanError = class extends Error {
141
+ constructor(message) {
142
+ super(message);
143
+ this.name = "TxPlanError";
144
+ }
145
+ };
146
+ var EXPR = /* @__PURE__ */ Symbol.for("palbase.tx.expr");
147
+ var REF = /* @__PURE__ */ Symbol.for("palbase.tx.ref");
148
+ var ROW = /* @__PURE__ */ Symbol.for("palbase.tx.row");
149
+ var ROWS = /* @__PURE__ */ Symbol.for("palbase.tx.rows");
150
+ var TRAPPED_PROPS = [
151
+ "then",
152
+ "valueOf",
153
+ "toString",
154
+ "toJSON",
155
+ Symbol.toPrimitive
156
+ ];
157
+ function trap(prop, what, hint) {
158
+ const name = typeof prop === "symbol" ? prop.description ?? String(prop) : prop;
159
+ throw new TxRefError(
160
+ `${what} was used as a value (via \`${name}\`). Nothing in a transaction callback has run yet, so there is no value to read. ${hint}`
161
+ );
162
+ }
163
+ function now() {
164
+ return makeExpr({ fn: "now" });
165
+ }
166
+ function inc(by) {
167
+ assertFiniteNumber(by, "inc");
168
+ return makeExpr({ fn: "inc", by });
169
+ }
170
+ function dec(by) {
171
+ assertFiniteNumber(by, "dec");
172
+ return makeExpr({ fn: "dec", by });
173
+ }
174
+ function assertFiniteNumber(by, fn) {
175
+ if (typeof by !== "number" || !Number.isFinite(by)) {
176
+ throw new TxPlanError(`${fn}() needs a finite number, got ${String(by)}`);
177
+ }
178
+ }
179
+ function makeExpr(expr) {
180
+ return new Proxy(
181
+ { [EXPR]: expr },
182
+ {
183
+ get(target, prop) {
184
+ if (prop === EXPR) return target[EXPR];
185
+ if (TRAPPED_PROPS.includes(prop)) {
186
+ trap(prop, "A plan expression", "Write it into an operation instead.");
187
+ }
188
+ return void 0;
189
+ }
190
+ }
191
+ );
192
+ }
193
+ function makeRef(op, field) {
194
+ const target = { [REF]: { op, field } };
195
+ return new Proxy(target, {
196
+ get(t, prop) {
197
+ if (prop === REF) return t[REF];
198
+ if (TRAPPED_PROPS.includes(prop)) {
199
+ trap(
200
+ prop,
201
+ `\`${field}\` of a row this transaction has not written yet`,
202
+ "Pass it to another operation in the same plan, or return it from the callback and read it after `transaction()` resolves."
203
+ );
204
+ }
205
+ return void 0;
206
+ }
207
+ });
208
+ }
209
+ function makeRowHandle(op) {
210
+ const target = { [ROW]: op };
211
+ return new Proxy(target, {
212
+ get(t, prop) {
213
+ if (prop === ROW) return t[ROW];
214
+ if (TRAPPED_PROPS.includes(prop)) {
215
+ trap(
216
+ prop,
217
+ "A row this transaction has not written yet",
218
+ "Read one of its columns to reference it, or return the row from the callback and read it after `transaction()` resolves."
219
+ );
220
+ }
221
+ if (typeof prop === "symbol") return void 0;
222
+ return makeRef(op, prop);
223
+ }
224
+ });
225
+ }
226
+ function refDescriptor(v) {
227
+ if (typeof v !== "object" || v === null) return null;
228
+ const d = v[REF];
229
+ return isRefDescriptor(d) ? d : null;
230
+ }
231
+ function isRefDescriptor(d) {
232
+ return typeof d === "object" && d !== null && typeof d.op === "number" && typeof d.field === "string";
233
+ }
234
+ function rowOpIndex(v) {
235
+ if (typeof v !== "object" || v === null) return null;
236
+ const op = v[ROW];
237
+ return typeof op === "number" ? op : null;
238
+ }
239
+ function exprOf(v) {
240
+ if (typeof v !== "object" || v === null) return null;
241
+ const e = v[EXPR];
242
+ return typeof e === "object" && e !== null ? e : null;
243
+ }
244
+ function isRowsHandle(v) {
245
+ return typeof v === "object" && v !== null && v[ROWS] !== void 0;
246
+ }
247
+ function encodeValue(value, column, allowColumnExpr) {
248
+ const ref = refDescriptor(value);
249
+ if (ref) return { $ref: { op: ref.op, field: ref.field } };
250
+ const expr = exprOf(value);
251
+ if (expr) {
252
+ if (expr.fn !== "now" && !allowColumnExpr) {
253
+ throw new TxPlanError(
254
+ `\`${column}\`: ${expr.fn}() reads the column's current value, so it is only valid in updateWhere(where, set).`
255
+ );
256
+ }
257
+ return { $expr: expr };
258
+ }
259
+ if (rowOpIndex(value) !== null) {
260
+ throw new TxPlanError(
261
+ `\`${column}\`: a row handle is not a value. Read the column you meant (e.g. \`row.id\`).`
262
+ );
263
+ }
264
+ if (isRowsHandle(value)) {
265
+ throw new TxPlanError(
266
+ `\`${column}\`: an operation result is not a value. Declare an expectation first (\`.expectOne(err)\`) and read a column from the row.`
267
+ );
268
+ }
269
+ assertNoNestedHandles(value, column);
270
+ return value;
271
+ }
272
+ function assertNoNestedHandles(value, column) {
273
+ if (typeof value !== "object" || value === null) return;
274
+ if (value instanceof Date) return;
275
+ if (refDescriptor(value) || exprOf(value) || rowOpIndex(value) !== null || isRowsHandle(value)) {
276
+ throw new TxPlanError(
277
+ `\`${column}\`: a plan handle is nested inside a value. The server would store it as literal JSON, not resolve it. Put the reference directly in the column.`
278
+ );
279
+ }
280
+ if (Array.isArray(value)) {
281
+ for (const item of value) assertNoNestedHandles(item, column);
282
+ return;
283
+ }
284
+ for (const item of Object.values(value)) {
285
+ assertNoNestedHandles(item, column);
286
+ }
287
+ }
288
+ function encodeMap(map, allowColumnExpr) {
289
+ const out = {};
290
+ for (const key of Object.keys(map).sort()) {
291
+ const value = map[key];
292
+ if (value === void 0) continue;
293
+ out[key] = encodeValue(value, key, allowColumnExpr);
294
+ }
295
+ return out;
296
+ }
297
+ var SKIPPED_OP = -1;
298
+ var TxRowsImpl = class {
299
+ constructor(builder, opIndex, what) {
300
+ this.builder = builder;
301
+ this.opIndex = opIndex;
302
+ this.what = what;
303
+ }
304
+ builder;
305
+ opIndex;
306
+ what;
307
+ // Present so `isRowsHandle` recognises the object; never read for its value.
308
+ [ROWS] = true;
309
+ guarded = false;
310
+ // The type-level `await` guard made real: TS rejects `await rows` at compile
311
+ // time, and reaching this means someone called `.then(...)` by hand.
312
+ then() {
313
+ throw new TxRefError(
314
+ `${this.what} cannot be awaited: a transaction callback builds a plan, it does not run statements. Remove the \`await\`.`
315
+ );
316
+ }
317
+ expectOne(error) {
318
+ this.declareGuard("one", 1, error);
319
+ if (this.opIndex === SKIPPED_OP) throw error;
320
+ return makeRowHandle(this.opIndex);
321
+ }
322
+ expectNone(error) {
323
+ this.declareGuard("none", 0, error);
324
+ }
325
+ expectAtLeast(n, error) {
326
+ assertGuardCount(n, "expectAtLeast");
327
+ this.declareGuard("atLeast", n, error);
328
+ if (this.opIndex === SKIPPED_OP && n > 0) throw error;
329
+ }
330
+ expectAtMost(n, error) {
331
+ assertGuardCount(n, "expectAtMost");
332
+ this.declareGuard("atMost", n, error);
333
+ }
334
+ declareGuard(kind, n, error) {
335
+ if (!(error instanceof Error)) {
336
+ throw new TxPlanError(
337
+ `${this.what}: an expectation needs the Error to throw when it does not hold (e.g. \`.expect\u2026(new Conflict("already accepted"))\`).`
338
+ );
339
+ }
340
+ if (this.guarded) {
341
+ throw new TxPlanError(
342
+ `${this.what} already has an expectation. One operation carries one expectation; declare the second one on its own operation.`
343
+ );
344
+ }
345
+ this.guarded = true;
346
+ if (this.opIndex === SKIPPED_OP) return;
347
+ this.builder.attachGuard(this.opIndex, kind, n, error);
348
+ }
349
+ };
350
+ function assertGuardCount(n, fn) {
351
+ if (!Number.isInteger(n) || n < 0) {
352
+ throw new TxPlanError(`${fn}(n) needs a non-negative integer, got ${String(n)}`);
353
+ }
354
+ }
355
+ var MAX_OPS = 1e3;
356
+ var MAX_ROWS = 5e3;
357
+ var TxPlanBuilder = class {
358
+ ops = [];
359
+ /** Errors handed to expectations, indexed by the `slot` the server echoes. */
360
+ slots = [];
361
+ /** The table surface handed to the callback. Untyped here; the public
362
+ * `transaction()` signatures put the schema types on top. */
363
+ table(name) {
364
+ return {
365
+ insert: (values) => {
366
+ const encoded = encodeMap(values, false);
367
+ if (Object.keys(encoded).length === 0) {
368
+ throw new TxPlanError(`${name}.insert() needs at least one column`);
369
+ }
370
+ return this.push({ op: "insert", table: name, values: encoded }, `${name}.insert()`);
371
+ },
372
+ insertMany: (rows) => {
373
+ if (rows.length === 0) {
374
+ return new TxRowsImpl(this, SKIPPED_OP, `${name}.insertMany()`);
375
+ }
376
+ if (rows.length > MAX_ROWS) {
377
+ throw new TxPlanError(
378
+ `${name}.insertMany() has ${rows.length} rows; the limit is ${MAX_ROWS}. Split the write across requests.`
379
+ );
380
+ }
381
+ const encoded = rows.map((row) => encodeMap(row, false));
382
+ assertUniformRows(encoded, name);
383
+ return this.push({ op: "insertMany", table: name, rows: encoded }, `${name}.insertMany()`);
384
+ },
385
+ updateWhere: (where, set) => {
386
+ const encodedWhere = encodeMap(where, false);
387
+ const encodedSet = encodeMap(set, true);
388
+ if (Object.keys(encodedWhere).length === 0) {
389
+ throw new TxPlanError(
390
+ `${name}.updateWhere() needs a filter. An update with no filter rewrites the whole table.`
391
+ );
392
+ }
393
+ if (Object.keys(encodedSet).length === 0) {
394
+ throw new TxPlanError(`${name}.updateWhere() needs at least one column to set`);
395
+ }
396
+ return this.push(
397
+ { op: "update", table: name, set: encodedSet, where: encodedWhere },
398
+ `${name}.updateWhere()`
399
+ );
400
+ },
401
+ deleteWhere: (where) => {
402
+ const encodedWhere = encodeMap(where, false);
403
+ if (Object.keys(encodedWhere).length === 0) {
404
+ throw new TxPlanError(
405
+ `${name}.deleteWhere() needs a filter. A delete with no filter empties the table.`
406
+ );
407
+ }
408
+ return this.push(
409
+ { op: "delete", table: name, where: encodedWhere },
410
+ `${name}.deleteWhere()`
411
+ );
412
+ },
413
+ select: (where, options) => {
414
+ const op = { op: "select", table: name };
415
+ const encodedWhere = encodeMap(where ?? {}, false);
416
+ if (Object.keys(encodedWhere).length > 0) op.where = encodedWhere;
417
+ if (options?.limit !== void 0) {
418
+ if (!Number.isInteger(options.limit) || options.limit < 0) {
419
+ throw new TxPlanError(
420
+ `${name}.select(): limit needs a non-negative integer, got ${String(options.limit)}`
421
+ );
422
+ }
423
+ op.limit = options.limit;
424
+ }
425
+ if (options?.lock !== void 0) op.lock = options.lock;
426
+ return this.push(op, `${name}.select()`);
427
+ }
428
+ };
429
+ }
430
+ push(op, what) {
431
+ if (this.ops.length >= MAX_OPS) {
432
+ throw new TxPlanError(
433
+ `this transaction has ${MAX_OPS} operations, which is the limit. Use insertMany() for bulk writes, or split the work across requests.`
434
+ );
435
+ }
436
+ const index = this.ops.length;
437
+ this.ops.push(op);
438
+ return new TxRowsImpl(this, index, what);
439
+ }
440
+ /** Attach an expectation to an op and record its error in the slot table. */
441
+ attachGuard(opIndex, kind, n, error) {
442
+ const op = this.ops[opIndex];
443
+ if (!op) throw new TxPlanError(`internal: expectation on unknown operation ${opIndex}`);
444
+ const slot = this.slots.length;
445
+ this.slots.push(error);
446
+ op.guard = { kind, n, slot };
447
+ }
448
+ /** The serialisable plan. Empty when the callback described no writes. */
449
+ body() {
450
+ return { ops: this.ops };
451
+ }
452
+ /** The error the server's `slot` selects, or `null` when it names one this
453
+ * plan never declared (a server/client disagreement, not a tenant error). */
454
+ errorForSlot(slot) {
455
+ return this.slots[slot] ?? null;
456
+ }
457
+ };
458
+ function assertUniformRows(rows, table) {
459
+ const first = rows[0];
460
+ if (!first) return;
461
+ const want = Object.keys(first);
462
+ const wantKey = want.join(",");
463
+ for (let i = 1; i < rows.length; i++) {
464
+ const got = Object.keys(rows[i]);
465
+ if (got.join(",") !== wantKey) {
466
+ throw new TxPlanError(
467
+ `${table}.insertMany(): every row must set the same columns. Row 0 sets [${want.join(", ")}] but row ${i} sets [${got.join(", ")}]. (A property set to \`undefined\` counts as absent \u2014 use \`null\`.)`
468
+ );
469
+ }
470
+ }
471
+ }
472
+ function materializeResult(value, results) {
473
+ const ref = refDescriptor(value);
474
+ if (ref) {
475
+ const row = rowOf(results, ref.op, `\`${ref.field}\``);
476
+ if (!(ref.field in row)) {
477
+ throw new TxPlanError(
478
+ `the transaction's operation ${ref.op} returned no column \`${ref.field}\`.`
479
+ );
480
+ }
481
+ return row[ref.field];
482
+ }
483
+ const rowOp = rowOpIndex(value);
484
+ if (rowOp !== null) return rowOf(results, rowOp, "a row");
485
+ if (isRowsHandle(value)) {
486
+ throw new TxPlanError(
487
+ "an operation result cannot be returned from a transaction callback: its row count is not known until the plan runs. Declare an expectation (`.expectOne(err)`) and return the row, or a column of it."
488
+ );
489
+ }
490
+ if (Array.isArray(value)) return value.map((item) => materializeResult(item, results));
491
+ if (isPlainObject(value)) {
492
+ const out = {};
493
+ for (const [key, item] of Object.entries(value)) out[key] = materializeResult(item, results);
494
+ return out;
495
+ }
496
+ return value;
497
+ }
498
+ function rowOf(results, opIndex, what) {
499
+ const result = results[opIndex];
500
+ if (!result) {
501
+ throw new TxPlanError(
502
+ `the transaction returned no result for operation ${opIndex}, so ${what} cannot be read.`
503
+ );
504
+ }
505
+ const row = result.rows[0];
506
+ if (!row) {
507
+ throw new TxPlanError(
508
+ `the transaction's operation ${opIndex} returned no row, so ${what} cannot be read.`
509
+ );
510
+ }
511
+ return row;
512
+ }
513
+ function isPlainObject(value) {
514
+ if (typeof value !== "object" || value === null) return false;
515
+ const proto = Object.getPrototypeOf(value);
516
+ return proto === Object.prototype || proto === null;
517
+ }
518
+ async function runTxPlan(transport, tables, builder, fn) {
519
+ const returned = fn({ tables });
520
+ const body = builder.body();
521
+ if (body.ops.length === 0) {
522
+ return materializeResult(returned, []);
523
+ }
524
+ let response;
525
+ try {
526
+ response = await transport.txPlan(body);
527
+ } catch (err) {
528
+ throw translateRejection(err, builder);
529
+ }
530
+ return materializeResult(returned, response.results);
531
+ }
532
+ function translateRejection(err, builder) {
533
+ if (typeof err !== "object" || err === null) return err;
534
+ const rejection = err;
535
+ if (rejection.error_code !== "tx_guard_failed" || typeof rejection.slot !== "number") {
536
+ return err;
537
+ }
538
+ return builder.errorForSlot(rejection.slot) ?? err;
539
+ }
540
+
541
+ // src/runtime.ts
121
542
  var __requestALS = new import_node_async_hooks.AsyncLocalStorage();
122
543
  var runtime = null;
123
544
  function __setRuntime(services) {
@@ -178,10 +599,23 @@ function makeTypedSurface(raw2) {
178
599
  return Object.assign(ops, {
179
600
  tables: makeTablesAccessor(() => raw2),
180
601
  transaction(fn) {
181
- return raw2.transaction((rawTx) => fn({ tables: makeTablesAccessor(() => rawTx) }));
602
+ const builder = new TxPlanBuilder();
603
+ return runTxPlan(raw2, makeTxTablesAccessor(builder), builder, fn);
182
604
  }
183
605
  });
184
606
  }
607
+ function makeTxTablesAccessor(builder) {
608
+ const tablesProxy = new Proxy(
609
+ {},
610
+ {
611
+ get(_t, prop) {
612
+ if (typeof prop !== "string") return void 0;
613
+ return builder.table(prop);
614
+ }
615
+ }
616
+ );
617
+ return tablesProxy;
618
+ }
185
619
  var Database = Object.assign(makeTypedSurface(rawDatabase), {
186
620
  /**
187
621
  * Lazily resolve the runtime's service-role sibling on each call. We do NOT
@@ -200,6 +634,7 @@ var Cache = makeServiceProxy("Cache");
200
634
  var Queue = makeServiceProxy("Queue");
201
635
  var Log = makeServiceProxy("Log");
202
636
  var Notifications = makeServiceProxy("Notifications");
637
+ var Purchases = makeServiceProxy("Purchases");
203
638
  var rawFlags = makeServiceProxy("Flags");
204
639
  var Flags = Object.assign(
205
640
  {
@@ -230,6 +665,276 @@ var Flags = Object.assign(
230
665
  );
231
666
  var Realtime = makeServiceProxy("Realtime");
232
667
 
668
+ // src/errors.ts
669
+ var HttpError = class extends Error {
670
+ status;
671
+ error;
672
+ errorDescription;
673
+ data;
674
+ constructor(status, error, errorDescription, data) {
675
+ super(errorDescription);
676
+ this.name = "HttpError";
677
+ this.status = status;
678
+ this.error = error;
679
+ this.errorDescription = errorDescription;
680
+ if (data !== void 0) {
681
+ this.data = data;
682
+ }
683
+ }
684
+ /**
685
+ * Serialize to the standard Palbase error response format.
686
+ * The `requestId` is injected by the runtime layer from the request context.
687
+ * When called without arguments (e.g. JSON.stringify), request_id is omitted.
688
+ * When `data` is set, it is appended as a strict-superset field.
689
+ */
690
+ toJSON(requestId) {
691
+ const result = {
692
+ error: this.error,
693
+ error_description: this.errorDescription,
694
+ status: this.status
695
+ };
696
+ if (requestId) {
697
+ result.request_id = requestId;
698
+ }
699
+ if (this.data !== void 0) {
700
+ result.data = this.data;
701
+ }
702
+ return result;
703
+ }
704
+ };
705
+ var PalError = class extends HttpError {
706
+ constructor(status, code, description, data) {
707
+ super(status, code, description, data);
708
+ this.name = "PalError";
709
+ }
710
+ };
711
+ var NamedHttpError = class extends HttpError {
712
+ constructor(status, defaultCode, name, message, code, data) {
713
+ super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
714
+ this.name = name;
715
+ }
716
+ };
717
+ function defaultMessage(name) {
718
+ const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
719
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
720
+ }
721
+ var BadRequest = class extends NamedHttpError {
722
+ constructor(data, message) {
723
+ super(400, "bad_request", "BadRequest", message, void 0, data);
724
+ }
725
+ };
726
+ var Unauthorized = class extends NamedHttpError {
727
+ constructor(message, code, data) {
728
+ super(401, "unauthorized", "Unauthorized", message, code, data);
729
+ }
730
+ };
731
+ var Forbidden = class extends NamedHttpError {
732
+ constructor(message, code, data) {
733
+ super(403, "forbidden", "Forbidden", message, code, data);
734
+ }
735
+ };
736
+ var NotFound = class extends NamedHttpError {
737
+ constructor(message, code, data) {
738
+ super(404, "not_found", "NotFound", message, code, data);
739
+ }
740
+ };
741
+ var Conflict = class extends NamedHttpError {
742
+ constructor(message, code, data) {
743
+ super(409, "conflict", "Conflict", message, code, data);
744
+ }
745
+ };
746
+ var TooManyRequests = class extends NamedHttpError {
747
+ constructor(data, message) {
748
+ super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
749
+ }
750
+ };
751
+
752
+ // src/purchases/errors.ts
753
+ function asPalstoreError(err) {
754
+ if (!(err instanceof Error)) return null;
755
+ const e = err;
756
+ if (typeof e.code !== "string" || typeof e.status !== "number") return null;
757
+ return { code: e.code, status: e.status, message: e.message };
758
+ }
759
+ function toHttpError(err) {
760
+ if (err instanceof HttpError) return err;
761
+ const p = asPalstoreError(err);
762
+ if (!p) return err;
763
+ switch (p.code) {
764
+ case "entitlement_required": {
765
+ const entitlement = err.entitlement ?? null;
766
+ return new PalError(403, "entitlement_required", p.message, { entitlement });
767
+ }
768
+ case "quota_exceeded":
769
+ case "credit_insufficient": {
770
+ const state = err.state;
771
+ return new PalError(429, p.code, p.message, state);
772
+ }
773
+ default:
774
+ return err;
775
+ }
776
+ }
777
+
778
+ // src/purchases/registry.ts
779
+ var ENTITLEMENTS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.entitlements");
780
+ var SPENDS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.spends");
781
+ function carrierOf(target) {
782
+ return typeof target === "function" ? target : target.constructor ?? target;
783
+ }
784
+ function own(carrier, slot) {
785
+ if (!Object.prototype.hasOwnProperty.call(carrier, slot)) {
786
+ carrier[slot] = {};
787
+ }
788
+ return carrier[slot];
789
+ }
790
+ function recordEntitlement(target, fnName, key) {
791
+ own(carrierOf(target), ENTITLEMENTS)[fnName] = key;
792
+ }
793
+ function recordSpend(target, fnName, meta) {
794
+ own(carrierOf(target), SPENDS)[fnName] = meta;
795
+ }
796
+ function entitlementFor(ctor, fnName) {
797
+ return carrierOf(ctor)[ENTITLEMENTS]?.[fnName];
798
+ }
799
+ function spendFor(ctor, fnName) {
800
+ return carrierOf(ctor)[SPENDS]?.[fnName];
801
+ }
802
+
803
+ // src/purchases/subject.ts
804
+ var subjectByRequest = /* @__PURE__ */ new WeakMap();
805
+ function storeEnv() {
806
+ return process.env.PALSTORE_STORE_ENV === "sandbox" ? "sandbox" : "production";
807
+ }
808
+ async function currentSubjectId() {
809
+ const store = __requestALS.getStore();
810
+ if (!store) {
811
+ throw new Error(
812
+ "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."
813
+ );
814
+ }
815
+ const memoized = subjectByRequest.get(store);
816
+ if (memoized) return memoized;
817
+ const userId = store.userId;
818
+ if (!userId) {
819
+ throw new Unauthorized(
820
+ "This endpoint requires an authenticated user: purchases entitlements and quota are per-user, and the subject is resolved from the request's identity."
821
+ );
822
+ }
823
+ const resolving = Purchases.resolveSubject({ userRef: userId, storeEnv: storeEnv() }).then(
824
+ (r) => r.subjectId
825
+ );
826
+ subjectByRequest.set(store, resolving);
827
+ resolving.catch(() => subjectByRequest.delete(store));
828
+ return resolving;
829
+ }
830
+
831
+ // src/purchases/decorators.ts
832
+ var WRAPPED = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.wrapped");
833
+ function idempotencyKeyFor(key) {
834
+ const store = __requestALS.getStore();
835
+ const base = store?.idempotencyKey || store?.requestId;
836
+ if (!base) {
837
+ throw new Error(
838
+ "@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."
839
+ );
840
+ }
841
+ return `${base}:${key}`;
842
+ }
843
+ function ensureWrapper(target, fnName, descriptor) {
844
+ const original = descriptor.value;
845
+ if (typeof original !== "function") {
846
+ throw new Error(
847
+ `@RequireEntitlement/@Spend can only decorate a method; "${fnName}" is not one.`
848
+ );
849
+ }
850
+ if (original[WRAPPED]) return;
851
+ const wrapper = async function(...args) {
852
+ const ctor = this.constructor;
853
+ const entitlement = entitlementFor(ctor, fnName);
854
+ const spend = spendFor(ctor, fnName);
855
+ let subjectId;
856
+ try {
857
+ subjectId = await currentSubjectId();
858
+ } catch (err) {
859
+ throw toHttpError(err);
860
+ }
861
+ if (entitlement !== void 0) {
862
+ try {
863
+ await Purchases.require(subjectId, entitlement);
864
+ } catch (err) {
865
+ throw toHttpError(err);
866
+ }
867
+ }
868
+ if (spend === void 0) {
869
+ return original.apply(this, args);
870
+ }
871
+ let handlerFailure;
872
+ try {
873
+ return await Purchases.withSpend(
874
+ subjectId,
875
+ spend.key,
876
+ { count: spend.count, idempotencyKey: idempotencyKeyFor(spend.key) },
877
+ async () => {
878
+ try {
879
+ return await original.apply(this, args);
880
+ } catch (err) {
881
+ handlerFailure = { err };
882
+ throw err;
883
+ }
884
+ }
885
+ );
886
+ } catch (err) {
887
+ if (handlerFailure !== void 0 && handlerFailure.err === err) throw err;
888
+ throw toHttpError(err);
889
+ }
890
+ };
891
+ wrapper[WRAPPED] = true;
892
+ descriptor.value = wrapper;
893
+ }
894
+ function RequireEntitlement(key) {
895
+ return function(target, propertyKey, descriptor) {
896
+ const fnName = String(propertyKey);
897
+ recordEntitlement(target, fnName, key);
898
+ ensureWrapper(target, fnName, descriptor);
899
+ };
900
+ }
901
+ function Spend(key) {
902
+ return function(target, propertyKey, descriptor) {
903
+ const fnName = String(propertyKey);
904
+ recordSpend(target, fnName, { key, count: 1 });
905
+ ensureWrapper(target, fnName, descriptor);
906
+ };
907
+ }
908
+
909
+ // src/purchases/keys-gen.ts
910
+ function liveKeys(entries) {
911
+ return Object.entries(entries ?? {}).filter(([, def]) => def?.removed !== true).map(([key]) => key).sort();
912
+ }
913
+ function memberName(key) {
914
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
915
+ }
916
+ function members(keys) {
917
+ if (keys.length === 0) return "";
918
+ return `
919
+ ${keys.map((k) => ` ${memberName(k)}: true;`).join("\n")}
920
+ `;
921
+ }
922
+ function makePurchasesDts(manifest) {
923
+ const entitlements = liveKeys(manifest.entitlements);
924
+ const limits = liveKeys({ ...manifest.limits, ...manifest.credits });
925
+ return `// palbase-purchases.d.ts \u2014 GENERATED by @palbase/backend. Do not edit.
926
+ // Source: the project's palstore catalog revision.
927
+
928
+ declare module "@palbase/backend/purchases" {
929
+ interface Entitlements {${members(entitlements)}}
930
+
931
+ interface Limits {${members(limits)}}
932
+ }
933
+
934
+ export {};
935
+ `;
936
+ }
937
+
233
938
  // src/db/policy.ts
234
939
  var PolicyBuilder = class {
235
940
  _def;
@@ -505,19 +1210,29 @@ function makeTypedTable(name, raw2) {
505
1210
  };
506
1211
  }
507
1212
  function makeTypedDB(schema, raw2) {
508
- function buildTables(client) {
509
- const tables = {};
510
- for (const key of Object.keys(schema.tables)) {
511
- const tableDef = schema.tables[key];
512
- if (tableDef !== void 0) {
513
- tables[key] = makeTypedTable(tableDef.name, client);
514
- }
1213
+ const tables = {};
1214
+ for (const key of Object.keys(schema.tables)) {
1215
+ const tableDef = schema.tables[key];
1216
+ if (tableDef !== void 0) {
1217
+ tables[key] = makeTypedTable(tableDef.name, raw2);
515
1218
  }
516
- return tables;
517
1219
  }
518
1220
  const result = {
519
- tables: buildTables(raw2),
520
- transaction: (fn) => raw2.transaction((rawTx) => fn({ tables: buildTables(rawTx) }))
1221
+ tables,
1222
+ transaction(fn) {
1223
+ const builder = new TxPlanBuilder();
1224
+ const planTables = {};
1225
+ for (const key of Object.keys(schema.tables)) {
1226
+ const tableDef = schema.tables[key];
1227
+ if (tableDef !== void 0) planTables[key] = builder.table(tableDef.name);
1228
+ }
1229
+ return runTxPlan(
1230
+ raw2,
1231
+ planTables,
1232
+ builder,
1233
+ fn
1234
+ );
1235
+ }
521
1236
  };
522
1237
  return result;
523
1238
  }
@@ -631,7 +1346,7 @@ function makeEnvDts(schema) {
631
1346
  ${blocks.join("\n")}
632
1347
  ` : "";
633
1348
  return `// AUTO-GENERATED by @palbase/backend \u2014 DO NOT EDIT.
634
- // Regenerated from db/schema.ts on every \`palbase serve\` / deploy.
1349
+ // Regenerated from db/schema.ts on every \`palbase db types\` / deploy.
635
1350
  // Augments the @palbase/backend/env \`Tables\` interface so \`Database.tables.*\`
636
1351
  // is typed with no import and no generic.
637
1352
 
@@ -856,12 +1571,12 @@ var E164_RE = /^\+[1-9]\d{1,14}$/;
856
1571
  var OTP_LENGTH = 6;
857
1572
  var OTP_RE = /^\d{6}$/;
858
1573
  var MIN_PASSWORD_LENGTH = 8;
859
- function isPlainObject(value) {
1574
+ function isPlainObject2(value) {
860
1575
  return typeof value === "object" && value !== null && !Array.isArray(value);
861
1576
  }
862
1577
  function normalizeSeed(seed, userName) {
863
1578
  if (seed === void 0) return {};
864
- if (!isPlainObject(seed)) {
1579
+ if (!isPlainObject2(seed)) {
865
1580
  throw new Error(`testUser ${JSON.stringify(userName)}: seed must be { <table>: [ {...} ] }`);
866
1581
  }
867
1582
  const out = {};
@@ -877,7 +1592,7 @@ function normalizeSeed(seed, userName) {
877
1592
  );
878
1593
  }
879
1594
  for (const row of rows) {
880
- if (!isPlainObject(row)) {
1595
+ if (!isPlainObject2(row)) {
881
1596
  throw new Error(
882
1597
  `testUser ${JSON.stringify(userName)}: every row in seed.${table} must be an object`
883
1598
  );
@@ -888,7 +1603,7 @@ function normalizeSeed(seed, userName) {
888
1603
  return out;
889
1604
  }
890
1605
  function testUser(opts = {}) {
891
- if (!isPlainObject(opts)) {
1606
+ if (!isPlainObject2(opts)) {
892
1607
  throw new Error("testUser() expects { email?, password?, phone?, otp?, seed? }");
893
1608
  }
894
1609
  const hasEmail = opts.email !== void 0;
@@ -938,7 +1653,7 @@ function testUser(opts = {}) {
938
1653
  return { email, password, phone, otp, seed: normalizeSeed(opts.seed, email ?? "<template>") };
939
1654
  }
940
1655
  function defineTestUsers(input) {
941
- if (!isPlainObject(input) || !isPlainObject(input.users)) {
1656
+ if (!isPlainObject2(input) || !isPlainObject2(input.users)) {
942
1657
  throw new Error("defineTestUsers expects { users: { <name>: testUser({...}) } }");
943
1658
  }
944
1659
  const users = {};
@@ -1081,7 +1796,7 @@ var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
1081
1796
  var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
1082
1797
  var RETURN_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.returnBuffer");
1083
1798
  var THROWS_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.throwsBuffer");
1084
- function carrierOf(target) {
1799
+ function carrierOf2(target) {
1085
1800
  const ctor = typeof target === "function" ? target : target.constructor ?? target;
1086
1801
  return ctor;
1087
1802
  }
@@ -1098,7 +1813,7 @@ function ownParamBuffer(carrier) {
1098
1813
  return carrier[PARAM_BUFFER];
1099
1814
  }
1100
1815
  function recordRoute(target, fnName, method, subpath, options) {
1101
- const carrier = carrierOf(target);
1816
+ const carrier = carrierOf2(target);
1102
1817
  const routes = ownRoutes(carrier);
1103
1818
  const buffer = ownParamBuffer(carrier);
1104
1819
  const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);
@@ -1114,7 +1829,7 @@ function recordRoute(target, fnName, method, subpath, options) {
1114
1829
  routes.push(route);
1115
1830
  }
1116
1831
  function recordParam(target, fnName, meta) {
1117
- const carrier = carrierOf(target);
1832
+ const carrier = carrierOf2(target);
1118
1833
  const buffer = ownParamBuffer(carrier);
1119
1834
  (buffer[fnName] ??= []).push(meta);
1120
1835
  const routes = carrier[ROUTES];
@@ -1127,7 +1842,7 @@ function recordParam(target, fnName, meta) {
1127
1842
  }
1128
1843
  }
1129
1844
  function recordThrows(target, fnName, throws) {
1130
- const carrier = carrierOf(target);
1845
+ const carrier = carrierOf2(target);
1131
1846
  const routes = carrier[ROUTES];
1132
1847
  const route = routes?.find((r) => r.fnName === fnName);
1133
1848
  if (route) {
@@ -1141,7 +1856,7 @@ function recordThrows(target, fnName, throws) {
1141
1856
  if (throwsBuffer) throwsBuffer[fnName] = throws;
1142
1857
  }
1143
1858
  function getRoutes(ctor) {
1144
- const carrier = carrierOf(ctor);
1859
+ const carrier = carrierOf2(ctor);
1145
1860
  const routes = carrier[ROUTES] ?? [];
1146
1861
  const returnBuffer = carrier[RETURN_BUFFER];
1147
1862
  if (returnBuffer) {
@@ -1277,90 +1992,6 @@ function defineMiddleware(fn) {
1277
1992
  return fn;
1278
1993
  }
1279
1994
 
1280
- // src/errors.ts
1281
- var HttpError = class extends Error {
1282
- status;
1283
- error;
1284
- errorDescription;
1285
- data;
1286
- constructor(status, error, errorDescription, data) {
1287
- super(errorDescription);
1288
- this.name = "HttpError";
1289
- this.status = status;
1290
- this.error = error;
1291
- this.errorDescription = errorDescription;
1292
- if (data !== void 0) {
1293
- this.data = data;
1294
- }
1295
- }
1296
- /**
1297
- * Serialize to the standard Palbase error response format.
1298
- * The `requestId` is injected by the runtime layer from the request context.
1299
- * When called without arguments (e.g. JSON.stringify), request_id is omitted.
1300
- * When `data` is set, it is appended as a strict-superset field.
1301
- */
1302
- toJSON(requestId) {
1303
- const result = {
1304
- error: this.error,
1305
- error_description: this.errorDescription,
1306
- status: this.status
1307
- };
1308
- if (requestId) {
1309
- result.request_id = requestId;
1310
- }
1311
- if (this.data !== void 0) {
1312
- result.data = this.data;
1313
- }
1314
- return result;
1315
- }
1316
- };
1317
- var PalError = class extends HttpError {
1318
- constructor(status, code, description, data) {
1319
- super(status, code, description, data);
1320
- this.name = "PalError";
1321
- }
1322
- };
1323
- var NamedHttpError = class extends HttpError {
1324
- constructor(status, defaultCode, name, message, code, data) {
1325
- super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
1326
- this.name = name;
1327
- }
1328
- };
1329
- function defaultMessage(name) {
1330
- const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
1331
- return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
1332
- }
1333
- var BadRequest = class extends NamedHttpError {
1334
- constructor(data, message) {
1335
- super(400, "bad_request", "BadRequest", message, void 0, data);
1336
- }
1337
- };
1338
- var Unauthorized = class extends NamedHttpError {
1339
- constructor(message, code, data) {
1340
- super(401, "unauthorized", "Unauthorized", message, code, data);
1341
- }
1342
- };
1343
- var Forbidden = class extends NamedHttpError {
1344
- constructor(message, code, data) {
1345
- super(403, "forbidden", "Forbidden", message, code, data);
1346
- }
1347
- };
1348
- var NotFound = class extends NamedHttpError {
1349
- constructor(message, code, data) {
1350
- super(404, "not_found", "NotFound", message, code, data);
1351
- }
1352
- };
1353
- var Conflict = class extends NamedHttpError {
1354
- constructor(message, code, data) {
1355
- super(409, "conflict", "Conflict", message, code, data);
1356
- }
1357
- };
1358
- var TooManyRequests = class extends NamedHttpError {
1359
- constructor(data, message) {
1360
- super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
1361
- }
1362
- };
1363
-
1364
1995
  // src/error-registry.ts
1365
1996
  var import_zod_to_openapi = require("@asteasolutions/zod-to-openapi");
1366
1997
  var import_zod = require("zod");
@@ -1370,7 +2001,27 @@ var BUILTIN_DATA_SCHEMAS = {
1370
2001
  bad_request: import_zod.z.object({
1371
2002
  fields: import_zod.z.array(import_zod.z.object({ field: import_zod.z.string(), message: import_zod.z.string() }))
1372
2003
  }),
1373
- too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() })
2004
+ too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() }),
2005
+ // Purchases (@RequireEntitlement / @Spend). Their payloads are what make a
2006
+ // paywall renderable on the client: WHICH entitlement was missing, and for a
2007
+ // spend the real ceiling and reset time rather than a guessed retry delay.
2008
+ entitlement_required: import_zod.z.object({ entitlement: import_zod.z.string().nullable() }),
2009
+ quota_exceeded: import_zod.z.object({
2010
+ key: import_zod.z.string(),
2011
+ scope: import_zod.z.string(),
2012
+ window: import_zod.z.string(),
2013
+ used: import_zod.z.number().int(),
2014
+ reserved: import_zod.z.number().int(),
2015
+ max: import_zod.z.number().int(),
2016
+ remaining: import_zod.z.number().int(),
2017
+ resetAt: import_zod.z.string()
2018
+ }),
2019
+ credit_insufficient: import_zod.z.object({
2020
+ key: import_zod.z.string(),
2021
+ balance: import_zod.z.number().int(),
2022
+ reserved: import_zod.z.number().int(),
2023
+ remaining: import_zod.z.number().int()
2024
+ })
1374
2025
  };
1375
2026
  function getErrorRegistry() {
1376
2027
  const g = globalThis;
@@ -1382,7 +2033,13 @@ function getErrorRegistry() {
1382
2033
  ["forbidden", 403, "Forbidden"],
1383
2034
  ["not_found", 404, "NotFound"],
1384
2035
  ["conflict", 409, "Conflict"],
1385
- ["too_many_requests", 429, "TooManyRequests"]
2036
+ ["too_many_requests", 429, "TooManyRequests"],
2037
+ // Thrown by the purchases decorators, so they are pre-seeded like the
2038
+ // other built-ins: a project never declares them, but every gated or
2039
+ // metered route must surface them as TYPED errors on the client.
2040
+ ["entitlement_required", 403, "EntitlementRequired"],
2041
+ ["quota_exceeded", 429, "QuotaExceeded"],
2042
+ ["credit_insufficient", 429, "CreditInsufficient"]
1386
2043
  ]) {
1387
2044
  const dataSchema = BUILTIN_DATA_SCHEMAS[code];
1388
2045
  m.set(code, {
@@ -1795,6 +2452,7 @@ var import_zod2 = require("zod");
1795
2452
  Patch,
1796
2453
  PolicyBuilder,
1797
2454
  Post,
2455
+ Purchases,
1798
2456
  Put,
1799
2457
  Query,
1800
2458
  QueryParams,
@@ -1803,12 +2461,16 @@ var import_zod2 = require("zod");
1803
2461
  Realtime,
1804
2462
  Req,
1805
2463
  RequestId,
2464
+ RequireEntitlement,
1806
2465
  Resource,
1807
2466
  STORAGE_CONFIG_KIND,
2467
+ Spend,
1808
2468
  Storage,
1809
2469
  TEST_USERS_CONFIG_KIND,
1810
2470
  TooManyRequests,
1811
2471
  TraceId,
2472
+ TxPlanError,
2473
+ TxRefError,
1812
2474
  Unauthorized,
1813
2475
  Upload,
1814
2476
  UploadedObject,
@@ -1825,6 +2487,7 @@ var import_zod2 = require("zod");
1825
2487
  boolean,
1826
2488
  bucket,
1827
2489
  buildProvider,
2490
+ dec,
1828
2491
  defineEgress,
1829
2492
  defineError,
1830
2493
  defineFlags,
@@ -1837,21 +2500,26 @@ var import_zod2 = require("zod");
1837
2500
  defineWebhook,
1838
2501
  defineWorker,
1839
2502
  documents,
2503
+ entitlementFor,
1840
2504
  enumType,
1841
2505
  flag,
1842
2506
  getErrorRegistry,
1843
2507
  getRoutes,
2508
+ inc,
1844
2509
  integer,
1845
2510
  isPalbaseExtension,
1846
2511
  jsonb,
1847
2512
  makeEnvDts,
2513
+ makePurchasesDts,
1848
2514
  makeTypedDB,
2515
+ now,
1849
2516
  numeric,
1850
2517
  parseFileSizeLimit,
1851
2518
  policy,
1852
2519
  raw,
1853
2520
  recordThrows,
1854
2521
  reservedSecretKey,
2522
+ spendFor,
1855
2523
  storage,
1856
2524
  testUser,
1857
2525
  text,