@palbase/backend 11.0.0 → 12.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/index.cjs +130 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -158
- package/dist/index.d.ts +56 -158
- package/dist/index.js +125 -106
- package/dist/index.js.map +1 -1
- package/docs/background.md +8 -9
- package/docs/events.md +20 -17
- package/docs/llms-full.txt +29 -27
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -37,10 +37,12 @@ __export(src_exports, {
|
|
|
37
37
|
Get: () => Get,
|
|
38
38
|
Headers: () => Headers,
|
|
39
39
|
HttpError: () => HttpError,
|
|
40
|
+
Job: () => Job,
|
|
40
41
|
Log: () => Log,
|
|
41
42
|
NOTIFICATIONS_CONFIG_KIND: () => NOTIFICATIONS_CONFIG_KIND,
|
|
42
43
|
NotFound: () => NotFound,
|
|
43
44
|
Notifications: () => Notifications,
|
|
45
|
+
On: () => On,
|
|
44
46
|
OptionalUser: () => OptionalUser,
|
|
45
47
|
PALBASE_EXTENSIONS: () => PALBASE_EXTENSIONS,
|
|
46
48
|
PROVIDER_CATALOG: () => PROVIDER_CATALOG,
|
|
@@ -72,6 +74,7 @@ __export(src_exports, {
|
|
|
72
74
|
Upload: () => Upload,
|
|
73
75
|
UploadedObject: () => UploadedObject,
|
|
74
76
|
User: () => User,
|
|
77
|
+
Webhook: () => Webhook,
|
|
75
78
|
__getRuntime: () => __getRuntime,
|
|
76
79
|
__registerResource: () => __registerResource,
|
|
77
80
|
__requestALS: () => __requestALS,
|
|
@@ -88,20 +91,20 @@ __export(src_exports, {
|
|
|
88
91
|
defineEgress: () => defineEgress,
|
|
89
92
|
defineError: () => defineError,
|
|
90
93
|
defineFlags: () => defineFlags,
|
|
91
|
-
defineJob: () => defineJob,
|
|
92
94
|
defineMiddleware: () => defineMiddleware,
|
|
93
95
|
defineNotifications: () => defineNotifications,
|
|
94
96
|
defineSchema: () => defineSchema,
|
|
95
97
|
defineStorage: () => defineStorage,
|
|
96
98
|
defineTestUsers: () => defineTestUsers,
|
|
97
|
-
defineWebhook: () => defineWebhook,
|
|
98
99
|
defineWorker: () => defineWorker,
|
|
99
100
|
documents: () => documents,
|
|
100
101
|
entitlementFor: () => entitlementFor,
|
|
101
102
|
enumType: () => enumType,
|
|
102
103
|
flag: () => flag,
|
|
103
104
|
getErrorRegistry: () => getErrorRegistry,
|
|
105
|
+
getJobConfig: () => getJobConfig,
|
|
104
106
|
getRoutes: () => getRoutes,
|
|
107
|
+
getWebhookConfig: () => getWebhookConfig,
|
|
105
108
|
inc: () => inc,
|
|
106
109
|
integer: () => integer,
|
|
107
110
|
isPalbaseExtension: () => isPalbaseExtension,
|
|
@@ -1769,6 +1772,12 @@ function defineFlags(input) {
|
|
|
1769
1772
|
var CONTROLLER_META = /* @__PURE__ */ Symbol.for("palbase.backend.controllerMeta");
|
|
1770
1773
|
function Controller(basePath, options = {}) {
|
|
1771
1774
|
return function(ctor) {
|
|
1775
|
+
const normalized = basePath.replace(/\/+$/, "");
|
|
1776
|
+
if (normalized === "/webhooks" || normalized.startsWith("/webhooks/")) {
|
|
1777
|
+
throw new Error(
|
|
1778
|
+
`@Controller("${basePath}") uses the reserved /webhooks path \u2014 inbound webhooks are served there`
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1772
1781
|
const carrier = ctor;
|
|
1773
1782
|
const meta = {
|
|
1774
1783
|
__palbase: "controller",
|
|
@@ -2156,12 +2165,81 @@ function defineWorker(config) {
|
|
|
2156
2165
|
};
|
|
2157
2166
|
}
|
|
2158
2167
|
|
|
2168
|
+
// src/decorators/webhook.ts
|
|
2169
|
+
var WEBHOOK_META = /* @__PURE__ */ Symbol.for("palbase.backend.webhookMeta");
|
|
2170
|
+
var WEBHOOK_EVENTS = /* @__PURE__ */ Symbol.for("palbase.backend.webhookEvents");
|
|
2171
|
+
function carrierOf3(ctor) {
|
|
2172
|
+
return ctor;
|
|
2173
|
+
}
|
|
2174
|
+
function Webhook(options) {
|
|
2175
|
+
return function(ctor) {
|
|
2176
|
+
const carrier = carrierOf3(ctor);
|
|
2177
|
+
Object.defineProperty(carrier, WEBHOOK_META, {
|
|
2178
|
+
value: options,
|
|
2179
|
+
enumerable: false,
|
|
2180
|
+
configurable: true,
|
|
2181
|
+
writable: false
|
|
2182
|
+
});
|
|
2183
|
+
Object.defineProperty(carrier, "__palbase", {
|
|
2184
|
+
value: "webhook",
|
|
2185
|
+
enumerable: false,
|
|
2186
|
+
configurable: true,
|
|
2187
|
+
writable: false
|
|
2188
|
+
});
|
|
2189
|
+
return ctor;
|
|
2190
|
+
};
|
|
2191
|
+
}
|
|
2192
|
+
function On(event) {
|
|
2193
|
+
return function(target, fnName) {
|
|
2194
|
+
const carrier = carrierOf3(target.constructor);
|
|
2195
|
+
const existing = carrier[WEBHOOK_EVENTS];
|
|
2196
|
+
const entries = existing ? [...existing] : [];
|
|
2197
|
+
entries.push({ event, fnName: String(fnName) });
|
|
2198
|
+
Object.defineProperty(carrier, WEBHOOK_EVENTS, {
|
|
2199
|
+
value: entries,
|
|
2200
|
+
enumerable: false,
|
|
2201
|
+
configurable: true,
|
|
2202
|
+
writable: false
|
|
2203
|
+
});
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
function getWebhookConfig(ctor) {
|
|
2207
|
+
const carrier = carrierOf3(ctor);
|
|
2208
|
+
const meta = carrier[WEBHOOK_META];
|
|
2209
|
+
const entries = carrier[WEBHOOK_EVENTS] ?? [];
|
|
2210
|
+
if (!meta) {
|
|
2211
|
+
throw new Error(
|
|
2212
|
+
`@On used on a class that is not decorated with @Webhook (${ctor.name ?? "anonymous"})`
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
2215
|
+
if (!meta.provider && !meta.signature) {
|
|
2216
|
+
throw new Error(
|
|
2217
|
+
"@Webhook requires either a `provider` preset or an explicit `signature` \u2014 an endpoint with no verification would accept forged deliveries"
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
2220
|
+
if (!meta.secret?.env) {
|
|
2221
|
+
throw new Error('@Webhook requires `secret: { env: "VAR_NAME" }`');
|
|
2222
|
+
}
|
|
2223
|
+
if (entries.length === 0) {
|
|
2224
|
+
throw new Error("@Webhook requires at least one @On handler");
|
|
2225
|
+
}
|
|
2226
|
+
const instance = new ctor();
|
|
2227
|
+
const events = /* @__PURE__ */ Object.create(null);
|
|
2228
|
+
for (const entry of entries) {
|
|
2229
|
+
if (Object.prototype.hasOwnProperty.call(events, entry.event)) {
|
|
2230
|
+
throw new Error(`@On("${entry.event}") declared twice on the same webhook`);
|
|
2231
|
+
}
|
|
2232
|
+
events[entry.event] = (event, metaArg) => instance[entry.fnName].call(instance, event, metaArg);
|
|
2233
|
+
}
|
|
2234
|
+
return {
|
|
2235
|
+
...meta.provider ? { provider: meta.provider } : {},
|
|
2236
|
+
...meta.signature ? { signature: meta.signature } : {},
|
|
2237
|
+
secret: meta.secret,
|
|
2238
|
+
events
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2159
2242
|
// src/job.ts
|
|
2160
|
-
var VALID_JOB_NAME = /^[a-zA-Z0-9_-]+$/;
|
|
2161
|
-
var MAX_TIMEOUT_SECONDS = 300;
|
|
2162
|
-
var JOB_DEFAULTS = {
|
|
2163
|
-
timeout: 30
|
|
2164
|
-
};
|
|
2165
2243
|
function validateCronExpression(expression) {
|
|
2166
2244
|
const trimmed = expression.trim();
|
|
2167
2245
|
if (trimmed === "") {
|
|
@@ -2227,115 +2305,56 @@ function validateCronField(field, name, min, max) {
|
|
|
2227
2305
|
}
|
|
2228
2306
|
return null;
|
|
2229
2307
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
}
|
|
2252
|
-
if (config.timeout !== void 0 && !Number.isInteger(config.timeout)) {
|
|
2253
|
-
throw new Error("Job timeout must be an integer");
|
|
2254
|
-
}
|
|
2255
|
-
if (config.timeout !== void 0 && config.timeout > MAX_TIMEOUT_SECONDS) {
|
|
2256
|
-
throw new Error(
|
|
2257
|
-
`Job timeout ${config.timeout}s exceeds maximum ${MAX_TIMEOUT_SECONDS}s`
|
|
2258
|
-
);
|
|
2259
|
-
}
|
|
2260
|
-
return {
|
|
2261
|
-
name: config.name,
|
|
2262
|
-
schedule: config.schedule.trim(),
|
|
2263
|
-
timeout: config.timeout ?? JOB_DEFAULTS.timeout,
|
|
2264
|
-
handler: config.handler
|
|
2308
|
+
|
|
2309
|
+
// src/decorators/job.ts
|
|
2310
|
+
var DEFAULT_TIMEOUT_SECONDS = 30;
|
|
2311
|
+
var MAX_TIMEOUT_SECONDS = 300;
|
|
2312
|
+
var JOB_META = /* @__PURE__ */ Symbol.for("palbase.backend.jobMeta");
|
|
2313
|
+
function Job(options) {
|
|
2314
|
+
return function(ctor) {
|
|
2315
|
+
const carrier = ctor;
|
|
2316
|
+
Object.defineProperty(carrier, JOB_META, {
|
|
2317
|
+
value: options,
|
|
2318
|
+
enumerable: false,
|
|
2319
|
+
configurable: true,
|
|
2320
|
+
writable: false
|
|
2321
|
+
});
|
|
2322
|
+
Object.defineProperty(carrier, "__palbase", {
|
|
2323
|
+
value: "job",
|
|
2324
|
+
enumerable: false,
|
|
2325
|
+
configurable: true,
|
|
2326
|
+
writable: false
|
|
2327
|
+
});
|
|
2328
|
+
return ctor;
|
|
2265
2329
|
};
|
|
2266
2330
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
function defineWebhook(config) {
|
|
2271
|
-
if ("provider" in config) {
|
|
2272
|
-
return validateProviderWebhook(config);
|
|
2273
|
-
}
|
|
2274
|
-
return validateCustomWebhook(config);
|
|
2275
|
-
}
|
|
2276
|
-
function validateProviderWebhook(config) {
|
|
2277
|
-
if (!config.provider) {
|
|
2278
|
-
throw new Error("Webhook provider is required");
|
|
2279
|
-
}
|
|
2280
|
-
const validProviders = [
|
|
2281
|
-
"stripe",
|
|
2282
|
-
"github",
|
|
2283
|
-
"twilio",
|
|
2284
|
-
"sendgrid",
|
|
2285
|
-
"slack",
|
|
2286
|
-
"discord",
|
|
2287
|
-
"livekit"
|
|
2288
|
-
];
|
|
2289
|
-
if (!validProviders.includes(config.provider)) {
|
|
2331
|
+
function getJobConfig(ctor) {
|
|
2332
|
+
const meta = ctor[JOB_META];
|
|
2333
|
+
if (!meta) {
|
|
2290
2334
|
throw new Error(
|
|
2291
|
-
`
|
|
2335
|
+
`getJobConfig on a class with no @Job decorator (${ctor.name ?? "anonymous"})`
|
|
2292
2336
|
);
|
|
2293
2337
|
}
|
|
2294
|
-
if (!
|
|
2295
|
-
throw new Error(
|
|
2338
|
+
if (!meta.schedule || meta.schedule.trim() === "") {
|
|
2339
|
+
throw new Error("@Job requires a `schedule` cron expression");
|
|
2296
2340
|
}
|
|
2297
|
-
|
|
2298
|
-
|
|
2341
|
+
const cronError = validateCronExpression(meta.schedule);
|
|
2342
|
+
if (cronError) {
|
|
2343
|
+
throw new Error(`@Job has an invalid cron schedule: ${cronError}`);
|
|
2299
2344
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2345
|
+
const timeout = meta.timeout ?? DEFAULT_TIMEOUT_SECONDS;
|
|
2346
|
+
if (!Number.isInteger(timeout) || timeout <= 0) {
|
|
2347
|
+
throw new Error("@Job `timeout` must be a positive whole number of seconds");
|
|
2302
2348
|
}
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
throw new Error(`Event handler for "${eventName}" must be a function`);
|
|
2306
|
-
}
|
|
2349
|
+
if (timeout > MAX_TIMEOUT_SECONDS) {
|
|
2350
|
+
throw new Error(`@Job \`timeout\` exceeds the ${MAX_TIMEOUT_SECONDS}s sandbox ceiling`);
|
|
2307
2351
|
}
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
secret: config.secret,
|
|
2312
|
-
events: config.events
|
|
2313
|
-
};
|
|
2314
|
-
}
|
|
2315
|
-
function validateCustomWebhook(config) {
|
|
2316
|
-
if (!config.path || config.path.trim() === "") {
|
|
2317
|
-
throw new Error("Webhook path is required");
|
|
2318
|
-
}
|
|
2319
|
-
if (!VALID_WEBHOOK_PATH.test(config.path)) {
|
|
2320
|
-
throw new Error(
|
|
2321
|
-
`Invalid webhook path "${config.path}": must start with / and contain only alphanumeric, hyphen, underscore, slash`
|
|
2322
|
-
);
|
|
2323
|
-
}
|
|
2324
|
-
if (!config.handler) {
|
|
2325
|
-
throw new Error("Webhook handler is required");
|
|
2352
|
+
const instance = new ctor();
|
|
2353
|
+
if (typeof instance.run !== "function") {
|
|
2354
|
+
throw new Error("@Job class must declare an async run() method");
|
|
2326
2355
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
}
|
|
2330
|
-
if (config.verify !== void 0 && typeof config.verify !== "function") {
|
|
2331
|
-
throw new Error("Webhook verify must be a function");
|
|
2332
|
-
}
|
|
2333
|
-
return {
|
|
2334
|
-
type: "custom",
|
|
2335
|
-
path: config.path,
|
|
2336
|
-
verify: config.verify,
|
|
2337
|
-
handler: config.handler
|
|
2338
|
-
};
|
|
2356
|
+
const run = instance.run.bind(instance);
|
|
2357
|
+
return { schedule: meta.schedule, timeout, handler: run };
|
|
2339
2358
|
}
|
|
2340
2359
|
|
|
2341
2360
|
// src/resource.ts
|
|
@@ -2440,10 +2459,12 @@ var import_zod2 = require("zod");
|
|
|
2440
2459
|
Get,
|
|
2441
2460
|
Headers,
|
|
2442
2461
|
HttpError,
|
|
2462
|
+
Job,
|
|
2443
2463
|
Log,
|
|
2444
2464
|
NOTIFICATIONS_CONFIG_KIND,
|
|
2445
2465
|
NotFound,
|
|
2446
2466
|
Notifications,
|
|
2467
|
+
On,
|
|
2447
2468
|
OptionalUser,
|
|
2448
2469
|
PALBASE_EXTENSIONS,
|
|
2449
2470
|
PROVIDER_CATALOG,
|
|
@@ -2475,6 +2496,7 @@ var import_zod2 = require("zod");
|
|
|
2475
2496
|
Upload,
|
|
2476
2497
|
UploadedObject,
|
|
2477
2498
|
User,
|
|
2499
|
+
Webhook,
|
|
2478
2500
|
__getRuntime,
|
|
2479
2501
|
__registerResource,
|
|
2480
2502
|
__requestALS,
|
|
@@ -2491,20 +2513,20 @@ var import_zod2 = require("zod");
|
|
|
2491
2513
|
defineEgress,
|
|
2492
2514
|
defineError,
|
|
2493
2515
|
defineFlags,
|
|
2494
|
-
defineJob,
|
|
2495
2516
|
defineMiddleware,
|
|
2496
2517
|
defineNotifications,
|
|
2497
2518
|
defineSchema,
|
|
2498
2519
|
defineStorage,
|
|
2499
2520
|
defineTestUsers,
|
|
2500
|
-
defineWebhook,
|
|
2501
2521
|
defineWorker,
|
|
2502
2522
|
documents,
|
|
2503
2523
|
entitlementFor,
|
|
2504
2524
|
enumType,
|
|
2505
2525
|
flag,
|
|
2506
2526
|
getErrorRegistry,
|
|
2527
|
+
getJobConfig,
|
|
2507
2528
|
getRoutes,
|
|
2529
|
+
getWebhookConfig,
|
|
2508
2530
|
inc,
|
|
2509
2531
|
integer,
|
|
2510
2532
|
isPalbaseExtension,
|