@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.
- package/dist/chunk-7LAXRLPG.js +418 -0
- package/dist/chunk-7LAXRLPG.js.map +1 -0
- package/dist/{chunk-4WOQWFUP.js → chunk-LUV36KQU.js} +26 -11
- package/dist/{chunk-4WOQWFUP.js.map → chunk-LUV36KQU.js.map} +1 -1
- package/dist/{chunk-RGQUB66H.js → chunk-XATG7BRC.js} +22 -2
- package/dist/chunk-XATG7BRC.js.map +1 -0
- package/dist/db/index.cjs +438 -10
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.js +13 -1
- package/dist/{endpoint-Cn3ICGTf.d.cts → endpoint-Ck4hER_7.d.cts} +397 -11
- package/dist/{endpoint-Cn3ICGTf.d.ts → endpoint-Ck4hER_7.d.ts} +397 -11
- package/dist/{index-Bt7UHkAM.d.cts → index-BJAf1uPC.d.cts} +64 -34
- package/dist/{index-V0ealeY-.d.ts → index-l7DhBDtn.d.ts} +64 -34
- package/dist/index.cjs +776 -108
- 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 +325 -94
- 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 +559 -13
- 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 +166 -13
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +7 -6
- package/docs/database.md +106 -16
- package/docs/getting-started.md +14 -12
- package/docs/llms-full.txt +140 -45
- package/docs/migrations.md +8 -8
- package/docs/schema.md +6 -4
- 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-XATG7BRC.js";
|
|
16
17
|
import {
|
|
17
18
|
EXTENSION_DEPENDENCIES,
|
|
18
19
|
PALBASE_EXTENSIONS,
|
|
@@ -31,7 +32,284 @@ import {
|
|
|
31
32
|
text,
|
|
32
33
|
timestamp,
|
|
33
34
|
uuid
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-LUV36KQU.js";
|
|
36
|
+
import {
|
|
37
|
+
TxPlanError,
|
|
38
|
+
TxRefError,
|
|
39
|
+
dec,
|
|
40
|
+
inc,
|
|
41
|
+
now
|
|
42
|
+
} from "./chunk-7LAXRLPG.js";
|
|
43
|
+
|
|
44
|
+
// src/errors.ts
|
|
45
|
+
var HttpError = class extends Error {
|
|
46
|
+
status;
|
|
47
|
+
error;
|
|
48
|
+
errorDescription;
|
|
49
|
+
data;
|
|
50
|
+
constructor(status, error, errorDescription, data) {
|
|
51
|
+
super(errorDescription);
|
|
52
|
+
this.name = "HttpError";
|
|
53
|
+
this.status = status;
|
|
54
|
+
this.error = error;
|
|
55
|
+
this.errorDescription = errorDescription;
|
|
56
|
+
if (data !== void 0) {
|
|
57
|
+
this.data = data;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Serialize to the standard Palbase error response format.
|
|
62
|
+
* The `requestId` is injected by the runtime layer from the request context.
|
|
63
|
+
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
64
|
+
* When `data` is set, it is appended as a strict-superset field.
|
|
65
|
+
*/
|
|
66
|
+
toJSON(requestId) {
|
|
67
|
+
const result = {
|
|
68
|
+
error: this.error,
|
|
69
|
+
error_description: this.errorDescription,
|
|
70
|
+
status: this.status
|
|
71
|
+
};
|
|
72
|
+
if (requestId) {
|
|
73
|
+
result.request_id = requestId;
|
|
74
|
+
}
|
|
75
|
+
if (this.data !== void 0) {
|
|
76
|
+
result.data = this.data;
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var PalError = class extends HttpError {
|
|
82
|
+
constructor(status, code, description, data) {
|
|
83
|
+
super(status, code, description, data);
|
|
84
|
+
this.name = "PalError";
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var NamedHttpError = class extends HttpError {
|
|
88
|
+
constructor(status, defaultCode, name, message, code, data) {
|
|
89
|
+
super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
|
|
90
|
+
this.name = name;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
function defaultMessage(name) {
|
|
94
|
+
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
95
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
96
|
+
}
|
|
97
|
+
var BadRequest = class extends NamedHttpError {
|
|
98
|
+
constructor(data, message) {
|
|
99
|
+
super(400, "bad_request", "BadRequest", message, void 0, data);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var Unauthorized = class extends NamedHttpError {
|
|
103
|
+
constructor(message, code, data) {
|
|
104
|
+
super(401, "unauthorized", "Unauthorized", message, code, data);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var Forbidden = class extends NamedHttpError {
|
|
108
|
+
constructor(message, code, data) {
|
|
109
|
+
super(403, "forbidden", "Forbidden", message, code, data);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var NotFound = class extends NamedHttpError {
|
|
113
|
+
constructor(message, code, data) {
|
|
114
|
+
super(404, "not_found", "NotFound", message, code, data);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var Conflict = class extends NamedHttpError {
|
|
118
|
+
constructor(message, code, data) {
|
|
119
|
+
super(409, "conflict", "Conflict", message, code, data);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
var TooManyRequests = class extends NamedHttpError {
|
|
123
|
+
constructor(data, message) {
|
|
124
|
+
super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// src/purchases/errors.ts
|
|
129
|
+
function asPalstoreError(err) {
|
|
130
|
+
if (!(err instanceof Error)) return null;
|
|
131
|
+
const e = err;
|
|
132
|
+
if (typeof e.code !== "string" || typeof e.status !== "number") return null;
|
|
133
|
+
return { code: e.code, status: e.status, message: e.message };
|
|
134
|
+
}
|
|
135
|
+
function toHttpError(err) {
|
|
136
|
+
if (err instanceof HttpError) return err;
|
|
137
|
+
const p = asPalstoreError(err);
|
|
138
|
+
if (!p) return err;
|
|
139
|
+
switch (p.code) {
|
|
140
|
+
case "entitlement_required": {
|
|
141
|
+
const entitlement = err.entitlement ?? null;
|
|
142
|
+
return new PalError(403, "entitlement_required", p.message, { entitlement });
|
|
143
|
+
}
|
|
144
|
+
case "quota_exceeded":
|
|
145
|
+
case "credit_insufficient": {
|
|
146
|
+
const state = err.state;
|
|
147
|
+
return new PalError(429, p.code, p.message, state);
|
|
148
|
+
}
|
|
149
|
+
default:
|
|
150
|
+
return err;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/purchases/registry.ts
|
|
155
|
+
var ENTITLEMENTS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.entitlements");
|
|
156
|
+
var SPENDS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.spends");
|
|
157
|
+
function carrierOf(target) {
|
|
158
|
+
return typeof target === "function" ? target : target.constructor ?? target;
|
|
159
|
+
}
|
|
160
|
+
function own(carrier, slot) {
|
|
161
|
+
if (!Object.prototype.hasOwnProperty.call(carrier, slot)) {
|
|
162
|
+
carrier[slot] = {};
|
|
163
|
+
}
|
|
164
|
+
return carrier[slot];
|
|
165
|
+
}
|
|
166
|
+
function recordEntitlement(target, fnName, key) {
|
|
167
|
+
own(carrierOf(target), ENTITLEMENTS)[fnName] = key;
|
|
168
|
+
}
|
|
169
|
+
function recordSpend(target, fnName, meta) {
|
|
170
|
+
own(carrierOf(target), SPENDS)[fnName] = meta;
|
|
171
|
+
}
|
|
172
|
+
function entitlementFor(ctor, fnName) {
|
|
173
|
+
return carrierOf(ctor)[ENTITLEMENTS]?.[fnName];
|
|
174
|
+
}
|
|
175
|
+
function spendFor(ctor, fnName) {
|
|
176
|
+
return carrierOf(ctor)[SPENDS]?.[fnName];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/purchases/subject.ts
|
|
180
|
+
var subjectByRequest = /* @__PURE__ */ new WeakMap();
|
|
181
|
+
function storeEnv() {
|
|
182
|
+
return process.env.PALSTORE_STORE_ENV === "sandbox" ? "sandbox" : "production";
|
|
183
|
+
}
|
|
184
|
+
async function currentSubjectId() {
|
|
185
|
+
const store = __requestALS.getStore();
|
|
186
|
+
if (!store) {
|
|
187
|
+
throw new Error(
|
|
188
|
+
"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."
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
const memoized = subjectByRequest.get(store);
|
|
192
|
+
if (memoized) return memoized;
|
|
193
|
+
const userId = store.userId;
|
|
194
|
+
if (!userId) {
|
|
195
|
+
throw new Unauthorized(
|
|
196
|
+
"This endpoint requires an authenticated user: purchases entitlements and quota are per-user, and the subject is resolved from the request's identity."
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const resolving = Purchases.resolveSubject({ userRef: userId, storeEnv: storeEnv() }).then(
|
|
200
|
+
(r) => r.subjectId
|
|
201
|
+
);
|
|
202
|
+
subjectByRequest.set(store, resolving);
|
|
203
|
+
resolving.catch(() => subjectByRequest.delete(store));
|
|
204
|
+
return resolving;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/purchases/decorators.ts
|
|
208
|
+
var WRAPPED = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.wrapped");
|
|
209
|
+
function idempotencyKeyFor(key) {
|
|
210
|
+
const store = __requestALS.getStore();
|
|
211
|
+
const base = store?.idempotencyKey || store?.requestId;
|
|
212
|
+
if (!base) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
"@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."
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
return `${base}:${key}`;
|
|
218
|
+
}
|
|
219
|
+
function ensureWrapper(target, fnName, descriptor) {
|
|
220
|
+
const original = descriptor.value;
|
|
221
|
+
if (typeof original !== "function") {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`@RequireEntitlement/@Spend can only decorate a method; "${fnName}" is not one.`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
if (original[WRAPPED]) return;
|
|
227
|
+
const wrapper = async function(...args) {
|
|
228
|
+
const ctor = this.constructor;
|
|
229
|
+
const entitlement = entitlementFor(ctor, fnName);
|
|
230
|
+
const spend = spendFor(ctor, fnName);
|
|
231
|
+
let subjectId;
|
|
232
|
+
try {
|
|
233
|
+
subjectId = await currentSubjectId();
|
|
234
|
+
} catch (err) {
|
|
235
|
+
throw toHttpError(err);
|
|
236
|
+
}
|
|
237
|
+
if (entitlement !== void 0) {
|
|
238
|
+
try {
|
|
239
|
+
await Purchases.require(subjectId, entitlement);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
throw toHttpError(err);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (spend === void 0) {
|
|
245
|
+
return original.apply(this, args);
|
|
246
|
+
}
|
|
247
|
+
let handlerFailure;
|
|
248
|
+
try {
|
|
249
|
+
return await Purchases.withSpend(
|
|
250
|
+
subjectId,
|
|
251
|
+
spend.key,
|
|
252
|
+
{ count: spend.count, idempotencyKey: idempotencyKeyFor(spend.key) },
|
|
253
|
+
async () => {
|
|
254
|
+
try {
|
|
255
|
+
return await original.apply(this, args);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
handlerFailure = { err };
|
|
258
|
+
throw err;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
} catch (err) {
|
|
263
|
+
if (handlerFailure !== void 0 && handlerFailure.err === err) throw err;
|
|
264
|
+
throw toHttpError(err);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
wrapper[WRAPPED] = true;
|
|
268
|
+
descriptor.value = wrapper;
|
|
269
|
+
}
|
|
270
|
+
function RequireEntitlement(key) {
|
|
271
|
+
return function(target, propertyKey, descriptor) {
|
|
272
|
+
const fnName = String(propertyKey);
|
|
273
|
+
recordEntitlement(target, fnName, key);
|
|
274
|
+
ensureWrapper(target, fnName, descriptor);
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function Spend(key) {
|
|
278
|
+
return function(target, propertyKey, descriptor) {
|
|
279
|
+
const fnName = String(propertyKey);
|
|
280
|
+
recordSpend(target, fnName, { key, count: 1 });
|
|
281
|
+
ensureWrapper(target, fnName, descriptor);
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/purchases/keys-gen.ts
|
|
286
|
+
function liveKeys(entries) {
|
|
287
|
+
return Object.entries(entries ?? {}).filter(([, def]) => def?.removed !== true).map(([key]) => key).sort();
|
|
288
|
+
}
|
|
289
|
+
function memberName(key) {
|
|
290
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
291
|
+
}
|
|
292
|
+
function members(keys) {
|
|
293
|
+
if (keys.length === 0) return "";
|
|
294
|
+
return `
|
|
295
|
+
${keys.map((k) => ` ${memberName(k)}: true;`).join("\n")}
|
|
296
|
+
`;
|
|
297
|
+
}
|
|
298
|
+
function makePurchasesDts(manifest) {
|
|
299
|
+
const entitlements = liveKeys(manifest.entitlements);
|
|
300
|
+
const limits = liveKeys({ ...manifest.limits, ...manifest.credits });
|
|
301
|
+
return `// palbase-purchases.d.ts \u2014 GENERATED by @palbase/backend. Do not edit.
|
|
302
|
+
// Source: the project's palstore catalog revision.
|
|
303
|
+
|
|
304
|
+
declare module "@palbase/backend/purchases" {
|
|
305
|
+
interface Entitlements {${members(entitlements)}}
|
|
306
|
+
|
|
307
|
+
interface Limits {${members(limits)}}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export {};
|
|
311
|
+
`;
|
|
312
|
+
}
|
|
35
313
|
|
|
36
314
|
// src/db/env-gen.ts
|
|
37
315
|
function baseTsType(def) {
|
|
@@ -142,7 +420,7 @@ function makeEnvDts(schema) {
|
|
|
142
420
|
${blocks.join("\n")}
|
|
143
421
|
` : "";
|
|
144
422
|
return `// AUTO-GENERATED by @palbase/backend \u2014 DO NOT EDIT.
|
|
145
|
-
// Regenerated from db/schema.ts on every \`palbase
|
|
423
|
+
// Regenerated from db/schema.ts on every \`palbase db types\` / deploy.
|
|
146
424
|
// Augments the @palbase/backend/env \`Tables\` interface so \`Database.tables.*\`
|
|
147
425
|
// is typed with no import and no generic.
|
|
148
426
|
|
|
@@ -592,7 +870,7 @@ var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
|
|
|
592
870
|
var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
|
|
593
871
|
var RETURN_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.returnBuffer");
|
|
594
872
|
var THROWS_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.throwsBuffer");
|
|
595
|
-
function
|
|
873
|
+
function carrierOf2(target) {
|
|
596
874
|
const ctor = typeof target === "function" ? target : target.constructor ?? target;
|
|
597
875
|
return ctor;
|
|
598
876
|
}
|
|
@@ -609,7 +887,7 @@ function ownParamBuffer(carrier) {
|
|
|
609
887
|
return carrier[PARAM_BUFFER];
|
|
610
888
|
}
|
|
611
889
|
function recordRoute(target, fnName, method, subpath, options) {
|
|
612
|
-
const carrier =
|
|
890
|
+
const carrier = carrierOf2(target);
|
|
613
891
|
const routes = ownRoutes(carrier);
|
|
614
892
|
const buffer = ownParamBuffer(carrier);
|
|
615
893
|
const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
@@ -625,7 +903,7 @@ function recordRoute(target, fnName, method, subpath, options) {
|
|
|
625
903
|
routes.push(route);
|
|
626
904
|
}
|
|
627
905
|
function recordParam(target, fnName, meta) {
|
|
628
|
-
const carrier =
|
|
906
|
+
const carrier = carrierOf2(target);
|
|
629
907
|
const buffer = ownParamBuffer(carrier);
|
|
630
908
|
(buffer[fnName] ??= []).push(meta);
|
|
631
909
|
const routes = carrier[ROUTES];
|
|
@@ -638,7 +916,7 @@ function recordParam(target, fnName, meta) {
|
|
|
638
916
|
}
|
|
639
917
|
}
|
|
640
918
|
function recordThrows(target, fnName, throws) {
|
|
641
|
-
const carrier =
|
|
919
|
+
const carrier = carrierOf2(target);
|
|
642
920
|
const routes = carrier[ROUTES];
|
|
643
921
|
const route = routes?.find((r) => r.fnName === fnName);
|
|
644
922
|
if (route) {
|
|
@@ -652,7 +930,7 @@ function recordThrows(target, fnName, throws) {
|
|
|
652
930
|
if (throwsBuffer) throwsBuffer[fnName] = throws;
|
|
653
931
|
}
|
|
654
932
|
function getRoutes(ctor) {
|
|
655
|
-
const carrier =
|
|
933
|
+
const carrier = carrierOf2(ctor);
|
|
656
934
|
const routes = carrier[ROUTES] ?? [];
|
|
657
935
|
const returnBuffer = carrier[RETURN_BUFFER];
|
|
658
936
|
if (returnBuffer) {
|
|
@@ -788,90 +1066,6 @@ function defineMiddleware(fn) {
|
|
|
788
1066
|
return fn;
|
|
789
1067
|
}
|
|
790
1068
|
|
|
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
1069
|
// src/error-registry.ts
|
|
876
1070
|
import {
|
|
877
1071
|
OpenAPIRegistry,
|
|
@@ -885,7 +1079,27 @@ var BUILTIN_DATA_SCHEMAS = {
|
|
|
885
1079
|
bad_request: z.object({
|
|
886
1080
|
fields: z.array(z.object({ field: z.string(), message: z.string() }))
|
|
887
1081
|
}),
|
|
888
|
-
too_many_requests: z.object({ retryAfter: z.number().int() })
|
|
1082
|
+
too_many_requests: z.object({ retryAfter: z.number().int() }),
|
|
1083
|
+
// Purchases (@RequireEntitlement / @Spend). Their payloads are what make a
|
|
1084
|
+
// paywall renderable on the client: WHICH entitlement was missing, and for a
|
|
1085
|
+
// spend the real ceiling and reset time rather than a guessed retry delay.
|
|
1086
|
+
entitlement_required: z.object({ entitlement: z.string().nullable() }),
|
|
1087
|
+
quota_exceeded: z.object({
|
|
1088
|
+
key: z.string(),
|
|
1089
|
+
scope: z.string(),
|
|
1090
|
+
window: z.string(),
|
|
1091
|
+
used: z.number().int(),
|
|
1092
|
+
reserved: z.number().int(),
|
|
1093
|
+
max: z.number().int(),
|
|
1094
|
+
remaining: z.number().int(),
|
|
1095
|
+
resetAt: z.string()
|
|
1096
|
+
}),
|
|
1097
|
+
credit_insufficient: z.object({
|
|
1098
|
+
key: z.string(),
|
|
1099
|
+
balance: z.number().int(),
|
|
1100
|
+
reserved: z.number().int(),
|
|
1101
|
+
remaining: z.number().int()
|
|
1102
|
+
})
|
|
889
1103
|
};
|
|
890
1104
|
function getErrorRegistry() {
|
|
891
1105
|
const g = globalThis;
|
|
@@ -897,7 +1111,13 @@ function getErrorRegistry() {
|
|
|
897
1111
|
["forbidden", 403, "Forbidden"],
|
|
898
1112
|
["not_found", 404, "NotFound"],
|
|
899
1113
|
["conflict", 409, "Conflict"],
|
|
900
|
-
["too_many_requests", 429, "TooManyRequests"]
|
|
1114
|
+
["too_many_requests", 429, "TooManyRequests"],
|
|
1115
|
+
// Thrown by the purchases decorators, so they are pre-seeded like the
|
|
1116
|
+
// other built-ins: a project never declares them, but every gated or
|
|
1117
|
+
// metered route must surface them as TYPED errors on the client.
|
|
1118
|
+
["entitlement_required", 403, "EntitlementRequired"],
|
|
1119
|
+
["quota_exceeded", 429, "QuotaExceeded"],
|
|
1120
|
+
["credit_insufficient", 429, "CreditInsufficient"]
|
|
901
1121
|
]) {
|
|
902
1122
|
const dataSchema = BUILTIN_DATA_SCHEMAS[code];
|
|
903
1123
|
m.set(code, {
|
|
@@ -1309,6 +1529,7 @@ export {
|
|
|
1309
1529
|
Patch,
|
|
1310
1530
|
PolicyBuilder,
|
|
1311
1531
|
Post,
|
|
1532
|
+
Purchases,
|
|
1312
1533
|
Put,
|
|
1313
1534
|
Query,
|
|
1314
1535
|
QueryParams,
|
|
@@ -1317,12 +1538,16 @@ export {
|
|
|
1317
1538
|
Realtime,
|
|
1318
1539
|
Req,
|
|
1319
1540
|
RequestId,
|
|
1541
|
+
RequireEntitlement,
|
|
1320
1542
|
Resource,
|
|
1321
1543
|
STORAGE_CONFIG_KIND,
|
|
1544
|
+
Spend,
|
|
1322
1545
|
Storage,
|
|
1323
1546
|
TEST_USERS_CONFIG_KIND,
|
|
1324
1547
|
TooManyRequests,
|
|
1325
1548
|
TraceId,
|
|
1549
|
+
TxPlanError,
|
|
1550
|
+
TxRefError,
|
|
1326
1551
|
Unauthorized,
|
|
1327
1552
|
Upload,
|
|
1328
1553
|
UploadedObject,
|
|
@@ -1339,6 +1564,7 @@ export {
|
|
|
1339
1564
|
boolean,
|
|
1340
1565
|
bucket,
|
|
1341
1566
|
buildProvider,
|
|
1567
|
+
dec,
|
|
1342
1568
|
defineEgress,
|
|
1343
1569
|
defineError,
|
|
1344
1570
|
defineFlags,
|
|
@@ -1351,21 +1577,26 @@ export {
|
|
|
1351
1577
|
defineWebhook,
|
|
1352
1578
|
defineWorker,
|
|
1353
1579
|
documents,
|
|
1580
|
+
entitlementFor,
|
|
1354
1581
|
enumType,
|
|
1355
1582
|
flag,
|
|
1356
1583
|
getErrorRegistry,
|
|
1357
1584
|
getRoutes,
|
|
1585
|
+
inc,
|
|
1358
1586
|
integer,
|
|
1359
1587
|
isPalbaseExtension,
|
|
1360
1588
|
jsonb,
|
|
1361
1589
|
makeEnvDts,
|
|
1590
|
+
makePurchasesDts,
|
|
1362
1591
|
makeTypedDB,
|
|
1592
|
+
now,
|
|
1363
1593
|
numeric,
|
|
1364
1594
|
parseFileSizeLimit,
|
|
1365
1595
|
policy,
|
|
1366
1596
|
raw,
|
|
1367
1597
|
recordThrows,
|
|
1368
1598
|
reservedSecretKey,
|
|
1599
|
+
spendFor,
|
|
1369
1600
|
storage,
|
|
1370
1601
|
testUser,
|
|
1371
1602
|
text,
|