@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.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
Flags,
|
|
6
6
|
Log,
|
|
7
7
|
Notifications,
|
|
8
|
+
Purchases,
|
|
8
9
|
Queue,
|
|
9
10
|
Realtime,
|
|
10
11
|
Storage,
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
__requestALS,
|
|
13
14
|
__runWithRuntime,
|
|
14
15
|
__setRuntime
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-2PCNZBNZ.js";
|
|
16
17
|
import {
|
|
17
18
|
EXTENSION_DEPENDENCIES,
|
|
18
19
|
PALBASE_EXTENSIONS,
|
|
@@ -33,6 +34,276 @@ import {
|
|
|
33
34
|
uuid
|
|
34
35
|
} from "./chunk-4WOQWFUP.js";
|
|
35
36
|
|
|
37
|
+
// src/errors.ts
|
|
38
|
+
var HttpError = class extends Error {
|
|
39
|
+
status;
|
|
40
|
+
error;
|
|
41
|
+
errorDescription;
|
|
42
|
+
data;
|
|
43
|
+
constructor(status, error, errorDescription, data) {
|
|
44
|
+
super(errorDescription);
|
|
45
|
+
this.name = "HttpError";
|
|
46
|
+
this.status = status;
|
|
47
|
+
this.error = error;
|
|
48
|
+
this.errorDescription = errorDescription;
|
|
49
|
+
if (data !== void 0) {
|
|
50
|
+
this.data = data;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Serialize to the standard Palbase error response format.
|
|
55
|
+
* The `requestId` is injected by the runtime layer from the request context.
|
|
56
|
+
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
57
|
+
* When `data` is set, it is appended as a strict-superset field.
|
|
58
|
+
*/
|
|
59
|
+
toJSON(requestId) {
|
|
60
|
+
const result = {
|
|
61
|
+
error: this.error,
|
|
62
|
+
error_description: this.errorDescription,
|
|
63
|
+
status: this.status
|
|
64
|
+
};
|
|
65
|
+
if (requestId) {
|
|
66
|
+
result.request_id = requestId;
|
|
67
|
+
}
|
|
68
|
+
if (this.data !== void 0) {
|
|
69
|
+
result.data = this.data;
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var PalError = class extends HttpError {
|
|
75
|
+
constructor(status, code, description, data) {
|
|
76
|
+
super(status, code, description, data);
|
|
77
|
+
this.name = "PalError";
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var NamedHttpError = class extends HttpError {
|
|
81
|
+
constructor(status, defaultCode, name, message, code, data) {
|
|
82
|
+
super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
|
|
83
|
+
this.name = name;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
function defaultMessage(name) {
|
|
87
|
+
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
88
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
89
|
+
}
|
|
90
|
+
var BadRequest = class extends NamedHttpError {
|
|
91
|
+
constructor(data, message) {
|
|
92
|
+
super(400, "bad_request", "BadRequest", message, void 0, data);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var Unauthorized = class extends NamedHttpError {
|
|
96
|
+
constructor(message, code, data) {
|
|
97
|
+
super(401, "unauthorized", "Unauthorized", message, code, data);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var Forbidden = class extends NamedHttpError {
|
|
101
|
+
constructor(message, code, data) {
|
|
102
|
+
super(403, "forbidden", "Forbidden", message, code, data);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var NotFound = class extends NamedHttpError {
|
|
106
|
+
constructor(message, code, data) {
|
|
107
|
+
super(404, "not_found", "NotFound", message, code, data);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
var Conflict = class extends NamedHttpError {
|
|
111
|
+
constructor(message, code, data) {
|
|
112
|
+
super(409, "conflict", "Conflict", message, code, data);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var TooManyRequests = class extends NamedHttpError {
|
|
116
|
+
constructor(data, message) {
|
|
117
|
+
super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// src/purchases/errors.ts
|
|
122
|
+
function asPalstoreError(err) {
|
|
123
|
+
if (!(err instanceof Error)) return null;
|
|
124
|
+
const e = err;
|
|
125
|
+
if (typeof e.code !== "string" || typeof e.status !== "number") return null;
|
|
126
|
+
return { code: e.code, status: e.status, message: e.message };
|
|
127
|
+
}
|
|
128
|
+
function toHttpError(err) {
|
|
129
|
+
if (err instanceof HttpError) return err;
|
|
130
|
+
const p = asPalstoreError(err);
|
|
131
|
+
if (!p) return err;
|
|
132
|
+
switch (p.code) {
|
|
133
|
+
case "entitlement_required": {
|
|
134
|
+
const entitlement = err.entitlement ?? null;
|
|
135
|
+
return new PalError(403, "entitlement_required", p.message, { entitlement });
|
|
136
|
+
}
|
|
137
|
+
case "quota_exceeded":
|
|
138
|
+
case "credit_insufficient": {
|
|
139
|
+
const state = err.state;
|
|
140
|
+
return new PalError(429, p.code, p.message, state);
|
|
141
|
+
}
|
|
142
|
+
default:
|
|
143
|
+
return err;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/purchases/registry.ts
|
|
148
|
+
var ENTITLEMENTS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.entitlements");
|
|
149
|
+
var SPENDS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.spends");
|
|
150
|
+
function carrierOf(target) {
|
|
151
|
+
return typeof target === "function" ? target : target.constructor ?? target;
|
|
152
|
+
}
|
|
153
|
+
function own(carrier, slot) {
|
|
154
|
+
if (!Object.prototype.hasOwnProperty.call(carrier, slot)) {
|
|
155
|
+
carrier[slot] = {};
|
|
156
|
+
}
|
|
157
|
+
return carrier[slot];
|
|
158
|
+
}
|
|
159
|
+
function recordEntitlement(target, fnName, key) {
|
|
160
|
+
own(carrierOf(target), ENTITLEMENTS)[fnName] = key;
|
|
161
|
+
}
|
|
162
|
+
function recordSpend(target, fnName, meta) {
|
|
163
|
+
own(carrierOf(target), SPENDS)[fnName] = meta;
|
|
164
|
+
}
|
|
165
|
+
function entitlementFor(ctor, fnName) {
|
|
166
|
+
return carrierOf(ctor)[ENTITLEMENTS]?.[fnName];
|
|
167
|
+
}
|
|
168
|
+
function spendFor(ctor, fnName) {
|
|
169
|
+
return carrierOf(ctor)[SPENDS]?.[fnName];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/purchases/subject.ts
|
|
173
|
+
var subjectByRequest = /* @__PURE__ */ new WeakMap();
|
|
174
|
+
function storeEnv() {
|
|
175
|
+
return process.env.PALSTORE_STORE_ENV === "sandbox" ? "sandbox" : "production";
|
|
176
|
+
}
|
|
177
|
+
async function currentSubjectId() {
|
|
178
|
+
const store = __requestALS.getStore();
|
|
179
|
+
if (!store) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
"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."
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const memoized = subjectByRequest.get(store);
|
|
185
|
+
if (memoized) return memoized;
|
|
186
|
+
const userId = store.userId;
|
|
187
|
+
if (!userId) {
|
|
188
|
+
throw new Unauthorized(
|
|
189
|
+
"This endpoint requires an authenticated user: purchases entitlements and quota are per-user, and the subject is resolved from the request's identity."
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
const resolving = Purchases.resolveSubject({ userRef: userId, storeEnv: storeEnv() }).then(
|
|
193
|
+
(r) => r.subjectId
|
|
194
|
+
);
|
|
195
|
+
subjectByRequest.set(store, resolving);
|
|
196
|
+
resolving.catch(() => subjectByRequest.delete(store));
|
|
197
|
+
return resolving;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/purchases/decorators.ts
|
|
201
|
+
var WRAPPED = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.wrapped");
|
|
202
|
+
function idempotencyKeyFor(key) {
|
|
203
|
+
const store = __requestALS.getStore();
|
|
204
|
+
const base = store?.idempotencyKey || store?.requestId;
|
|
205
|
+
if (!base) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
"@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."
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return `${base}:${key}`;
|
|
211
|
+
}
|
|
212
|
+
function ensureWrapper(target, fnName, descriptor) {
|
|
213
|
+
const original = descriptor.value;
|
|
214
|
+
if (typeof original !== "function") {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`@RequireEntitlement/@Spend can only decorate a method; "${fnName}" is not one.`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
if (original[WRAPPED]) return;
|
|
220
|
+
const wrapper = async function(...args) {
|
|
221
|
+
const ctor = this.constructor;
|
|
222
|
+
const entitlement = entitlementFor(ctor, fnName);
|
|
223
|
+
const spend = spendFor(ctor, fnName);
|
|
224
|
+
let subjectId;
|
|
225
|
+
try {
|
|
226
|
+
subjectId = await currentSubjectId();
|
|
227
|
+
} catch (err) {
|
|
228
|
+
throw toHttpError(err);
|
|
229
|
+
}
|
|
230
|
+
if (entitlement !== void 0) {
|
|
231
|
+
try {
|
|
232
|
+
await Purchases.require(subjectId, entitlement);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
throw toHttpError(err);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (spend === void 0) {
|
|
238
|
+
return original.apply(this, args);
|
|
239
|
+
}
|
|
240
|
+
let handlerFailure;
|
|
241
|
+
try {
|
|
242
|
+
return await Purchases.withSpend(
|
|
243
|
+
subjectId,
|
|
244
|
+
spend.key,
|
|
245
|
+
{ count: spend.count, idempotencyKey: idempotencyKeyFor(spend.key) },
|
|
246
|
+
async () => {
|
|
247
|
+
try {
|
|
248
|
+
return await original.apply(this, args);
|
|
249
|
+
} catch (err) {
|
|
250
|
+
handlerFailure = { err };
|
|
251
|
+
throw err;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
} catch (err) {
|
|
256
|
+
if (handlerFailure !== void 0 && handlerFailure.err === err) throw err;
|
|
257
|
+
throw toHttpError(err);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
wrapper[WRAPPED] = true;
|
|
261
|
+
descriptor.value = wrapper;
|
|
262
|
+
}
|
|
263
|
+
function RequireEntitlement(key) {
|
|
264
|
+
return function(target, propertyKey, descriptor) {
|
|
265
|
+
const fnName = String(propertyKey);
|
|
266
|
+
recordEntitlement(target, fnName, key);
|
|
267
|
+
ensureWrapper(target, fnName, descriptor);
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function Spend(key) {
|
|
271
|
+
return function(target, propertyKey, descriptor) {
|
|
272
|
+
const fnName = String(propertyKey);
|
|
273
|
+
recordSpend(target, fnName, { key, count: 1 });
|
|
274
|
+
ensureWrapper(target, fnName, descriptor);
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/purchases/keys-gen.ts
|
|
279
|
+
function liveKeys(entries) {
|
|
280
|
+
return Object.entries(entries ?? {}).filter(([, def]) => def?.removed !== true).map(([key]) => key).sort();
|
|
281
|
+
}
|
|
282
|
+
function memberName(key) {
|
|
283
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
284
|
+
}
|
|
285
|
+
function members(keys) {
|
|
286
|
+
if (keys.length === 0) return "";
|
|
287
|
+
return `
|
|
288
|
+
${keys.map((k) => ` ${memberName(k)}: true;`).join("\n")}
|
|
289
|
+
`;
|
|
290
|
+
}
|
|
291
|
+
function makePurchasesDts(manifest) {
|
|
292
|
+
const entitlements = liveKeys(manifest.entitlements);
|
|
293
|
+
const limits = liveKeys({ ...manifest.limits, ...manifest.credits });
|
|
294
|
+
return `// palbase-purchases.d.ts \u2014 GENERATED by @palbase/backend. Do not edit.
|
|
295
|
+
// Source: the project's palstore catalog revision.
|
|
296
|
+
|
|
297
|
+
declare module "@palbase/backend/purchases" {
|
|
298
|
+
interface Entitlements {${members(entitlements)}}
|
|
299
|
+
|
|
300
|
+
interface Limits {${members(limits)}}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export {};
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
|
|
36
307
|
// src/db/env-gen.ts
|
|
37
308
|
function baseTsType(def) {
|
|
38
309
|
switch (def.type) {
|
|
@@ -142,7 +413,7 @@ function makeEnvDts(schema) {
|
|
|
142
413
|
${blocks.join("\n")}
|
|
143
414
|
` : "";
|
|
144
415
|
return `// AUTO-GENERATED by @palbase/backend \u2014 DO NOT EDIT.
|
|
145
|
-
// Regenerated from db/schema.ts on every \`palbase
|
|
416
|
+
// Regenerated from db/schema.ts on every \`palbase db types\` / deploy.
|
|
146
417
|
// Augments the @palbase/backend/env \`Tables\` interface so \`Database.tables.*\`
|
|
147
418
|
// is typed with no import and no generic.
|
|
148
419
|
|
|
@@ -592,7 +863,7 @@ var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
|
|
|
592
863
|
var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
|
|
593
864
|
var RETURN_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.returnBuffer");
|
|
594
865
|
var THROWS_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.throwsBuffer");
|
|
595
|
-
function
|
|
866
|
+
function carrierOf2(target) {
|
|
596
867
|
const ctor = typeof target === "function" ? target : target.constructor ?? target;
|
|
597
868
|
return ctor;
|
|
598
869
|
}
|
|
@@ -609,7 +880,7 @@ function ownParamBuffer(carrier) {
|
|
|
609
880
|
return carrier[PARAM_BUFFER];
|
|
610
881
|
}
|
|
611
882
|
function recordRoute(target, fnName, method, subpath, options) {
|
|
612
|
-
const carrier =
|
|
883
|
+
const carrier = carrierOf2(target);
|
|
613
884
|
const routes = ownRoutes(carrier);
|
|
614
885
|
const buffer = ownParamBuffer(carrier);
|
|
615
886
|
const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
@@ -625,7 +896,7 @@ function recordRoute(target, fnName, method, subpath, options) {
|
|
|
625
896
|
routes.push(route);
|
|
626
897
|
}
|
|
627
898
|
function recordParam(target, fnName, meta) {
|
|
628
|
-
const carrier =
|
|
899
|
+
const carrier = carrierOf2(target);
|
|
629
900
|
const buffer = ownParamBuffer(carrier);
|
|
630
901
|
(buffer[fnName] ??= []).push(meta);
|
|
631
902
|
const routes = carrier[ROUTES];
|
|
@@ -638,7 +909,7 @@ function recordParam(target, fnName, meta) {
|
|
|
638
909
|
}
|
|
639
910
|
}
|
|
640
911
|
function recordThrows(target, fnName, throws) {
|
|
641
|
-
const carrier =
|
|
912
|
+
const carrier = carrierOf2(target);
|
|
642
913
|
const routes = carrier[ROUTES];
|
|
643
914
|
const route = routes?.find((r) => r.fnName === fnName);
|
|
644
915
|
if (route) {
|
|
@@ -652,7 +923,7 @@ function recordThrows(target, fnName, throws) {
|
|
|
652
923
|
if (throwsBuffer) throwsBuffer[fnName] = throws;
|
|
653
924
|
}
|
|
654
925
|
function getRoutes(ctor) {
|
|
655
|
-
const carrier =
|
|
926
|
+
const carrier = carrierOf2(ctor);
|
|
656
927
|
const routes = carrier[ROUTES] ?? [];
|
|
657
928
|
const returnBuffer = carrier[RETURN_BUFFER];
|
|
658
929
|
if (returnBuffer) {
|
|
@@ -788,90 +1059,6 @@ function defineMiddleware(fn) {
|
|
|
788
1059
|
return fn;
|
|
789
1060
|
}
|
|
790
1061
|
|
|
791
|
-
// src/errors.ts
|
|
792
|
-
var HttpError = class extends Error {
|
|
793
|
-
status;
|
|
794
|
-
error;
|
|
795
|
-
errorDescription;
|
|
796
|
-
data;
|
|
797
|
-
constructor(status, error, errorDescription, data) {
|
|
798
|
-
super(errorDescription);
|
|
799
|
-
this.name = "HttpError";
|
|
800
|
-
this.status = status;
|
|
801
|
-
this.error = error;
|
|
802
|
-
this.errorDescription = errorDescription;
|
|
803
|
-
if (data !== void 0) {
|
|
804
|
-
this.data = data;
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* Serialize to the standard Palbase error response format.
|
|
809
|
-
* The `requestId` is injected by the runtime layer from the request context.
|
|
810
|
-
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
811
|
-
* When `data` is set, it is appended as a strict-superset field.
|
|
812
|
-
*/
|
|
813
|
-
toJSON(requestId) {
|
|
814
|
-
const result = {
|
|
815
|
-
error: this.error,
|
|
816
|
-
error_description: this.errorDescription,
|
|
817
|
-
status: this.status
|
|
818
|
-
};
|
|
819
|
-
if (requestId) {
|
|
820
|
-
result.request_id = requestId;
|
|
821
|
-
}
|
|
822
|
-
if (this.data !== void 0) {
|
|
823
|
-
result.data = this.data;
|
|
824
|
-
}
|
|
825
|
-
return result;
|
|
826
|
-
}
|
|
827
|
-
};
|
|
828
|
-
var PalError = class extends HttpError {
|
|
829
|
-
constructor(status, code, description, data) {
|
|
830
|
-
super(status, code, description, data);
|
|
831
|
-
this.name = "PalError";
|
|
832
|
-
}
|
|
833
|
-
};
|
|
834
|
-
var NamedHttpError = class extends HttpError {
|
|
835
|
-
constructor(status, defaultCode, name, message, code, data) {
|
|
836
|
-
super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
|
|
837
|
-
this.name = name;
|
|
838
|
-
}
|
|
839
|
-
};
|
|
840
|
-
function defaultMessage(name) {
|
|
841
|
-
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
842
|
-
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
843
|
-
}
|
|
844
|
-
var BadRequest = class extends NamedHttpError {
|
|
845
|
-
constructor(data, message) {
|
|
846
|
-
super(400, "bad_request", "BadRequest", message, void 0, data);
|
|
847
|
-
}
|
|
848
|
-
};
|
|
849
|
-
var Unauthorized = class extends NamedHttpError {
|
|
850
|
-
constructor(message, code, data) {
|
|
851
|
-
super(401, "unauthorized", "Unauthorized", message, code, data);
|
|
852
|
-
}
|
|
853
|
-
};
|
|
854
|
-
var Forbidden = class extends NamedHttpError {
|
|
855
|
-
constructor(message, code, data) {
|
|
856
|
-
super(403, "forbidden", "Forbidden", message, code, data);
|
|
857
|
-
}
|
|
858
|
-
};
|
|
859
|
-
var NotFound = class extends NamedHttpError {
|
|
860
|
-
constructor(message, code, data) {
|
|
861
|
-
super(404, "not_found", "NotFound", message, code, data);
|
|
862
|
-
}
|
|
863
|
-
};
|
|
864
|
-
var Conflict = class extends NamedHttpError {
|
|
865
|
-
constructor(message, code, data) {
|
|
866
|
-
super(409, "conflict", "Conflict", message, code, data);
|
|
867
|
-
}
|
|
868
|
-
};
|
|
869
|
-
var TooManyRequests = class extends NamedHttpError {
|
|
870
|
-
constructor(data, message) {
|
|
871
|
-
super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
|
|
872
|
-
}
|
|
873
|
-
};
|
|
874
|
-
|
|
875
1062
|
// src/error-registry.ts
|
|
876
1063
|
import {
|
|
877
1064
|
OpenAPIRegistry,
|
|
@@ -885,7 +1072,27 @@ var BUILTIN_DATA_SCHEMAS = {
|
|
|
885
1072
|
bad_request: z.object({
|
|
886
1073
|
fields: z.array(z.object({ field: z.string(), message: z.string() }))
|
|
887
1074
|
}),
|
|
888
|
-
too_many_requests: z.object({ retryAfter: z.number().int() })
|
|
1075
|
+
too_many_requests: z.object({ retryAfter: z.number().int() }),
|
|
1076
|
+
// Purchases (@RequireEntitlement / @Spend). Their payloads are what make a
|
|
1077
|
+
// paywall renderable on the client: WHICH entitlement was missing, and for a
|
|
1078
|
+
// spend the real ceiling and reset time rather than a guessed retry delay.
|
|
1079
|
+
entitlement_required: z.object({ entitlement: z.string().nullable() }),
|
|
1080
|
+
quota_exceeded: z.object({
|
|
1081
|
+
key: z.string(),
|
|
1082
|
+
scope: z.string(),
|
|
1083
|
+
window: z.string(),
|
|
1084
|
+
used: z.number().int(),
|
|
1085
|
+
reserved: z.number().int(),
|
|
1086
|
+
max: z.number().int(),
|
|
1087
|
+
remaining: z.number().int(),
|
|
1088
|
+
resetAt: z.string()
|
|
1089
|
+
}),
|
|
1090
|
+
credit_insufficient: z.object({
|
|
1091
|
+
key: z.string(),
|
|
1092
|
+
balance: z.number().int(),
|
|
1093
|
+
reserved: z.number().int(),
|
|
1094
|
+
remaining: z.number().int()
|
|
1095
|
+
})
|
|
889
1096
|
};
|
|
890
1097
|
function getErrorRegistry() {
|
|
891
1098
|
const g = globalThis;
|
|
@@ -897,7 +1104,13 @@ function getErrorRegistry() {
|
|
|
897
1104
|
["forbidden", 403, "Forbidden"],
|
|
898
1105
|
["not_found", 404, "NotFound"],
|
|
899
1106
|
["conflict", 409, "Conflict"],
|
|
900
|
-
["too_many_requests", 429, "TooManyRequests"]
|
|
1107
|
+
["too_many_requests", 429, "TooManyRequests"],
|
|
1108
|
+
// Thrown by the purchases decorators, so they are pre-seeded like the
|
|
1109
|
+
// other built-ins: a project never declares them, but every gated or
|
|
1110
|
+
// metered route must surface them as TYPED errors on the client.
|
|
1111
|
+
["entitlement_required", 403, "EntitlementRequired"],
|
|
1112
|
+
["quota_exceeded", 429, "QuotaExceeded"],
|
|
1113
|
+
["credit_insufficient", 429, "CreditInsufficient"]
|
|
901
1114
|
]) {
|
|
902
1115
|
const dataSchema = BUILTIN_DATA_SCHEMAS[code];
|
|
903
1116
|
m.set(code, {
|
|
@@ -1309,6 +1522,7 @@ export {
|
|
|
1309
1522
|
Patch,
|
|
1310
1523
|
PolicyBuilder,
|
|
1311
1524
|
Post,
|
|
1525
|
+
Purchases,
|
|
1312
1526
|
Put,
|
|
1313
1527
|
Query,
|
|
1314
1528
|
QueryParams,
|
|
@@ -1317,8 +1531,10 @@ export {
|
|
|
1317
1531
|
Realtime,
|
|
1318
1532
|
Req,
|
|
1319
1533
|
RequestId,
|
|
1534
|
+
RequireEntitlement,
|
|
1320
1535
|
Resource,
|
|
1321
1536
|
STORAGE_CONFIG_KIND,
|
|
1537
|
+
Spend,
|
|
1322
1538
|
Storage,
|
|
1323
1539
|
TEST_USERS_CONFIG_KIND,
|
|
1324
1540
|
TooManyRequests,
|
|
@@ -1351,6 +1567,7 @@ export {
|
|
|
1351
1567
|
defineWebhook,
|
|
1352
1568
|
defineWorker,
|
|
1353
1569
|
documents,
|
|
1570
|
+
entitlementFor,
|
|
1354
1571
|
enumType,
|
|
1355
1572
|
flag,
|
|
1356
1573
|
getErrorRegistry,
|
|
@@ -1359,6 +1576,7 @@ export {
|
|
|
1359
1576
|
isPalbaseExtension,
|
|
1360
1577
|
jsonb,
|
|
1361
1578
|
makeEnvDts,
|
|
1579
|
+
makePurchasesDts,
|
|
1362
1580
|
makeTypedDB,
|
|
1363
1581
|
numeric,
|
|
1364
1582
|
parseFileSizeLimit,
|
|
@@ -1366,6 +1584,7 @@ export {
|
|
|
1366
1584
|
raw,
|
|
1367
1585
|
recordThrows,
|
|
1368
1586
|
reservedSecretKey,
|
|
1587
|
+
spendFor,
|
|
1369
1588
|
storage,
|
|
1370
1589
|
testUser,
|
|
1371
1590
|
text,
|