@beignet/next 0.0.26 → 0.0.27
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/CHANGELOG.md +16 -0
- package/README.md +298 -202
- package/dist/index.d.ts +91 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +128 -21
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/skills/routes-server/SKILL.md +9 -10
- package/src/index.ts +256 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beignet/next",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Next.js server-side handlers for Beignet",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"next": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@beignet/web": "^0.0.
|
|
62
|
+
"@beignet/web": "^0.0.27",
|
|
63
63
|
"@standard-schema/spec": "^1.0.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
@@ -64,6 +64,7 @@ registry. If a route is missing from OpenAPI or clients, check this file first.
|
|
|
64
64
|
`server/index.ts` owns runtime composition:
|
|
65
65
|
|
|
66
66
|
- `createNextServer(...)`
|
|
67
|
+
- `createNextServerLoader(...)`
|
|
67
68
|
- app ports
|
|
68
69
|
- providers
|
|
69
70
|
- hooks
|
|
@@ -79,19 +80,17 @@ feature route files.
|
|
|
79
80
|
Application API routes usually use one catch-all file:
|
|
80
81
|
|
|
81
82
|
```ts
|
|
82
|
-
import {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
export const GET =
|
|
86
|
-
|
|
87
|
-
export const OPTIONS = server.api;
|
|
88
|
-
export const PATCH = server.api;
|
|
89
|
-
export const POST = server.api;
|
|
90
|
-
export const PUT = server.api;
|
|
83
|
+
import { createApiRoute } from "@beignet/next";
|
|
84
|
+
import { getServer } from "@/server";
|
|
85
|
+
|
|
86
|
+
export const { DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT } =
|
|
87
|
+
createApiRoute(getServer);
|
|
91
88
|
```
|
|
92
89
|
|
|
93
90
|
The catch-all file is adapter glue. Contracts should still use explicit paths
|
|
94
|
-
such as `/projects/:id`, not catch-all contract patterns.
|
|
91
|
+
such as `/projects/:id`, not catch-all contract patterns. Keep provider
|
|
92
|
+
startup behind `getServer` so Next production builds can import route modules
|
|
93
|
+
without booting the app runtime.
|
|
95
94
|
|
|
96
95
|
## Focused Routes
|
|
97
96
|
|
package/src/index.ts
CHANGED
|
@@ -19,16 +19,18 @@ import {
|
|
|
19
19
|
type StandardSchema,
|
|
20
20
|
} from "@beignet/core/schedules";
|
|
21
21
|
import type {
|
|
22
|
+
AppEnvironment,
|
|
22
23
|
ContractLike,
|
|
23
24
|
CreateServerOptions,
|
|
24
25
|
Handler,
|
|
26
|
+
HealthConfig,
|
|
25
27
|
HttpRequestLike,
|
|
26
28
|
ResolveContract,
|
|
27
29
|
RouteDef,
|
|
28
30
|
ServerInstance,
|
|
29
31
|
StandardSchemaV1,
|
|
30
32
|
} from "@beignet/core/server";
|
|
31
|
-
import { createServer } from "@beignet/core/server";
|
|
33
|
+
import { createHealthHandler, createServer } from "@beignet/core/server";
|
|
32
34
|
import type { UploadRouter } from "@beignet/core/uploads";
|
|
33
35
|
import {
|
|
34
36
|
type InferWebhookEvent,
|
|
@@ -38,7 +40,7 @@ import {
|
|
|
38
40
|
type WebhookEvent,
|
|
39
41
|
type WebhookEventSchemas,
|
|
40
42
|
} from "@beignet/core/webhooks";
|
|
41
|
-
import { createFetchHandler, toWebResponse } from "@beignet/web";
|
|
43
|
+
import { createFetchHandler, toRequestLike, toWebResponse } from "@beignet/web";
|
|
42
44
|
|
|
43
45
|
/**
|
|
44
46
|
* Server types re-exported for Next.js apps.
|
|
@@ -81,6 +83,22 @@ type AnyProvider = ServiceProvider<
|
|
|
81
83
|
any
|
|
82
84
|
>;
|
|
83
85
|
|
|
86
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
87
|
+
|
|
88
|
+
type ServerSource<TServer> = TServer | (() => MaybePromise<TServer>);
|
|
89
|
+
|
|
90
|
+
function isServerLoader<TServer>(
|
|
91
|
+
source: ServerSource<TServer>,
|
|
92
|
+
): source is () => MaybePromise<TServer> {
|
|
93
|
+
return typeof source === "function";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function resolveServerSource<TServer>(
|
|
97
|
+
source: ServerSource<TServer>,
|
|
98
|
+
): Promise<TServer> {
|
|
99
|
+
return isServerLoader(source) ? source() : source;
|
|
100
|
+
}
|
|
101
|
+
|
|
84
102
|
/**
|
|
85
103
|
* Beignet client config with Next.js base URL defaults.
|
|
86
104
|
*/
|
|
@@ -267,14 +285,30 @@ type OutboxDrainContext = {
|
|
|
267
285
|
ports: OutboxDrainPorts;
|
|
268
286
|
};
|
|
269
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Minimal server surface required by the Next.js route factories.
|
|
290
|
+
*
|
|
291
|
+
* Both a `NextServer` and a core `ServerInstance` satisfy this shape
|
|
292
|
+
* structurally, so route modules built on these factories are executable
|
|
293
|
+
* under `bun test`: construct the route with a fake server exposing
|
|
294
|
+
* `createRequestContext` and invoke the exported handlers with a plain
|
|
295
|
+
* `Request`. No Next.js runtime (`next/headers`) is involved.
|
|
296
|
+
*/
|
|
297
|
+
export type NextRouteServer<Ctx> = {
|
|
298
|
+
createRequestContext: (req: HttpRequestLike) => Promise<Ctx>;
|
|
299
|
+
};
|
|
300
|
+
|
|
270
301
|
/**
|
|
271
302
|
* Options for creating a serverless-safe outbox drain route.
|
|
272
303
|
*/
|
|
273
304
|
export type CreateOutboxDrainRouteOptions<Ctx extends OutboxDrainContext> = {
|
|
274
305
|
/**
|
|
275
|
-
* Beignet
|
|
306
|
+
* Beignet server that owns app context and ports.
|
|
307
|
+
*
|
|
308
|
+
* Any object exposing `createRequestContext` works: a `NextServer`, a core
|
|
309
|
+
* `ServerInstance`, or a test fake driven from `bun test`.
|
|
276
310
|
*/
|
|
277
|
-
server:
|
|
311
|
+
server: ServerSource<NextRouteServer<Ctx>>;
|
|
278
312
|
/**
|
|
279
313
|
* Registry of events and jobs that may be delivered from the outbox.
|
|
280
314
|
*/
|
|
@@ -405,9 +439,12 @@ export type CreateWebhookRouteOptions<
|
|
|
405
439
|
AllowUnknownEvents extends boolean = true,
|
|
406
440
|
> = {
|
|
407
441
|
/**
|
|
408
|
-
* Beignet
|
|
442
|
+
* Beignet server that owns app context and ports.
|
|
443
|
+
*
|
|
444
|
+
* Any object exposing `createRequestContext` works: a `NextServer`, a core
|
|
445
|
+
* `ServerInstance`, or a test fake driven from `bun test`.
|
|
409
446
|
*/
|
|
410
|
-
server:
|
|
447
|
+
server: ServerSource<NextRouteServer<Ctx>>;
|
|
411
448
|
/**
|
|
412
449
|
* Inbound webhook definition.
|
|
413
450
|
*/
|
|
@@ -444,9 +481,12 @@ export type CreateWebhookRouteOptions<
|
|
|
444
481
|
*/
|
|
445
482
|
export type CreateScheduleRouteOptions<Ctx extends ScheduleRouteContext> = {
|
|
446
483
|
/**
|
|
447
|
-
* Beignet
|
|
484
|
+
* Beignet server that owns app context and ports.
|
|
485
|
+
*
|
|
486
|
+
* Any object exposing `createRequestContext` works: a `NextServer`, a core
|
|
487
|
+
* `ServerInstance`, or a test fake driven from `bun test`.
|
|
448
488
|
*/
|
|
449
|
-
server:
|
|
489
|
+
server: ServerSource<NextRouteServer<Ctx>>;
|
|
450
490
|
/**
|
|
451
491
|
* Registered schedules, usually the `schedules` array from
|
|
452
492
|
* `server/schedules.ts`.
|
|
@@ -483,9 +523,12 @@ export type CreatePaymentWebhookRouteOptions<
|
|
|
483
523
|
Ctx extends PaymentWebhookRouteContext,
|
|
484
524
|
> = {
|
|
485
525
|
/**
|
|
486
|
-
* Beignet
|
|
526
|
+
* Beignet server that owns app context and ports.
|
|
527
|
+
*
|
|
528
|
+
* Any object exposing `createRequestContext` works: a `NextServer`, a core
|
|
529
|
+
* `ServerInstance`, or a test fake driven from `bun test`.
|
|
487
530
|
*/
|
|
488
|
-
server:
|
|
531
|
+
server: ServerSource<NextRouteServer<Ctx>>;
|
|
489
532
|
/**
|
|
490
533
|
* Provider signature header. Stripe uses `stripe-signature`.
|
|
491
534
|
*
|
|
@@ -559,6 +602,18 @@ export interface NextServer<
|
|
|
559
602
|
Ports,
|
|
560
603
|
ServiceInput
|
|
561
604
|
>["createServiceContext"];
|
|
605
|
+
/**
|
|
606
|
+
* Build a service context and run `fn` inside a scoped ambient correlation
|
|
607
|
+
* frame, returning its result.
|
|
608
|
+
*
|
|
609
|
+
* This is the script-safe counterpart to `createServiceContext(...)`.
|
|
610
|
+
* Requires `context.service` to be declared in `createNextServer(...)`.
|
|
611
|
+
*/
|
|
612
|
+
runServiceContext: ServerInstance<
|
|
613
|
+
Ctx,
|
|
614
|
+
Ports,
|
|
615
|
+
ServiceInput
|
|
616
|
+
>["runServiceContext"];
|
|
562
617
|
/**
|
|
563
618
|
* Registered contract inputs.
|
|
564
619
|
*/
|
|
@@ -573,6 +628,108 @@ export interface NextServer<
|
|
|
573
628
|
ports: Ports;
|
|
574
629
|
}
|
|
575
630
|
|
|
631
|
+
/**
|
|
632
|
+
* Lazy, memoized loader for a Beignet Next server.
|
|
633
|
+
*/
|
|
634
|
+
export type NextServerLoader<TServer> = () => Promise<TServer>;
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* HTTP method handlers for a central Beignet API catch-all route.
|
|
638
|
+
*/
|
|
639
|
+
export type NextApiRouteHandlers = {
|
|
640
|
+
DELETE: (req: Request) => Promise<Response>;
|
|
641
|
+
GET: (req: Request) => Promise<Response>;
|
|
642
|
+
HEAD: (req: Request) => Promise<Response>;
|
|
643
|
+
OPTIONS: (req: Request) => Promise<Response>;
|
|
644
|
+
PATCH: (req: Request) => Promise<Response>;
|
|
645
|
+
POST: (req: Request) => Promise<Response>;
|
|
646
|
+
PUT: (req: Request) => Promise<Response>;
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Create a memoized Next server loader for route modules.
|
|
651
|
+
*
|
|
652
|
+
* Next.js imports route modules during production builds. Exporting a lazy
|
|
653
|
+
* loader lets those modules stay cheap to import while still booting providers
|
|
654
|
+
* before the first real request in each runtime process.
|
|
655
|
+
*/
|
|
656
|
+
export function createNextServerLoader<TServer>(
|
|
657
|
+
createServer: () => MaybePromise<TServer>,
|
|
658
|
+
): NextServerLoader<TServer> {
|
|
659
|
+
let server: Promise<TServer> | undefined;
|
|
660
|
+
|
|
661
|
+
return () => {
|
|
662
|
+
if (!server) {
|
|
663
|
+
const nextServer = Promise.resolve().then(createServer);
|
|
664
|
+
server = nextServer;
|
|
665
|
+
void nextServer.catch(() => {
|
|
666
|
+
if (server === nextServer) {
|
|
667
|
+
server = undefined;
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return server;
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Create HTTP method exports for a catch-all API route.
|
|
678
|
+
*/
|
|
679
|
+
export function createApiRoute<
|
|
680
|
+
TServer extends Pick<NextServer<unknown, AnyPorts, unknown>, "api">,
|
|
681
|
+
>(loadServer: NextServerLoader<TServer>): NextApiRouteHandlers {
|
|
682
|
+
const handle = async (req: Request) => {
|
|
683
|
+
const server = await loadServer();
|
|
684
|
+
return server.api(req);
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
return {
|
|
688
|
+
DELETE: handle,
|
|
689
|
+
GET: handle,
|
|
690
|
+
HEAD: handle,
|
|
691
|
+
OPTIONS: handle,
|
|
692
|
+
PATCH: handle,
|
|
693
|
+
POST: handle,
|
|
694
|
+
PUT: handle,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Create a readiness route whose server is loaded only when the route handles
|
|
700
|
+
* a request.
|
|
701
|
+
*/
|
|
702
|
+
export function createHealthRoute<Ports extends AnyPorts>(
|
|
703
|
+
loadServer: NextServerLoader<
|
|
704
|
+
Pick<NextServer<unknown, Ports, unknown>, "ports">
|
|
705
|
+
>,
|
|
706
|
+
healthConfig: HealthConfig<Ports> | undefined,
|
|
707
|
+
environment: AppEnvironment = "production",
|
|
708
|
+
): {
|
|
709
|
+
GET: (req: Request) => Promise<Response>;
|
|
710
|
+
} {
|
|
711
|
+
return {
|
|
712
|
+
GET: async (req) => {
|
|
713
|
+
const server = await loadServer();
|
|
714
|
+
const handler = createHealthHandler(
|
|
715
|
+
server.ports,
|
|
716
|
+
healthConfig,
|
|
717
|
+
environment,
|
|
718
|
+
);
|
|
719
|
+
const response = await handler(req);
|
|
720
|
+
const headers = new Headers(response.headers);
|
|
721
|
+
if (!headers.has("cache-control")) {
|
|
722
|
+
headers.set("cache-control", "no-store");
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return Response.json(response.body, {
|
|
726
|
+
status: response.status,
|
|
727
|
+
headers,
|
|
728
|
+
});
|
|
729
|
+
},
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
|
|
576
733
|
/**
|
|
577
734
|
* Convert a Beignet response into a native `Response`.
|
|
578
735
|
*
|
|
@@ -936,7 +1093,7 @@ export function createSwaggerUIHandler(
|
|
|
936
1093
|
* all return 404 so storage routes do not leak object existence.
|
|
937
1094
|
*/
|
|
938
1095
|
export function createStorageRoute(
|
|
939
|
-
storage: StoragePort
|
|
1096
|
+
storage: ServerSource<StoragePort>,
|
|
940
1097
|
options: CreateStorageRouteOptions = {},
|
|
941
1098
|
): {
|
|
942
1099
|
GET: (req: Request) => Promise<Response>;
|
|
@@ -945,8 +1102,22 @@ export function createStorageRoute(
|
|
|
945
1102
|
const basePath = normalizeStorageBasePath(options.basePath);
|
|
946
1103
|
|
|
947
1104
|
return {
|
|
948
|
-
GET: (req) =>
|
|
949
|
-
|
|
1105
|
+
GET: async (req) =>
|
|
1106
|
+
storageResponse(
|
|
1107
|
+
await resolveServerSource(storage),
|
|
1108
|
+
req,
|
|
1109
|
+
basePath,
|
|
1110
|
+
options,
|
|
1111
|
+
true,
|
|
1112
|
+
),
|
|
1113
|
+
HEAD: async (req) =>
|
|
1114
|
+
storageResponse(
|
|
1115
|
+
await resolveServerSource(storage),
|
|
1116
|
+
req,
|
|
1117
|
+
basePath,
|
|
1118
|
+
options,
|
|
1119
|
+
false,
|
|
1120
|
+
),
|
|
950
1121
|
};
|
|
951
1122
|
}
|
|
952
1123
|
|
|
@@ -958,7 +1129,7 @@ export function createStorageRoute(
|
|
|
958
1129
|
* `prepare`, `complete`, or `upload`.
|
|
959
1130
|
*/
|
|
960
1131
|
export function createUploadRoute(
|
|
961
|
-
router: UploadRouter
|
|
1132
|
+
router: ServerSource<UploadRouter>,
|
|
962
1133
|
options: CreateUploadRouteOptions = {},
|
|
963
1134
|
): {
|
|
964
1135
|
POST: (req: Request, ctx?: UploadRouteContext) => Promise<Response>;
|
|
@@ -999,7 +1170,9 @@ export function createUploadRoute(
|
|
|
999
1170
|
);
|
|
1000
1171
|
}
|
|
1001
1172
|
|
|
1002
|
-
|
|
1173
|
+
const resolvedRouter = await resolveServerSource(router);
|
|
1174
|
+
|
|
1175
|
+
return resolvedRouter.handleRequest(req, {
|
|
1003
1176
|
uploadName,
|
|
1004
1177
|
action,
|
|
1005
1178
|
});
|
|
@@ -1052,6 +1225,24 @@ function toHttpResponseHeaders(
|
|
|
1052
1225
|
return result;
|
|
1053
1226
|
}
|
|
1054
1227
|
|
|
1228
|
+
/**
|
|
1229
|
+
* Rebuild a request whose body was already consumed.
|
|
1230
|
+
*
|
|
1231
|
+
* Webhook routes read the raw body before anything else so signature
|
|
1232
|
+
* verification never races the context factory over one body stream. Context
|
|
1233
|
+
* creation then receives a rehydrated request carrying the same method, URL,
|
|
1234
|
+
* headers, and raw body.
|
|
1235
|
+
*/
|
|
1236
|
+
function toContextRequest(req: Request, rawBody: string): HttpRequestLike {
|
|
1237
|
+
return toRequestLike(
|
|
1238
|
+
new Request(req.url, {
|
|
1239
|
+
method: req.method,
|
|
1240
|
+
headers: req.headers,
|
|
1241
|
+
body: rawBody,
|
|
1242
|
+
}),
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1055
1246
|
function headersToRecord(headers: Headers): Record<string, string> {
|
|
1056
1247
|
const result: Record<string, string> = {};
|
|
1057
1248
|
headers.forEach((value, key) => {
|
|
@@ -1128,7 +1319,8 @@ export function createOutboxDrainRoute<Ctx extends OutboxDrainContext>(
|
|
|
1128
1319
|
let ctx: Ctx | undefined;
|
|
1129
1320
|
|
|
1130
1321
|
try {
|
|
1131
|
-
|
|
1322
|
+
const server = await resolveServerSource(options.server);
|
|
1323
|
+
ctx = await server.createRequestContext(toRequestLike(req));
|
|
1132
1324
|
const drainCtx = ctx;
|
|
1133
1325
|
const result = await drainOutbox({
|
|
1134
1326
|
outbox: drainCtx.ports.outbox,
|
|
@@ -1206,9 +1398,10 @@ export function createOutboxDrainRoute<Ctx extends OutboxDrainContext>(
|
|
|
1206
1398
|
* Create a Next.js POST handler for generic verified inbound webhooks.
|
|
1207
1399
|
*
|
|
1208
1400
|
* The route reads the raw request body as text, normalizes request headers,
|
|
1209
|
-
*
|
|
1210
|
-
*
|
|
1211
|
-
*
|
|
1401
|
+
* builds app context from the real request through
|
|
1402
|
+
* `server.createRequestContext(...)`, verifies the event through the webhook
|
|
1403
|
+
* definition or a context-aware verifier, validates the matching event
|
|
1404
|
+
* payload schema, and delegates fulfillment to app code.
|
|
1212
1405
|
*/
|
|
1213
1406
|
export function createWebhookRoute<
|
|
1214
1407
|
Ctx extends WebhookRouteContext,
|
|
@@ -1224,10 +1417,26 @@ export function createWebhookRoute<
|
|
|
1224
1417
|
const headers = resolveHeaders(options.headers, req);
|
|
1225
1418
|
const requestHeaders = headersToRecord(req.headers);
|
|
1226
1419
|
|
|
1420
|
+
let rawBody: string;
|
|
1421
|
+
|
|
1422
|
+
try {
|
|
1423
|
+
rawBody = await req.text();
|
|
1424
|
+
} catch {
|
|
1425
|
+
return webhookRouteJson(
|
|
1426
|
+
{
|
|
1427
|
+
ok: false,
|
|
1428
|
+
error: "Webhook body read failed.",
|
|
1429
|
+
},
|
|
1430
|
+
400,
|
|
1431
|
+
headers,
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1227
1435
|
let ctx: Ctx;
|
|
1228
1436
|
|
|
1229
1437
|
try {
|
|
1230
|
-
|
|
1438
|
+
const server = await resolveServerSource(options.server);
|
|
1439
|
+
ctx = await server.createRequestContext(toContextRequest(req, rawBody));
|
|
1231
1440
|
} catch {
|
|
1232
1441
|
return webhookRouteJson(
|
|
1233
1442
|
{
|
|
@@ -1239,7 +1448,6 @@ export function createWebhookRoute<
|
|
|
1239
1448
|
);
|
|
1240
1449
|
}
|
|
1241
1450
|
|
|
1242
|
-
let rawBody: string;
|
|
1243
1451
|
let event: WebhookRouteHandlerArgs<
|
|
1244
1452
|
Ctx,
|
|
1245
1453
|
Webhook,
|
|
@@ -1247,7 +1455,6 @@ export function createWebhookRoute<
|
|
|
1247
1455
|
>["event"];
|
|
1248
1456
|
|
|
1249
1457
|
try {
|
|
1250
|
-
rawBody = await req.text();
|
|
1251
1458
|
const input: VerifyWebhookInput = {
|
|
1252
1459
|
rawBody,
|
|
1253
1460
|
headers: requestHeaders,
|
|
@@ -1350,9 +1557,10 @@ export function createWebhookRoute<
|
|
|
1350
1557
|
/**
|
|
1351
1558
|
* Create a Next.js POST handler for provider-verified payment webhooks.
|
|
1352
1559
|
*
|
|
1353
|
-
* The route
|
|
1354
|
-
*
|
|
1355
|
-
*
|
|
1560
|
+
* The route extracts the provider signature header, reads the raw request
|
|
1561
|
+
* body as text, builds app context from the real request through
|
|
1562
|
+
* `server.createRequestContext(...)`, verifies the event through
|
|
1563
|
+
* `ctx.ports.payments`, and then delegates fulfillment to app code. This is the canonical helper for Beignet
|
|
1356
1564
|
* billing flows backed by `PaymentsPort`. Keep this route on the Node.js
|
|
1357
1565
|
* runtime when your provider SDK requires Node APIs.
|
|
1358
1566
|
*/
|
|
@@ -1381,10 +1589,26 @@ export function createPaymentWebhookRoute<
|
|
|
1381
1589
|
);
|
|
1382
1590
|
}
|
|
1383
1591
|
|
|
1592
|
+
let rawBody: string;
|
|
1593
|
+
|
|
1594
|
+
try {
|
|
1595
|
+
rawBody = await req.text();
|
|
1596
|
+
} catch {
|
|
1597
|
+
return paymentWebhookJson(
|
|
1598
|
+
{
|
|
1599
|
+
ok: false,
|
|
1600
|
+
error: "Payment webhook body read failed.",
|
|
1601
|
+
},
|
|
1602
|
+
400,
|
|
1603
|
+
headers,
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1384
1607
|
let ctx: Ctx;
|
|
1385
1608
|
|
|
1386
1609
|
try {
|
|
1387
|
-
|
|
1610
|
+
const server = await resolveServerSource(options.server);
|
|
1611
|
+
ctx = await server.createRequestContext(toContextRequest(req, rawBody));
|
|
1388
1612
|
} catch {
|
|
1389
1613
|
return paymentWebhookJson(
|
|
1390
1614
|
{
|
|
@@ -1396,11 +1620,9 @@ export function createPaymentWebhookRoute<
|
|
|
1396
1620
|
);
|
|
1397
1621
|
}
|
|
1398
1622
|
|
|
1399
|
-
let rawBody: string;
|
|
1400
1623
|
let event: PaymentWebhookEvent;
|
|
1401
1624
|
|
|
1402
1625
|
try {
|
|
1403
|
-
rawBody = await req.text();
|
|
1404
1626
|
event = await ctx.ports.payments.verifyWebhook({
|
|
1405
1627
|
rawBody,
|
|
1406
1628
|
signature,
|
|
@@ -1476,8 +1698,9 @@ export function createPaymentWebhookRoute<
|
|
|
1476
1698
|
* Create Next.js route handlers that trigger one registered schedule.
|
|
1477
1699
|
*
|
|
1478
1700
|
* This helper is intended for serverless cron routes. It authenticates the
|
|
1479
|
-
* caller with a timing-safe bearer comparison,
|
|
1480
|
-
*
|
|
1701
|
+
* caller with a timing-safe bearer comparison, builds app context from the
|
|
1702
|
+
* real request through `server.createRequestContext(...)`, runs the schedule
|
|
1703
|
+
* inline, and records `schedule` events through the resolved
|
|
1481
1704
|
* provider instrumentation port (`ports.instrumentation`, then
|
|
1482
1705
|
* `ports.devtools`) when one is installed.
|
|
1483
1706
|
*/
|
|
@@ -1524,7 +1747,8 @@ export function createScheduleRoute<Ctx extends ScheduleRouteContext>(
|
|
|
1524
1747
|
}
|
|
1525
1748
|
|
|
1526
1749
|
try {
|
|
1527
|
-
const
|
|
1750
|
+
const server = await resolveServerSource(options.server);
|
|
1751
|
+
const ctx = await server.createRequestContext(toRequestLike(req));
|
|
1528
1752
|
const runner = createInlineScheduleRunner<Ctx>({
|
|
1529
1753
|
ctx,
|
|
1530
1754
|
instrumentation: resolveProviderInstrumentationPort(ctx.ports),
|
|
@@ -1641,6 +1865,7 @@ export async function createNextServer<
|
|
|
1641
1865
|
},
|
|
1642
1866
|
createRequestContext: runtime.createRequestContext,
|
|
1643
1867
|
createServiceContext: runtime.createServiceContext,
|
|
1868
|
+
runServiceContext: runtime.runServiceContext,
|
|
1644
1869
|
contracts: runtime.contracts,
|
|
1645
1870
|
stop: () => runtime.stop(),
|
|
1646
1871
|
ports: runtime.ports,
|