@palbase/backend 10.2.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 +317 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +203 -9
- package/dist/index.d.ts +203 -9
- package/dist/index.js +312 -93
- 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/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
|
|
|
@@ -1081,7 +1358,7 @@ var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
|
|
|
1081
1358
|
var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
|
|
1082
1359
|
var RETURN_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.returnBuffer");
|
|
1083
1360
|
var THROWS_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.throwsBuffer");
|
|
1084
|
-
function
|
|
1361
|
+
function carrierOf2(target) {
|
|
1085
1362
|
const ctor = typeof target === "function" ? target : target.constructor ?? target;
|
|
1086
1363
|
return ctor;
|
|
1087
1364
|
}
|
|
@@ -1098,7 +1375,7 @@ function ownParamBuffer(carrier) {
|
|
|
1098
1375
|
return carrier[PARAM_BUFFER];
|
|
1099
1376
|
}
|
|
1100
1377
|
function recordRoute(target, fnName, method, subpath, options) {
|
|
1101
|
-
const carrier =
|
|
1378
|
+
const carrier = carrierOf2(target);
|
|
1102
1379
|
const routes = ownRoutes(carrier);
|
|
1103
1380
|
const buffer = ownParamBuffer(carrier);
|
|
1104
1381
|
const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
@@ -1114,7 +1391,7 @@ function recordRoute(target, fnName, method, subpath, options) {
|
|
|
1114
1391
|
routes.push(route);
|
|
1115
1392
|
}
|
|
1116
1393
|
function recordParam(target, fnName, meta) {
|
|
1117
|
-
const carrier =
|
|
1394
|
+
const carrier = carrierOf2(target);
|
|
1118
1395
|
const buffer = ownParamBuffer(carrier);
|
|
1119
1396
|
(buffer[fnName] ??= []).push(meta);
|
|
1120
1397
|
const routes = carrier[ROUTES];
|
|
@@ -1127,7 +1404,7 @@ function recordParam(target, fnName, meta) {
|
|
|
1127
1404
|
}
|
|
1128
1405
|
}
|
|
1129
1406
|
function recordThrows(target, fnName, throws) {
|
|
1130
|
-
const carrier =
|
|
1407
|
+
const carrier = carrierOf2(target);
|
|
1131
1408
|
const routes = carrier[ROUTES];
|
|
1132
1409
|
const route = routes?.find((r) => r.fnName === fnName);
|
|
1133
1410
|
if (route) {
|
|
@@ -1141,7 +1418,7 @@ function recordThrows(target, fnName, throws) {
|
|
|
1141
1418
|
if (throwsBuffer) throwsBuffer[fnName] = throws;
|
|
1142
1419
|
}
|
|
1143
1420
|
function getRoutes(ctor) {
|
|
1144
|
-
const carrier =
|
|
1421
|
+
const carrier = carrierOf2(ctor);
|
|
1145
1422
|
const routes = carrier[ROUTES] ?? [];
|
|
1146
1423
|
const returnBuffer = carrier[RETURN_BUFFER];
|
|
1147
1424
|
if (returnBuffer) {
|
|
@@ -1277,90 +1554,6 @@ function defineMiddleware(fn) {
|
|
|
1277
1554
|
return fn;
|
|
1278
1555
|
}
|
|
1279
1556
|
|
|
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
1557
|
// src/error-registry.ts
|
|
1365
1558
|
var import_zod_to_openapi = require("@asteasolutions/zod-to-openapi");
|
|
1366
1559
|
var import_zod = require("zod");
|
|
@@ -1370,7 +1563,27 @@ var BUILTIN_DATA_SCHEMAS = {
|
|
|
1370
1563
|
bad_request: import_zod.z.object({
|
|
1371
1564
|
fields: import_zod.z.array(import_zod.z.object({ field: import_zod.z.string(), message: import_zod.z.string() }))
|
|
1372
1565
|
}),
|
|
1373
|
-
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
|
+
})
|
|
1374
1587
|
};
|
|
1375
1588
|
function getErrorRegistry() {
|
|
1376
1589
|
const g = globalThis;
|
|
@@ -1382,7 +1595,13 @@ function getErrorRegistry() {
|
|
|
1382
1595
|
["forbidden", 403, "Forbidden"],
|
|
1383
1596
|
["not_found", 404, "NotFound"],
|
|
1384
1597
|
["conflict", 409, "Conflict"],
|
|
1385
|
-
["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"]
|
|
1386
1605
|
]) {
|
|
1387
1606
|
const dataSchema = BUILTIN_DATA_SCHEMAS[code];
|
|
1388
1607
|
m.set(code, {
|
|
@@ -1795,6 +2014,7 @@ var import_zod2 = require("zod");
|
|
|
1795
2014
|
Patch,
|
|
1796
2015
|
PolicyBuilder,
|
|
1797
2016
|
Post,
|
|
2017
|
+
Purchases,
|
|
1798
2018
|
Put,
|
|
1799
2019
|
Query,
|
|
1800
2020
|
QueryParams,
|
|
@@ -1803,8 +2023,10 @@ var import_zod2 = require("zod");
|
|
|
1803
2023
|
Realtime,
|
|
1804
2024
|
Req,
|
|
1805
2025
|
RequestId,
|
|
2026
|
+
RequireEntitlement,
|
|
1806
2027
|
Resource,
|
|
1807
2028
|
STORAGE_CONFIG_KIND,
|
|
2029
|
+
Spend,
|
|
1808
2030
|
Storage,
|
|
1809
2031
|
TEST_USERS_CONFIG_KIND,
|
|
1810
2032
|
TooManyRequests,
|
|
@@ -1837,6 +2059,7 @@ var import_zod2 = require("zod");
|
|
|
1837
2059
|
defineWebhook,
|
|
1838
2060
|
defineWorker,
|
|
1839
2061
|
documents,
|
|
2062
|
+
entitlementFor,
|
|
1840
2063
|
enumType,
|
|
1841
2064
|
flag,
|
|
1842
2065
|
getErrorRegistry,
|
|
@@ -1845,6 +2068,7 @@ var import_zod2 = require("zod");
|
|
|
1845
2068
|
isPalbaseExtension,
|
|
1846
2069
|
jsonb,
|
|
1847
2070
|
makeEnvDts,
|
|
2071
|
+
makePurchasesDts,
|
|
1848
2072
|
makeTypedDB,
|
|
1849
2073
|
numeric,
|
|
1850
2074
|
parseFileSizeLimit,
|
|
@@ -1852,6 +2076,7 @@ var import_zod2 = require("zod");
|
|
|
1852
2076
|
raw,
|
|
1853
2077
|
recordThrows,
|
|
1854
2078
|
reservedSecretKey,
|
|
2079
|
+
spendFor,
|
|
1855
2080
|
storage,
|
|
1856
2081
|
testUser,
|
|
1857
2082
|
text,
|