@fougere/adapter-rest 0.6.0-alpha.0 → 0.8.0-alpha.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/register.d.ts +1 -8
- package/dist/register.d.ts.map +1 -1
- package/dist/register.js +2 -9
- package/dist/register.js.map +1 -1
- package/dist/routes.d.ts +11 -56
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +7 -21
- package/dist/routes.js.map +1 -1
- package/package.json +4 -4
- package/src/register.ts +2 -9
- package/src/routes.ts +18 -64
package/dist/register.d.ts
CHANGED
|
@@ -3,13 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { HttpRouter } from '@fougere/http';
|
|
5
5
|
import type { RouteDefinition } from './routes.js';
|
|
6
|
-
/**
|
|
7
|
-
* Register all route definitions on an HttpRouter.
|
|
8
|
-
*
|
|
9
|
-
* ```ts
|
|
10
|
-
* const routes = generateRoutes(app, { prefix: '/api' });
|
|
11
|
-
* registerRoutes(router, routes);
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
6
|
+
/** Register all route definitions on an HttpRouter. */
|
|
14
7
|
export declare function registerRoutes(router: HttpRouter, routes: RouteDefinition[]): void;
|
|
15
8
|
//# sourceMappingURL=register.d.ts.map
|
package/dist/register.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,uDAAuD;AACvD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,eAAe,EAAE,GACxB,IAAI,CA0CN"}
|
package/dist/register.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { toHttpError } from '@fougere/core';
|
|
2
|
-
/**
|
|
3
|
-
* Register all route definitions on an HttpRouter.
|
|
4
|
-
*
|
|
5
|
-
* ```ts
|
|
6
|
-
* const routes = generateRoutes(app, { prefix: '/api' });
|
|
7
|
-
* registerRoutes(router, routes);
|
|
8
|
-
* ```
|
|
9
|
-
*/
|
|
2
|
+
/** Register all route definitions on an HttpRouter. */
|
|
10
3
|
export function registerRoutes(router, routes) {
|
|
11
4
|
for (const route of routes) {
|
|
12
5
|
router.on(route.method, route.path, async (ctx) => {
|
|
@@ -14,7 +7,7 @@ export function registerRoutes(router, routes) {
|
|
|
14
7
|
const invocation = {
|
|
15
8
|
params: ctx.params,
|
|
16
9
|
query: ctx.query,
|
|
17
|
-
body,
|
|
10
|
+
input: body,
|
|
18
11
|
state: ctx.state,
|
|
19
12
|
};
|
|
20
13
|
try {
|
package/dist/register.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,uDAAuD;AACvD,MAAM,UAAU,cAAc,CAC5B,MAAkB,EAClB,MAAyB;IAEzB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAChD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAwC,CAAC;YAEvE,MAAM,UAAU,GAAG;gBACjB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,KAAK,EAAE,IAAI;gBACX,KAAK,EAAE,GAAG,CAAC,KAAK;aACjB,CAAC;YAEF,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBAE/C,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,CAAC,IAAI,KAAK,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;oBACpF,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;gBACvD,CAAC;gBACD,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,aAAa,KAAK,QAAQ,EAAE,CAAC;oBACxD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBACrC,CAAC;gBACD,IAAI,MAAM,KAAK,KAAK,IAAI,KAAK,CAAC,aAAa,KAAK,QAAQ,EAAE,CAAC;oBACzD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;gBACvD,CAAC;gBAED,kFAAkF;gBAClF,qFAAqF;gBACrF,mFAAmF;gBACnF,0EAA0E;gBAE1E,OAAO;oBACL,MAAM,EAAE,KAAK,CAAC,aAAa,IAAI,CAAC,KAAK,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;oBAC7E,4EAA4E;oBAC5E,6EAA6E;oBAC7E,IAAI,EAAE,MAAM;iBACb,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;gBAC1C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;YAChC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
package/dist/routes.d.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Framework-agnostic: produces RouteDefinition[], consumed by an adapter (Fastify, Express, etc).
|
|
5
|
-
*/
|
|
6
|
-
import type { Fields, SchemaOrCard } from '@fougere/schema';
|
|
1
|
+
/** @fougere/adapter-rest — generates REST route definitions from fougere handlers. */
|
|
2
|
+
import type { Fields, SchemaView } from '@fougere/schema';
|
|
7
3
|
import type { HandlerEntry as CoreHandlerEntry } from '@fougere/core';
|
|
8
4
|
export type { HttpMethod } from '@fougere/http';
|
|
9
5
|
import type { HttpMethod } from '@fougere/http';
|
|
@@ -20,20 +16,12 @@ export interface RouteDefinition {
|
|
|
20
16
|
outputFields?: Fields;
|
|
21
17
|
/** Explicit success status. Defaults to 201 only for the canonical `create` op. */
|
|
22
18
|
successStatus?: number;
|
|
23
|
-
/**
|
|
24
|
-
* The operation in words — the method's own doc sentence, carried by the contract.
|
|
25
|
-
*
|
|
26
|
-
* A route is what an OpenAPI `summary` is generated FROM, and the sentence reached
|
|
27
|
-
* this file already through core's `EffectiveOperation` table;
|
|
28
|
-
* only this type had no name for it, so every generated route was undocumented while
|
|
29
|
-
* the sentence sat one property away. Carried, not rendered: emitting OpenAPI is a
|
|
30
|
-
* reader's job, and this is what it reads.
|
|
31
|
-
*/
|
|
19
|
+
/** The operation in words — the method's own doc sentence, carried by the contract. */
|
|
32
20
|
description?: string;
|
|
33
21
|
}
|
|
34
22
|
interface OperationMeta {
|
|
35
|
-
input?:
|
|
36
|
-
output?:
|
|
23
|
+
input?: SchemaView;
|
|
24
|
+
output?: SchemaView;
|
|
37
25
|
/** Canonical kind from core's EffectiveOperation. */
|
|
38
26
|
kind: 'query' | 'command';
|
|
39
27
|
/** The operation in words — see `RouteDefinition.description`. */
|
|
@@ -42,36 +30,16 @@ interface OperationMeta {
|
|
|
42
30
|
interface EntityEntry {
|
|
43
31
|
name: string;
|
|
44
32
|
/** A live class in-process, a card from a frond whose class never crossed. */
|
|
45
|
-
entityClass:
|
|
33
|
+
entityClass: SchemaView;
|
|
46
34
|
exposed?: boolean;
|
|
47
35
|
}
|
|
48
|
-
/**
|
|
49
|
-
* Only what this projection reads of a scanned handler — five fields of nine.
|
|
50
|
-
*
|
|
51
|
-
* The narrowness is deliberate: a consumer that declares what it consumes accepts a
|
|
52
|
-
* minimal literal in a test and does not break when a field it ignores moves. What was
|
|
53
|
-
* NOT deliberate is that the two names it does read were spelled again here, so renaming
|
|
54
|
-
* `entityName` to `address` in core left this file compiling against a field the runtime
|
|
55
|
-
* object no longer carried — a silent break, caught only because the same commit touched
|
|
56
|
-
* the reads. `Pick` keeps the narrow view and makes core the one place the names live.
|
|
57
|
-
*
|
|
58
|
-
* `schema-graphql` holds a copy of this shape and cannot do the same: it depends on
|
|
59
|
-
* `@fougere/schema` and `@fougere/http`, never on core, which is what lets a projection
|
|
60
|
-
* package stay structurally typed. There the duplication is the doctrine, not an oversight.
|
|
61
|
-
*/
|
|
36
|
+
/** Only what this projection reads of a scanned handler — five fields of nine. */
|
|
62
37
|
type HandlerEntry = Pick<CoreHandlerEntry, 'address' | 'surface'> & {
|
|
63
38
|
/** `Crud(Post, PostPublic)` — the handler-wide output view, scoping every op. */
|
|
64
|
-
outputOverride?:
|
|
65
|
-
/**
|
|
66
|
-
* The scanned constructor, which carries the same statement made on the class.
|
|
67
|
-
*
|
|
68
|
-
* Typed as a constructor that MAY carry the static, not as `{ __output?: … }` alone:
|
|
69
|
-
* a type whose every property is optional is weak, and TS refuses a constructor that
|
|
70
|
-
* happens not to carry it — *has no properties in common*. So the narrow view was
|
|
71
|
-
* unassignable from the real entry for a second reason after `operations`.
|
|
72
|
-
*/
|
|
39
|
+
outputOverride?: SchemaView;
|
|
40
|
+
/** The scanned constructor, which carries the same statement made on the class. */
|
|
73
41
|
ctor?: (new (...args: never[]) => unknown) & {
|
|
74
|
-
__output?:
|
|
42
|
+
__output?: SchemaView;
|
|
75
43
|
};
|
|
76
44
|
};
|
|
77
45
|
interface PresenterEntry {
|
|
@@ -106,19 +74,6 @@ export interface GenerateRoutesOptions {
|
|
|
106
74
|
/** Surface name for filtering (e.g. 'rest', 'graphql'). Uses frond.config.ts surfaces if set. */
|
|
107
75
|
surface?: string;
|
|
108
76
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Generate REST route definitions from a fougere App.
|
|
111
|
-
*
|
|
112
|
-
* Conventions:
|
|
113
|
-
* - Entity name → pluralized base path (/posts, /authors)
|
|
114
|
-
* - list → GET /posts
|
|
115
|
-
* - findById → GET /posts/:id
|
|
116
|
-
* - create → POST /posts
|
|
117
|
-
* - update → PUT /posts/:id
|
|
118
|
-
* - delete → DELETE /posts/:id
|
|
119
|
-
* - Custom: searchByTitle → GET /posts/search-by-title
|
|
120
|
-
* - Custom with ById: archiveById → POST /posts/:id/archive
|
|
121
|
-
* - Override any route via options.overrides
|
|
122
|
-
*/
|
|
77
|
+
/** Generate REST route definitions from a fougere App. */
|
|
123
78
|
export declare function generateRoutes(app: AppLike, options?: GenerateRoutesOptions): RouteDefinition[];
|
|
124
79
|
//# sourceMappingURL=routes.d.ts.map
|
package/dist/routes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,KAAK,EAAS,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEjE,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMtE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,OAAO,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;CAItB;AAED,UAAU,aAAa;IACrB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,qDAAqD;IACrD,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,WAAW,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,kFAAkF;AAClF,KAAK,YAAY,GAAG,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,SAAS,CAAC,GAAG;IAClE,iFAAiF;IACjF,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,mFAAmF;IACnF,IAAI,CAAC,EAAE,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,GAAG;QAAE,QAAQ,CAAC,EAAE,UAAU,CAAA;KAAE,CAAC;CACxE,CAAC;AASF,UAAU,cAAc;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACrC;AAED,UAAU,OAAO;IACf,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,4EAA4E;IAC5E,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,SAAS,CAAC;IAClF,kDAAkD;IAClD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,SAAS,CAAC;CACzF;AA8DD,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,UAAU,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpG,uBAAuB;IACvB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7D,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,eAAe,EAAE,CAqF/F"}
|
package/dist/routes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Visibility } from '@fougere/schema';
|
|
2
2
|
// ─── Naming conventions ─────────────────────────
|
|
3
3
|
function hasById(name) {
|
|
4
4
|
return name.includes('ById') || name === 'findById' || name === 'update' || name === 'delete';
|
|
@@ -44,20 +44,7 @@ function derivePath(entityName, opName) {
|
|
|
44
44
|
const segment = cleanName.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase());
|
|
45
45
|
return withId ? `${base}/:id/${segment}` : `${base}/${segment}`;
|
|
46
46
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Generate REST route definitions from a fougere App.
|
|
49
|
-
*
|
|
50
|
-
* Conventions:
|
|
51
|
-
* - Entity name → pluralized base path (/posts, /authors)
|
|
52
|
-
* - list → GET /posts
|
|
53
|
-
* - findById → GET /posts/:id
|
|
54
|
-
* - create → POST /posts
|
|
55
|
-
* - update → PUT /posts/:id
|
|
56
|
-
* - delete → DELETE /posts/:id
|
|
57
|
-
* - Custom: searchByTitle → GET /posts/search-by-title
|
|
58
|
-
* - Custom with ById: archiveById → POST /posts/:id/archive
|
|
59
|
-
* - Override any route via options.overrides
|
|
60
|
-
*/
|
|
47
|
+
/** Generate REST route definitions from a fougere App. */
|
|
61
48
|
export function generateRoutes(app, options) {
|
|
62
49
|
const prefix = options?.prefix ?? '';
|
|
63
50
|
const overrides = options?.overrides ?? {};
|
|
@@ -85,13 +72,12 @@ export function generateRoutes(app, options) {
|
|
|
85
72
|
// proxy facades, which intentionally cannot enumerate their keys before discovery.
|
|
86
73
|
const opNames = [...effectiveOperations.keys()];
|
|
87
74
|
const entityOverrides = overrides[entity.name] ?? {};
|
|
88
|
-
// Use handler's output schema if declared, otherwise entity. A
|
|
89
|
-
//
|
|
90
|
-
// projects the same routes as a local one.
|
|
75
|
+
// Use handler's output schema if declared, otherwise entity. A frond whose class
|
|
76
|
+
// never crossed the wire arrives as one too: boot rebuilds the card before here.
|
|
91
77
|
const outputSchema = handler?.outputOverride
|
|
92
78
|
?? handler?.ctor?.__output
|
|
93
79
|
?? entity.entityClass;
|
|
94
|
-
const fields =
|
|
80
|
+
const fields = outputSchema.getFields();
|
|
95
81
|
for (const opName of opNames) {
|
|
96
82
|
const meta = effectiveOperations.get(opName);
|
|
97
83
|
if (!meta) {
|
|
@@ -105,13 +91,13 @@ export function generateRoutes(app, options) {
|
|
|
105
91
|
let inputFields;
|
|
106
92
|
let outputFields = Visibility.of(fields).output;
|
|
107
93
|
if (meta?.input) {
|
|
108
|
-
inputFields =
|
|
94
|
+
inputFields = meta.input.getFields();
|
|
109
95
|
}
|
|
110
96
|
else if (opName === 'create' || opName === 'update') {
|
|
111
97
|
inputFields = Visibility.of(fields).input;
|
|
112
98
|
}
|
|
113
99
|
if (meta?.output)
|
|
114
|
-
outputFields = Visibility.of(
|
|
100
|
+
outputFields = Visibility.of(meta.output.getFields()).output;
|
|
115
101
|
// Handler/method overrides are already executed by the facade from the same
|
|
116
102
|
// EffectiveOperation local and RPC use. The adapter never resolves DI itself.
|
|
117
103
|
const op = facade[opName];
|
package/dist/routes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/adapter-rest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-alpha.0",
|
|
4
4
|
"description": "Entity → REST routes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"src"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@fougere/core": "0.
|
|
33
|
-
"@fougere/schema": "0.
|
|
34
|
-
"@fougere/http": "0.
|
|
32
|
+
"@fougere/core": "0.8.0-alpha.0",
|
|
33
|
+
"@fougere/schema": "0.8.0-alpha.0",
|
|
34
|
+
"@fougere/http": "0.8.0-alpha.0"
|
|
35
35
|
},
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
package/src/register.ts
CHANGED
|
@@ -5,14 +5,7 @@ import type { HttpRouter } from '@fougere/http';
|
|
|
5
5
|
import { toHttpError } from '@fougere/core';
|
|
6
6
|
import type { RouteDefinition } from './routes.js';
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
* Register all route definitions on an HttpRouter.
|
|
10
|
-
*
|
|
11
|
-
* ```ts
|
|
12
|
-
* const routes = generateRoutes(app, { prefix: '/api' });
|
|
13
|
-
* registerRoutes(router, routes);
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
8
|
+
/** Register all route definitions on an HttpRouter. */
|
|
16
9
|
export function registerRoutes(
|
|
17
10
|
router: HttpRouter,
|
|
18
11
|
routes: RouteDefinition[],
|
|
@@ -24,7 +17,7 @@ export function registerRoutes(
|
|
|
24
17
|
const invocation = {
|
|
25
18
|
params: ctx.params,
|
|
26
19
|
query: ctx.query,
|
|
27
|
-
body,
|
|
20
|
+
input: body,
|
|
28
21
|
state: ctx.state,
|
|
29
22
|
};
|
|
30
23
|
|
package/src/routes.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Framework-agnostic: produces RouteDefinition[], consumed by an adapter (Fastify, Express, etc).
|
|
5
|
-
*/
|
|
6
|
-
import type { Field, Fields, SchemaOrCard } from '@fougere/schema';
|
|
7
|
-
import { fieldsOf, Visibility } from '@fougere/schema';
|
|
1
|
+
/** @fougere/adapter-rest — generates REST route definitions from fougere handlers. */
|
|
2
|
+
import type { Field, Fields, SchemaView } from '@fougere/schema';
|
|
3
|
+
import { Visibility } from '@fougere/schema';
|
|
8
4
|
import type { HandlerEntry as CoreHandlerEntry } from '@fougere/core';
|
|
9
5
|
|
|
10
6
|
// ─── Types ──────────────────────────────────────
|
|
@@ -27,15 +23,7 @@ export interface RouteDefinition {
|
|
|
27
23
|
outputFields?: Fields;
|
|
28
24
|
/** Explicit success status. Defaults to 201 only for the canonical `create` op. */
|
|
29
25
|
successStatus?: number;
|
|
30
|
-
/**
|
|
31
|
-
* The operation in words — the method's own doc sentence, carried by the contract.
|
|
32
|
-
*
|
|
33
|
-
* A route is what an OpenAPI `summary` is generated FROM, and the sentence reached
|
|
34
|
-
* this file already through core's `EffectiveOperation` table;
|
|
35
|
-
* only this type had no name for it, so every generated route was undocumented while
|
|
36
|
-
* the sentence sat one property away. Carried, not rendered: emitting OpenAPI is a
|
|
37
|
-
* reader's job, and this is what it reads.
|
|
38
|
-
*/
|
|
26
|
+
/** The operation in words — the method's own doc sentence, carried by the contract. */
|
|
39
27
|
description?: string;
|
|
40
28
|
// No presenter here. A route used to carry the instance and its field names so the
|
|
41
29
|
// registration could enrich each row; the façade does that for every door now
|
|
@@ -43,8 +31,8 @@ export interface RouteDefinition {
|
|
|
43
31
|
}
|
|
44
32
|
|
|
45
33
|
interface OperationMeta {
|
|
46
|
-
input?:
|
|
47
|
-
output?:
|
|
34
|
+
input?: SchemaView;
|
|
35
|
+
output?: SchemaView;
|
|
48
36
|
/** Canonical kind from core's EffectiveOperation. */
|
|
49
37
|
kind: 'query' | 'command';
|
|
50
38
|
/** The operation in words — see `RouteDefinition.description`. */
|
|
@@ -54,36 +42,16 @@ interface OperationMeta {
|
|
|
54
42
|
interface EntityEntry {
|
|
55
43
|
name: string;
|
|
56
44
|
/** A live class in-process, a card from a frond whose class never crossed. */
|
|
57
|
-
entityClass:
|
|
45
|
+
entityClass: SchemaView;
|
|
58
46
|
exposed?: boolean;
|
|
59
47
|
}
|
|
60
48
|
|
|
61
|
-
/**
|
|
62
|
-
* Only what this projection reads of a scanned handler — five fields of nine.
|
|
63
|
-
*
|
|
64
|
-
* The narrowness is deliberate: a consumer that declares what it consumes accepts a
|
|
65
|
-
* minimal literal in a test and does not break when a field it ignores moves. What was
|
|
66
|
-
* NOT deliberate is that the two names it does read were spelled again here, so renaming
|
|
67
|
-
* `entityName` to `address` in core left this file compiling against a field the runtime
|
|
68
|
-
* object no longer carried — a silent break, caught only because the same commit touched
|
|
69
|
-
* the reads. `Pick` keeps the narrow view and makes core the one place the names live.
|
|
70
|
-
*
|
|
71
|
-
* `schema-graphql` holds a copy of this shape and cannot do the same: it depends on
|
|
72
|
-
* `@fougere/schema` and `@fougere/http`, never on core, which is what lets a projection
|
|
73
|
-
* package stay structurally typed. There the duplication is the doctrine, not an oversight.
|
|
74
|
-
*/
|
|
49
|
+
/** Only what this projection reads of a scanned handler — five fields of nine. */
|
|
75
50
|
type HandlerEntry = Pick<CoreHandlerEntry, 'address' | 'surface'> & {
|
|
76
51
|
/** `Crud(Post, PostPublic)` — the handler-wide output view, scoping every op. */
|
|
77
|
-
outputOverride?:
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
* Typed as a constructor that MAY carry the static, not as `{ __output?: … }` alone:
|
|
82
|
-
* a type whose every property is optional is weak, and TS refuses a constructor that
|
|
83
|
-
* happens not to carry it — *has no properties in common*. So the narrow view was
|
|
84
|
-
* unassignable from the real entry for a second reason after `operations`.
|
|
85
|
-
*/
|
|
86
|
-
ctor?: (new (...args: never[]) => unknown) & { __output?: SchemaOrCard };
|
|
52
|
+
outputOverride?: SchemaView;
|
|
53
|
+
/** The scanned constructor, which carries the same statement made on the class. */
|
|
54
|
+
ctor?: (new (...args: never[]) => unknown) & { __output?: SchemaView };
|
|
87
55
|
};
|
|
88
56
|
// No `operations` here, and its absence is the point. It was declared, never read — this
|
|
89
57
|
// file takes its table from `app.operationsFor()` — and it carried `OperationMeta`, whose
|
|
@@ -185,20 +153,7 @@ export interface GenerateRoutesOptions {
|
|
|
185
153
|
surface?: string;
|
|
186
154
|
}
|
|
187
155
|
|
|
188
|
-
/**
|
|
189
|
-
* Generate REST route definitions from a fougere App.
|
|
190
|
-
*
|
|
191
|
-
* Conventions:
|
|
192
|
-
* - Entity name → pluralized base path (/posts, /authors)
|
|
193
|
-
* - list → GET /posts
|
|
194
|
-
* - findById → GET /posts/:id
|
|
195
|
-
* - create → POST /posts
|
|
196
|
-
* - update → PUT /posts/:id
|
|
197
|
-
* - delete → DELETE /posts/:id
|
|
198
|
-
* - Custom: searchByTitle → GET /posts/search-by-title
|
|
199
|
-
* - Custom with ById: archiveById → POST /posts/:id/archive
|
|
200
|
-
* - Override any route via options.overrides
|
|
201
|
-
*/
|
|
156
|
+
/** Generate REST route definitions from a fougere App. */
|
|
202
157
|
export function generateRoutes(app: AppLike, options?: GenerateRoutesOptions): RouteDefinition[] {
|
|
203
158
|
const prefix = options?.prefix ?? '';
|
|
204
159
|
const overrides = options?.overrides ?? {};
|
|
@@ -230,13 +185,12 @@ export function generateRoutes(app: AppLike, options?: GenerateRoutesOptions): R
|
|
|
230
185
|
// proxy facades, which intentionally cannot enumerate their keys before discovery.
|
|
231
186
|
const opNames = [...effectiveOperations.keys()];
|
|
232
187
|
const entityOverrides = overrides[entity.name] ?? {};
|
|
233
|
-
// Use handler's output schema if declared, otherwise entity. A
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
const outputSchema: SchemaOrCard = handler?.outputOverride
|
|
188
|
+
// Use handler's output schema if declared, otherwise entity. A frond whose class
|
|
189
|
+
// never crossed the wire arrives as one too: boot rebuilds the card before here.
|
|
190
|
+
const outputSchema: SchemaView = handler?.outputOverride
|
|
237
191
|
?? handler?.ctor?.__output
|
|
238
192
|
?? entity.entityClass;
|
|
239
|
-
const fields =
|
|
193
|
+
const fields = outputSchema.getFields();
|
|
240
194
|
|
|
241
195
|
for (const opName of opNames) {
|
|
242
196
|
const meta = effectiveOperations.get(opName);
|
|
@@ -254,11 +208,11 @@ export function generateRoutes(app: AppLike, options?: GenerateRoutesOptions): R
|
|
|
254
208
|
let inputFields: Fields | undefined;
|
|
255
209
|
let outputFields: Fields | undefined = Visibility.of(fields).output;
|
|
256
210
|
if (meta?.input) {
|
|
257
|
-
inputFields =
|
|
211
|
+
inputFields = meta.input.getFields();
|
|
258
212
|
} else if (opName === 'create' || opName === 'update') {
|
|
259
213
|
inputFields = Visibility.of(fields).input;
|
|
260
214
|
}
|
|
261
|
-
if (meta?.output) outputFields = Visibility.of(
|
|
215
|
+
if (meta?.output) outputFields = Visibility.of(meta.output.getFields()).output;
|
|
262
216
|
|
|
263
217
|
// Handler/method overrides are already executed by the facade from the same
|
|
264
218
|
// EffectiveOperation local and RPC use. The adapter never resolves DI itself.
|