@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/{chunk-RGQUB66H.js → chunk-2PCNZBNZ.js} +3 -1
- package/dist/chunk-2PCNZBNZ.js.map +1 -0
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.ts +2 -2
- package/dist/{endpoint-Cn3ICGTf.d.cts → endpoint-92kVepng.d.cts} +54 -0
- package/dist/{endpoint-Cn3ICGTf.d.ts → endpoint-92kVepng.d.ts} +54 -0
- package/dist/{index-Bt7UHkAM.d.cts → index-BaHs33jr.d.cts} +1 -1
- package/dist/{index-V0ealeY-.d.ts → index-DfryIw9U.d.ts} +1 -1
- package/dist/index.cjs +322 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +261 -33
- package/dist/index.d.ts +261 -33
- package/dist/index.js +317 -96
- package/dist/index.js.map +1 -1
- package/dist/purchases/keys.cjs +19 -0
- package/dist/purchases/keys.cjs.map +1 -0
- package/dist/purchases/keys.d.cts +42 -0
- package/dist/purchases/keys.d.ts +42 -0
- package/dist/purchases/keys.js +1 -0
- package/dist/purchases/keys.js.map +1 -0
- package/dist/test/index.cjs +14 -1
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.js +14 -2
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +7 -6
- package/docs/config.md +7 -1
- package/docs/getting-started.md +14 -12
- package/docs/llms-full.txt +28 -25
- package/docs/migrations.md +8 -8
- package/package.json +11 -1
- 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,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
|
|
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 "
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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,
|