@beignet/core 0.0.2 → 0.0.3
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 +55 -6
- package/dist/jobs/index.d.ts +138 -4
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js +161 -1
- package/dist/jobs/index.js.map +1 -1
- package/dist/outbox/index.d.ts +5 -0
- package/dist/outbox/index.d.ts.map +1 -1
- package/dist/outbox/index.js +59 -3
- package/dist/outbox/index.js.map +1 -1
- package/dist/providers/instrumentation.d.ts +1 -1
- package/dist/providers/instrumentation.d.ts.map +1 -1
- package/dist/providers/instrumentation.js.map +1 -1
- package/dist/server/hooks/auth.d.ts +50 -65
- package/dist/server/hooks/auth.d.ts.map +1 -1
- package/dist/server/hooks/auth.js +44 -55
- package/dist/server/hooks/auth.js.map +1 -1
- package/dist/server/hooks/index.d.ts +1 -1
- package/dist/server/hooks/index.d.ts.map +1 -1
- package/dist/server/hooks/index.js.map +1 -1
- package/dist/server/http.d.ts +52 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +20 -1
- package/dist/server/http.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/server.d.ts +54 -13
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +56 -35
- package/dist/server/server.js.map +1 -1
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +8 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/uploads/client.d.ts +278 -0
- package/dist/uploads/client.d.ts.map +1 -0
- package/dist/uploads/client.js +428 -0
- package/dist/uploads/client.js.map +1 -0
- package/dist/uploads/index.d.ts +361 -0
- package/dist/uploads/index.d.ts.map +1 -0
- package/dist/uploads/index.js +543 -0
- package/dist/uploads/index.js.map +1 -0
- package/package.json +11 -2
- package/src/jobs/index.ts +326 -5
- package/src/outbox/index.ts +83 -3
- package/src/providers/instrumentation.ts +7 -1
- package/src/server/hooks/auth.ts +89 -162
- package/src/server/hooks/index.ts +1 -5
- package/src/server/http.ts +79 -0
- package/src/server/index.ts +1 -0
- package/src/server/server.ts +191 -23
- package/src/testing/index.ts +11 -0
- package/src/uploads/client.ts +861 -0
- package/src/uploads/index.ts +1067 -0
package/src/server/server.ts
CHANGED
|
@@ -31,6 +31,7 @@ import type {
|
|
|
31
31
|
HttpResponse,
|
|
32
32
|
HttpResponseLike,
|
|
33
33
|
ResolvedRoute,
|
|
34
|
+
RouteHook,
|
|
34
35
|
ServerCaughtErrorHook,
|
|
35
36
|
ServerHook,
|
|
36
37
|
ServerUnhandledErrorMapper,
|
|
@@ -48,29 +49,65 @@ import {
|
|
|
48
49
|
* apps keep these in `features/<feature>/routes.ts` and compose them with
|
|
49
50
|
* `defineRoutes(...)`.
|
|
50
51
|
*/
|
|
51
|
-
export type RouteDef<
|
|
52
|
+
export type RouteDef<
|
|
53
|
+
Ctx,
|
|
54
|
+
CLike extends ContractLike = ContractLike,
|
|
55
|
+
Hooks extends readonly RouteHook<Ctx, object>[] = readonly RouteHook<
|
|
56
|
+
Ctx,
|
|
57
|
+
object
|
|
58
|
+
>[],
|
|
59
|
+
> = {
|
|
52
60
|
/**
|
|
53
61
|
* Contract builder or plain contract config for this route.
|
|
54
62
|
*/
|
|
55
63
|
contract: CLike;
|
|
64
|
+
/**
|
|
65
|
+
* Route-scoped hooks that run after group hooks and before the handler.
|
|
66
|
+
*/
|
|
67
|
+
hooks?: Hooks;
|
|
56
68
|
/**
|
|
57
69
|
* Handler that implements the contract.
|
|
58
70
|
*/
|
|
59
|
-
handle: Handler<Ctx
|
|
71
|
+
handle: Handler<Ctx & AddedCtxFromHooks<Hooks>, ResolveContract<CLike>>;
|
|
60
72
|
};
|
|
61
73
|
|
|
74
|
+
type AddedCtxFromHook<Hook> =
|
|
75
|
+
Hook extends RouteHook<infer _Ctx, infer AddedCtx> ? AddedCtx : unknown;
|
|
76
|
+
|
|
77
|
+
type UnionToIntersection<Union> = (
|
|
78
|
+
Union extends unknown
|
|
79
|
+
? (value: Union) => void
|
|
80
|
+
: never
|
|
81
|
+
) extends (value: infer Intersection) => void
|
|
82
|
+
? Intersection
|
|
83
|
+
: never;
|
|
84
|
+
|
|
85
|
+
type AddedCtxFromHooks<Hooks extends readonly unknown[]> =
|
|
86
|
+
Hooks extends readonly []
|
|
87
|
+
? unknown
|
|
88
|
+
: UnionToIntersection<AddedCtxFromHook<Hooks[number]>>;
|
|
89
|
+
|
|
90
|
+
// biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at collection boundaries
|
|
91
|
+
type AnyRouteDef = RouteDef<any, any>;
|
|
92
|
+
|
|
93
|
+
// biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at collection boundaries
|
|
94
|
+
type PlainRouteDef<Ctx> = RouteDef<Ctx, any, readonly []>;
|
|
95
|
+
|
|
96
|
+
// biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at collection boundaries
|
|
97
|
+
type AnyContractRouteDef<Ctx> = RouteDef<Ctx, any>;
|
|
98
|
+
|
|
62
99
|
const ROUTE_GROUP_KIND = "beignet.route-group";
|
|
63
100
|
|
|
64
101
|
/**
|
|
65
102
|
* Named collection of related route registrations.
|
|
66
103
|
*
|
|
67
|
-
* Route groups
|
|
68
|
-
* them before server
|
|
104
|
+
* Route groups colocate feature routes and can apply scoped route hooks to
|
|
105
|
+
* every route in the group. `defineRoutes(...)` flattens them before server
|
|
106
|
+
* registration while preserving those hooks.
|
|
69
107
|
*/
|
|
70
108
|
export type RouteGroup<
|
|
71
109
|
Ctx,
|
|
72
|
-
|
|
73
|
-
Routes extends readonly RouteDef<Ctx, any>[] = readonly RouteDef<Ctx, any>[],
|
|
110
|
+
Routes extends readonly AnyRouteDef[] = readonly AnyRouteDef[],
|
|
74
111
|
> = {
|
|
75
112
|
/**
|
|
76
113
|
* Internal marker used by `defineRoutes(...)`.
|
|
@@ -80,6 +117,10 @@ export type RouteGroup<
|
|
|
80
117
|
* Human-readable group name.
|
|
81
118
|
*/
|
|
82
119
|
name: string;
|
|
120
|
+
/**
|
|
121
|
+
* Hooks applied to every route in this group.
|
|
122
|
+
*/
|
|
123
|
+
hooks?: readonly RouteHook<Ctx, object>[];
|
|
83
124
|
/**
|
|
84
125
|
* Route definitions in this group.
|
|
85
126
|
*/
|
|
@@ -87,24 +128,45 @@ export type RouteGroup<
|
|
|
87
128
|
};
|
|
88
129
|
|
|
89
130
|
type RouteInput<Ctx> =
|
|
90
|
-
|
|
91
|
-
|
|
|
92
|
-
|
|
93
|
-
|
|
131
|
+
| AnyContractRouteDef<Ctx>
|
|
132
|
+
| AnyRouteDef
|
|
133
|
+
| RouteGroup<Ctx, readonly AnyRouteDef[]>;
|
|
134
|
+
|
|
135
|
+
type ContextualRouteInput<Ctx> =
|
|
136
|
+
| PlainRouteDef<Ctx>
|
|
137
|
+
| RouteGroup<Ctx, readonly AnyRouteDef[]>;
|
|
138
|
+
|
|
139
|
+
type RouteGroupBuilder<Ctx> = {
|
|
140
|
+
<
|
|
141
|
+
const GroupHooks extends readonly RouteHook<Ctx, object>[] = readonly [],
|
|
142
|
+
const R extends readonly PlainRouteDef<
|
|
143
|
+
Ctx & AddedCtxFromHooks<GroupHooks>
|
|
144
|
+
>[] = readonly PlainRouteDef<Ctx & AddedCtxFromHooks<GroupHooks>>[],
|
|
145
|
+
>(group: {
|
|
146
|
+
name: string;
|
|
147
|
+
hooks?: GroupHooks;
|
|
148
|
+
routes: R;
|
|
149
|
+
}): RouteGroup<Ctx, R>;
|
|
150
|
+
<
|
|
151
|
+
const GroupHooks extends readonly RouteHook<Ctx, object>[] = readonly [],
|
|
152
|
+
const R extends readonly AnyRouteDef[] = readonly AnyRouteDef[],
|
|
153
|
+
>(group: {
|
|
154
|
+
name: string;
|
|
155
|
+
hooks?: GroupHooks;
|
|
156
|
+
routes: R;
|
|
157
|
+
}): RouteGroup<Ctx, R>;
|
|
158
|
+
};
|
|
94
159
|
|
|
95
160
|
type RoutesFromInput<Input> =
|
|
96
|
-
|
|
97
|
-
Input extends RouteGroup<any, infer Routes>
|
|
161
|
+
Input extends RouteGroup<infer _Ctx, infer Routes>
|
|
98
162
|
? Routes
|
|
99
|
-
:
|
|
100
|
-
Input extends RouteDef<any, any>
|
|
163
|
+
: Input extends AnyRouteDef
|
|
101
164
|
? readonly [Input]
|
|
102
165
|
: readonly [];
|
|
103
166
|
|
|
104
167
|
type FlattenRouteInputs<Inputs extends readonly unknown[]> =
|
|
105
168
|
number extends Inputs["length"]
|
|
106
|
-
?
|
|
107
|
-
readonly RouteDef<any, any>[]
|
|
169
|
+
? readonly AnyRouteDef[]
|
|
108
170
|
: Inputs extends readonly [infer First, ...infer Rest]
|
|
109
171
|
? readonly [...RoutesFromInput<First>, ...FlattenRouteInputs<Rest>]
|
|
110
172
|
: readonly [];
|
|
@@ -122,6 +184,22 @@ type ContractsFromRouteList<
|
|
|
122
184
|
: never;
|
|
123
185
|
};
|
|
124
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Define one route registration with hook-aware handler typing.
|
|
189
|
+
*
|
|
190
|
+
* Direct route objects are still supported. Use this helper when route-scoped
|
|
191
|
+
* hooks enrich `ctx` for a single handler and you want TypeScript to infer the
|
|
192
|
+
* added fields.
|
|
193
|
+
*/
|
|
194
|
+
export function defineRoute<Ctx>() {
|
|
195
|
+
return <
|
|
196
|
+
CLike extends ContractLike,
|
|
197
|
+
const Hooks extends readonly RouteHook<Ctx, object>[] = readonly [],
|
|
198
|
+
>(
|
|
199
|
+
route: RouteDef<Ctx, CLike, Hooks>,
|
|
200
|
+
): RouteDef<Ctx, CLike, Hooks> => route;
|
|
201
|
+
}
|
|
202
|
+
|
|
125
203
|
/**
|
|
126
204
|
* Options for creating a Beignet server instance.
|
|
127
205
|
*/
|
|
@@ -129,7 +207,7 @@ export type CreateServerOptions<
|
|
|
129
207
|
Ctx,
|
|
130
208
|
Ports extends AnyPorts,
|
|
131
209
|
// biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
|
|
132
|
-
Routes extends readonly RouteDef<
|
|
210
|
+
Routes extends readonly RouteDef<any, any>[] = readonly RouteDef<any, any>[],
|
|
133
211
|
Providers extends readonly ServiceProvider<
|
|
134
212
|
unknown,
|
|
135
213
|
// biome-ignore lint/suspicious/noExplicitAny: provider config types are erased at this level
|
|
@@ -637,6 +715,7 @@ function buildHandler<
|
|
|
637
715
|
contract: C,
|
|
638
716
|
userHandler: Handler<Ctx, C>,
|
|
639
717
|
hooks: ServerHook<Ctx, FinalPorts>[],
|
|
718
|
+
routeHooks: readonly RouteHook<unknown, object>[] = [],
|
|
640
719
|
optionsOverrides?: {
|
|
641
720
|
skipRoutePreparation?: boolean;
|
|
642
721
|
},
|
|
@@ -1208,6 +1287,39 @@ function buildHandler<
|
|
|
1208
1287
|
}
|
|
1209
1288
|
}
|
|
1210
1289
|
|
|
1290
|
+
if (!result) {
|
|
1291
|
+
for (const hook of routeHooks) {
|
|
1292
|
+
try {
|
|
1293
|
+
const additions = await hook.resolve({
|
|
1294
|
+
req,
|
|
1295
|
+
ctx: currentCtx,
|
|
1296
|
+
contract,
|
|
1297
|
+
path,
|
|
1298
|
+
query,
|
|
1299
|
+
headers,
|
|
1300
|
+
body,
|
|
1301
|
+
});
|
|
1302
|
+
if (additions && typeof additions === "object") {
|
|
1303
|
+
currentCtx = {
|
|
1304
|
+
...(currentCtx as object),
|
|
1305
|
+
...additions,
|
|
1306
|
+
} as Ctx;
|
|
1307
|
+
}
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
result = await resolveErrorResult(
|
|
1310
|
+
error,
|
|
1311
|
+
currentCtx,
|
|
1312
|
+
pathValue,
|
|
1313
|
+
queryValue,
|
|
1314
|
+
headersValue,
|
|
1315
|
+
bodyValue,
|
|
1316
|
+
{ owner: "framework" },
|
|
1317
|
+
);
|
|
1318
|
+
break;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1211
1323
|
if (!result) {
|
|
1212
1324
|
try {
|
|
1213
1325
|
result = {
|
|
@@ -1328,7 +1440,7 @@ export async function createServer<
|
|
|
1328
1440
|
Ctx,
|
|
1329
1441
|
Ports extends AnyPorts,
|
|
1330
1442
|
// biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
|
|
1331
|
-
Routes extends readonly RouteDef<
|
|
1443
|
+
Routes extends readonly RouteDef<any, any>[] = readonly RouteDef<any, any>[],
|
|
1332
1444
|
Providers extends readonly ServiceProvider<
|
|
1333
1445
|
unknown,
|
|
1334
1446
|
// biome-ignore lint/suspicious/noExplicitAny: provider config types are erased at this level
|
|
@@ -1382,6 +1494,7 @@ export async function createServer<
|
|
|
1382
1494
|
const registerRoute = <C extends HttpContractConfig>(
|
|
1383
1495
|
contract: C,
|
|
1384
1496
|
handler: Handler<Ctx, C>,
|
|
1497
|
+
routeHooks: readonly RouteHook<unknown, object>[] = [],
|
|
1385
1498
|
): void => {
|
|
1386
1499
|
if (contract.body && !methodSupportsRequestBody(contract.method)) {
|
|
1387
1500
|
throw new Error(
|
|
@@ -1412,6 +1525,7 @@ export async function createServer<
|
|
|
1412
1525
|
contract,
|
|
1413
1526
|
handler,
|
|
1414
1527
|
hooks,
|
|
1528
|
+
routeHooks,
|
|
1415
1529
|
);
|
|
1416
1530
|
registry.push({
|
|
1417
1531
|
contract,
|
|
@@ -1444,7 +1558,11 @@ export async function createServer<
|
|
|
1444
1558
|
try {
|
|
1445
1559
|
for (const route of options.routes) {
|
|
1446
1560
|
const contract = resolveContract(route.contract);
|
|
1447
|
-
registerRoute(
|
|
1561
|
+
registerRoute(
|
|
1562
|
+
contract,
|
|
1563
|
+
route.handle as Handler<Ctx, typeof contract>,
|
|
1564
|
+
route.hooks as readonly RouteHook<unknown, object>[] | undefined,
|
|
1565
|
+
);
|
|
1448
1566
|
}
|
|
1449
1567
|
} catch (error) {
|
|
1450
1568
|
try {
|
|
@@ -1518,6 +1636,7 @@ export async function createServer<
|
|
|
1518
1636
|
notFoundContract,
|
|
1519
1637
|
async () => errorResponse(404, "NOT_FOUND", "Not found"),
|
|
1520
1638
|
hooks,
|
|
1639
|
+
[],
|
|
1521
1640
|
{ skipRoutePreparation: true },
|
|
1522
1641
|
);
|
|
1523
1642
|
|
|
@@ -1549,6 +1668,15 @@ export async function createServer<
|
|
|
1549
1668
|
* ]);
|
|
1550
1669
|
* ```
|
|
1551
1670
|
*/
|
|
1671
|
+
export function defineRoutes<
|
|
1672
|
+
Ctx,
|
|
1673
|
+
const R extends
|
|
1674
|
+
readonly ContextualRouteInput<Ctx>[] = readonly ContextualRouteInput<Ctx>[],
|
|
1675
|
+
>(routes: R): FlattenRouteInputs<R>;
|
|
1676
|
+
export function defineRoutes<
|
|
1677
|
+
Ctx,
|
|
1678
|
+
const R extends readonly RouteInput<Ctx>[] = readonly RouteInput<Ctx>[],
|
|
1679
|
+
>(routes: R): FlattenRouteInputs<R>;
|
|
1552
1680
|
export function defineRoutes<
|
|
1553
1681
|
Ctx,
|
|
1554
1682
|
const R extends readonly RouteInput<Ctx>[] = readonly RouteInput<Ctx>[],
|
|
@@ -1557,7 +1685,12 @@ export function defineRoutes<
|
|
|
1557
1685
|
|
|
1558
1686
|
for (const route of routes) {
|
|
1559
1687
|
if (isRouteGroup(route)) {
|
|
1560
|
-
|
|
1688
|
+
for (const groupRoute of route.routes) {
|
|
1689
|
+
flattened.push({
|
|
1690
|
+
...groupRoute,
|
|
1691
|
+
hooks: [...(route.hooks ?? []), ...(groupRoute.hooks ?? [])],
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1561
1694
|
} else {
|
|
1562
1695
|
flattened.push(route);
|
|
1563
1696
|
}
|
|
@@ -1585,26 +1718,61 @@ export function contractsFromRoutes<
|
|
|
1585
1718
|
* Define a named group of related route registrations.
|
|
1586
1719
|
*
|
|
1587
1720
|
* Route groups are flattened by defineRoutes, so createServer still receives
|
|
1588
|
-
* a regular route list while app code can keep feature route wiring
|
|
1721
|
+
* a regular route list while app code can keep feature route wiring and scoped
|
|
1722
|
+
* hooks colocated.
|
|
1589
1723
|
*
|
|
1590
1724
|
* @example
|
|
1591
1725
|
* ```ts
|
|
1592
|
-
* const todoRoutes = defineRouteGroup<AppContext>({
|
|
1726
|
+
* const todoRoutes = defineRouteGroup<AppContext>()({
|
|
1593
1727
|
* name: "todos",
|
|
1728
|
+
* hooks: [auth.optional()],
|
|
1594
1729
|
* routes: [
|
|
1595
1730
|
* { contract: listTodos, handle: async ({ ctx }) => ctx.todos.list() },
|
|
1596
1731
|
* ]
|
|
1597
1732
|
* });
|
|
1598
1733
|
* ```
|
|
1599
1734
|
*/
|
|
1735
|
+
export function defineRouteGroup<Ctx>(): RouteGroupBuilder<Ctx>;
|
|
1600
1736
|
export function defineRouteGroup<
|
|
1601
1737
|
Ctx,
|
|
1602
1738
|
// biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
|
|
1603
1739
|
const R extends readonly RouteDef<Ctx, any>[] = readonly RouteDef<Ctx, any>[],
|
|
1604
|
-
>(group: {
|
|
1740
|
+
>(group: {
|
|
1741
|
+
name: string;
|
|
1742
|
+
hooks?: readonly RouteHook<Ctx, object>[];
|
|
1743
|
+
routes: R;
|
|
1744
|
+
}): RouteGroup<Ctx, R>;
|
|
1745
|
+
export function defineRouteGroup<
|
|
1746
|
+
Ctx,
|
|
1747
|
+
// biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
|
|
1748
|
+
const R extends readonly RouteDef<any, any>[] = readonly RouteDef<any, any>[],
|
|
1749
|
+
>(group?: {
|
|
1750
|
+
name: string;
|
|
1751
|
+
hooks?: readonly RouteHook<Ctx, object>[];
|
|
1752
|
+
routes: R;
|
|
1753
|
+
}): RouteGroup<Ctx, R> | RouteGroupBuilder<Ctx> {
|
|
1754
|
+
const createGroup = <
|
|
1755
|
+
const GroupHooks extends readonly RouteHook<Ctx, object>[] = readonly [],
|
|
1756
|
+
const GroupRoutes extends readonly AnyRouteDef[] = readonly AnyRouteDef[],
|
|
1757
|
+
>(input: {
|
|
1758
|
+
name: string;
|
|
1759
|
+
hooks?: GroupHooks;
|
|
1760
|
+
routes: GroupRoutes;
|
|
1761
|
+
}): RouteGroup<Ctx, GroupRoutes> => ({
|
|
1762
|
+
kind: ROUTE_GROUP_KIND,
|
|
1763
|
+
name: input.name,
|
|
1764
|
+
hooks: input.hooks,
|
|
1765
|
+
routes: input.routes,
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
if (!group) {
|
|
1769
|
+
return createGroup;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1605
1772
|
return {
|
|
1606
1773
|
kind: ROUTE_GROUP_KIND,
|
|
1607
1774
|
name: group.name,
|
|
1775
|
+
hooks: group.hooks,
|
|
1608
1776
|
routes: group.routes,
|
|
1609
1777
|
};
|
|
1610
1778
|
}
|
package/src/testing/index.ts
CHANGED
|
@@ -196,6 +196,17 @@ export class SeedRunError extends Error {
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Reset one or more factory sequences to their configured starting values.
|
|
201
|
+
*/
|
|
202
|
+
export function resetFactories(
|
|
203
|
+
...factories: readonly Pick<FactoryDef, "resetSequence">[]
|
|
204
|
+
): void {
|
|
205
|
+
for (const factory of factories) {
|
|
206
|
+
factory.resetSequence();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
199
210
|
function errorMessage(error: unknown): string {
|
|
200
211
|
return error instanceof Error ? error.message : String(error);
|
|
201
212
|
}
|