@fougere/adapter-rest 0.16.7-alpha.0 → 0.17.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.
@@ -1,16 +1,15 @@
1
1
  import type { HttpMethod } from '@fougere/http';
2
- import type { EntityEntry } from './EntityEntry.js';
3
2
  export interface GenerateRoutesOptions {
4
3
  /** Base path prefix (e.g. '/api'). Default: ''. */
5
4
  prefix?: string;
6
- /** Override route config per entity. */
5
+ /** Override route config per address, then per operation. */
7
6
  overrides?: Record<string, Record<string, {
8
7
  method?: HttpMethod;
9
8
  path?: string;
10
9
  status?: number;
11
10
  }>>;
12
- /** Filter entities. */
13
- filter?: (entity: EntityEntry, frondName: string) => boolean;
11
+ /** Keep only the addresses this answers true for. */
12
+ filter?: (address: string, frondName: string) => boolean;
14
13
  /** Surface name for filtering (e.g. 'rest', 'graphql'). Uses frond.config.ts surfaces if set. */
15
14
  surface?: string;
16
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GenerateRoutesOptions.d.ts","sourceRoot":"","sources":["../../src/routes/GenerateRoutesOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,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"}
1
+ {"version":3,"file":"GenerateRoutesOptions.d.ts","sourceRoot":"","sources":["../../src/routes/GenerateRoutesOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,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,qDAAqD;IACrD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IACzD,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
@@ -1,14 +1,14 @@
1
- import type { Fields, SchemaView } from '@fougere/schema';
1
+ import type { Fields } from '@fougere/schema';
2
2
  import type { HttpMethod } from '@fougere/http';
3
3
  import type { HandlerEntry as CoreHandlerEntry } from '@fougere/core';
4
- import type { EntityEntry } from './EntityEntry.js';
5
4
  import type { OperationMeta } from './OperationMeta.js';
6
5
  import type { GenerateRoutesOptions } from './GenerateRoutesOptions.js';
7
6
  export interface RouteDefinition {
8
7
  method: HttpMethod;
9
8
  path: string;
10
9
  operationName: string;
11
- entityName: string;
10
+ /** Where the operation answers — `checkout` in `checkout.pay`. */
11
+ address: string;
12
12
  /** Handler facade method to call (receives InvocationContext). */
13
13
  handler: (invocation: unknown) => Promise<unknown>;
14
14
  /** Input schema (for validation / JSON schema generation). */
@@ -20,22 +20,14 @@ export interface RouteDefinition {
20
20
  /** The operation in words — the method's own doc sentence, carried by the contract. */
21
21
  description?: string;
22
22
  }
23
- /** Only what this projection reads of a scanned handler — five fields of nine. */
24
- type HandlerEntry = Pick<CoreHandlerEntry, 'address' | 'surface'> & {
25
- /** `Crud(Post, PostPublic)` — the handler-wide output view, scoping every op. */
26
- outputOverride?: SchemaView;
27
- /** The scanned constructor, which carries the same statement made on the class. */
28
- ctor?: (new (...args: never[]) => unknown) & {
29
- __output?: SchemaView;
30
- };
31
- };
23
+ /** Only what this projection reads of a scanned handler. */
24
+ type HandlerEntry = Pick<CoreHandlerEntry, 'address' | 'surface' | 'exposed'>;
32
25
  interface PresenterEntry {
33
26
  entityName: string;
34
27
  fields: string[];
35
28
  }
36
29
  interface FrondLike {
37
30
  name: string;
38
- entities: EntityEntry[];
39
31
  handlers: HandlerEntry[];
40
32
  presenters: PresenterEntry[];
41
33
  surfaces?: Record<string, string[]>;
@@ -50,12 +42,12 @@ interface FrondLike {
50
42
  }
51
43
  interface AppLike {
52
44
  fronds: FrondLike[];
53
- /** The façade an entity exposes to one audience — `undefined` when none. */
54
- facadeFor(entity: string, surface?: string): Record<string, Function> | undefined;
45
+ /** The façade an address serves to one audience — `undefined` when none. */
46
+ facadeFor(address: string, surface?: string): Record<string, Function> | undefined;
55
47
  /** Canonical operation table produced by core. */
56
- operationsFor(entity: string, surface?: string): Map<string, OperationMeta> | undefined;
48
+ operationsFor(address: string, surface?: string): Map<string, OperationMeta> | undefined;
57
49
  }
58
- /** Generate REST route definitions from a fougere App. */
50
+ /** Generate REST route definitions from a fougere App — one route per operation, per address served. */
59
51
  export declare function generateRoutes(app: AppLike, options?: GenerateRoutesOptions): RouteDefinition[];
60
52
  export {};
61
53
  //# sourceMappingURL=RouteDefinition.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouteDefinition.d.ts","sourceRoot":"","sources":["../../src/routes/RouteDefinition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,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,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;IACpC,yFAAyF;IACzF,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;CACtG;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,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,qBAA0B,GAAG,eAAe,EAAE,CAGnG"}
1
+ {"version":3,"file":"RouteDefinition.d.ts","sourceRoot":"","sources":["../../src/routes/RouteDefinition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,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,4DAA4D;AAC5D,KAAK,YAAY,GAAG,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC;AAS9E,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,YAAY,EAAE,CAAC;IACzB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACpC,yFAAyF;IACzF,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;CACtG;AAED,UAAU,OAAO;IACf,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,4EAA4E;IAC5E,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,SAAS,CAAC;IACnF,kDAAkD;IAClD,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,SAAS,CAAC;CAC1F;AA8DD,wGAAwG;AACxG,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,qBAA0B,GAAG,eAAe,EAAE,CAInG"}
@@ -24,9 +24,9 @@ function deriveMethod(opName, resolvedKind) {
24
24
  return 'DELETE';
25
25
  return 'POST';
26
26
  }
27
- /** Derive route path from entity name + operation name. */
28
- function derivePath(entityName, opName) {
29
- const base = `/${pluralize(entityName)}`;
27
+ /** Derive route path from the address + operation name. */
28
+ function derivePath(address, opName) {
29
+ const base = `/${pluralize(address)}`;
30
30
  // Standard CRUD
31
31
  if (opName === 'list')
32
32
  return base;
@@ -49,52 +49,49 @@ function derivePath(entityName, opName) {
49
49
  // route's MODE (operationName 'update' → patch), never forged per-field flags.
50
50
  // Membership is the axes-derived `Visibility.input` projection from @fougere/schema.
51
51
  // ─── Public API ─────────────────────────────────
52
- /** Generate REST route definitions from a fougere App. */
52
+ /** Generate REST route definitions from a fougere App — one route per operation, per address served. */
53
53
  export function generateRoutes(app, options = {}) {
54
- return app.fronds.flatMap((frond) => frond.entities.flatMap((entity) => routesOf(app, frond, entity, options)));
54
+ return app.fronds.flatMap((frond) => [...new Set(frond.handlers.map((handler) => handler.address))]
55
+ .flatMap((address) => routesOf(app, frond, address, options)));
55
56
  }
56
- /** What one entity answers on REST — nothing, when this surface does not serve it. */
57
- function routesOf(app, frond, entity, options) {
57
+ /** What one address answers on REST — nothing, when this surface does not serve it. */
58
+ function routesOf(app, frond, address, options) {
58
59
  const surface = options.surface;
59
60
  // Membership is core's answer, not ours — one rule, read here (see App.facadeFor).
60
- const facade = app.facadeFor(entity.name, surface);
61
+ const facade = app.facadeFor(address, surface);
61
62
  if (!facade)
62
63
  return [];
63
- if (options.filter && !options.filter(entity, frond.name))
64
+ if (options.filter && !options.filter(address, frond.name))
64
65
  return [];
65
- if (!surface && entity.exposed === false)
66
+ const handler = (surface
67
+ ? frond.handlers.find((one) => one.address === address && one.surface === surface)
68
+ : undefined) ?? frond.handlers.find((one) => !one.surface && one.address === address);
69
+ if (!surface && handler?.exposed === false)
66
70
  return [];
67
- const effectiveOperations = app.operationsFor(entity.name, surface);
71
+ const effectiveOperations = app.operationsFor(address, surface);
68
72
  if (!effectiveOperations) {
69
- throw new Error(`REST cannot project '${entity.name}' without its EffectiveOperation table.`);
73
+ throw new Error(`REST cannot project '${address}' without its EffectiveOperation table.`);
70
74
  }
71
- const handler = (surface
72
- ? frond.handlers.find((one) => one.address === entity.name && one.surface === surface)
73
- : undefined) ?? frond.handlers.find((one) => !one.surface && one.address === entity.name);
74
- // The handler's output schema if declared, otherwise the entity. A frond whose class never
75
- // crossed the wire arrives as one too: boot rebuilds the card before here.
76
- const outputSchema = handler?.outputOverride ?? handler?.ctor?.__output ?? entity.entityClass;
77
75
  // The resolved table defines the public operation set. This also works for remote proxy
78
76
  // facades, which intentionally cannot enumerate their keys before discovery.
79
77
  return [...effectiveOperations.keys()].map((opName) => routeFor({
80
- entity,
78
+ address,
81
79
  frond,
82
80
  facade,
83
- fields: outputSchema.getFields(),
84
81
  opName,
85
82
  meta: effectiveOperations.get(opName),
86
83
  options,
87
84
  }));
88
85
  }
89
- function routeFor({ entity, frond, facade, fields, opName, meta, options }) {
86
+ function routeFor({ address, frond, facade, opName, meta, options }) {
90
87
  if (!meta) {
91
- throw new Error(`REST facade '${entity.name}' exposes '${opName}' but its EffectiveOperation table does not.`);
88
+ throw new Error(`REST facade '${address}' exposes '${opName}' but its EffectiveOperation table does not.`);
92
89
  }
93
90
  // What the FROND said, then what this call said, then the convention. The frond comes first
94
91
  // because it names the operation; a host that overrides is deciding for someone else's,
95
92
  // which is what `overrides:` is for and why it wins.
96
93
  const stated = frond.operationsOverrides?.[opName]?.rest;
97
- const override = { ...stated, ...(options.overrides?.[entity.name] ?? {})[opName] };
94
+ const override = { ...stated, ...(options.overrides?.[address] ?? {})[opName] };
98
95
  // Handler and method overrides are already executed by the facade, from the same
99
96
  // EffectiveOperation local and RPC use. The adapter never resolves DI itself.
100
97
  const op = facade[opName];
@@ -103,22 +100,20 @@ function routeFor({ entity, frond, facade, fields, opName, meta, options }) {
103
100
  }
104
101
  return {
105
102
  method: override.method ?? deriveMethod(opName, meta.kind),
106
- path: (options.prefix ?? '') + (override.path ?? derivePath(entity.name, opName)),
103
+ path: (options.prefix ?? '') + (override.path ?? derivePath(address, opName)),
107
104
  operationName: opName,
108
- entityName: entity.name,
105
+ address,
109
106
  handler: (invocation) => op(invocation),
110
- ...inputAndOutput(meta, fields, opName),
107
+ ...inputAndOutput(meta),
111
108
  successStatus: override.status,
112
109
  ...(meta.description && { description: meta.description }),
113
110
  };
114
111
  }
115
112
  /** Both pass through the client-surface projections — write-only out, read-only in. */
116
- function inputAndOutput(meta, fields, opName) {
117
- const inputFields = meta.input?.getFields()
118
- ?? (opName === 'create' || opName === 'update' ? Visibility.of(fields).input : undefined);
113
+ function inputAndOutput(meta) {
119
114
  return {
120
- inputFields,
121
- outputFields: meta.output ? Visibility.of(meta.output.getFields()).output : Visibility.of(fields).output,
115
+ inputFields: meta.input ? Visibility.of(meta.input.getFields()).input : undefined,
116
+ outputFields: meta.output ? Visibility.of(meta.output.getFields()).output : undefined,
122
117
  };
123
118
  }
124
119
  //# sourceMappingURL=RouteDefinition.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouteDefinition.js","sourceRoot":"","sources":["../../src/routes/RouteDefinition.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RouteDefinition.js","sourceRoot":"","sources":["../../src/routes/RouteDefinition.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA6D7C,mDAAmD;AAEnD,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,CAAC;AAChG,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QACvB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK;QAC3B,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC;AACjB,CAAC;AAED,kFAAkF;AAClF,SAAS,YAAY,CACnB,MAAc,EACd,YAAmC;IAEnC,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,wBAAwB,MAAM,yCAAyC;cACrE,uFAAuF,CAC1F,CAAC;IACJ,CAAC;IACD,IAAI,YAAY,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,MAAM,CAAC;IAC/C,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3E,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEhF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,2DAA2D;AAC3D,SAAS,UAAU,CAAC,OAAe,EAAE,MAAc;IACjD,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;IAEtC,gBAAgB;IAChB,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,GAAG,IAAI,MAAM,CAAC;IAChD,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,GAAG,IAAI,MAAM,CAAC;IAC9C,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,GAAG,IAAI,MAAM,CAAC;IAE9C,0CAA0C;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAE1E,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC;AAClE,CAAC;AAED,mDAAmD;AAEnD,iFAAiF;AACjF,+EAA+E;AAC/E,qFAAqF;AAErF,mDAAmD;AAEnD,wGAAwG;AACxG,MAAM,UAAU,cAAc,CAAC,GAAY,EAAE,OAAO,GAA0B,EAAE;IAC9E,OAAO,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAClC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;SAC3D,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,uFAAuF;AACvF,SAAS,QAAQ,CACf,GAAY,EACZ,KAAgB,EAChB,OAAe,EACf,OAA8B;IAE9B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,mFAAmF;IACnF,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAA8B,CAAC;IAC5E,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtE,MAAM,OAAO,GAAG,CAAC,OAAO;QACtB,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,KAAK,OAAO,IAAI,GAAG,CAAC,OAAO,KAAK,OAAO,CAAC;QAClF,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IACxF,IAAI,CAAC,OAAO,IAAI,OAAO,EAAE,OAAO,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtD,MAAM,mBAAmB,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChE,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,wBAAwB,OAAO,yCAAyC,CAAC,CAAC;IAC5F,CAAC;IAED,wFAAwF;IACxF,6EAA6E;IAC7E,OAAO,CAAC,GAAG,mBAAmB,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC;QAC9D,OAAO;QACP,KAAK;QACL,MAAM;QACN,MAAM;QACN,IAAI,EAAE,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC;QACrC,OAAO;KACR,CAAC,CAAC,CAAC;AACN,CAAC;AAYD,SAAS,QAAQ,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAc;IAC7E,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,gBAAgB,OAAO,cAAc,MAAM,8CAA8C,CAC1F,CAAC;IACJ,CAAC;IAED,4FAA4F;IAC5F,wFAAwF;IACxF,qDAAqD;IACrD,MAAM,MAAM,GAAG,KAAK,CAAC,mBAAmB,EAAE,CAAC,MAAM,CAAC,EAAE,IACiB,CAAC;IACtE,MAAM,QAAQ,GAAG,EAAE,GAAG,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EACpB,CAAC;IAE1D,iFAAiF;IACjF,8EAA8E;IAC9E,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1B,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,0CAA0C,MAAM,4BAA4B,CAAC,CAAC;IAChG,CAAC;IAED,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;QAC1D,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7E,aAAa,EAAE,MAAM;QACrB,OAAO;QACP,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC;QACvC,GAAG,cAAc,CAAC,IAAI,CAAC;QACvB,aAAa,EAAE,QAAQ,CAAC,MAAM;QAC9B,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;KAC3D,CAAC;AACJ,CAAC;AAED,uFAAuF;AACvF,SAAS,cAAc,CAAC,IAAmB;IACzC,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QACjF,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;KACtF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fougere/adapter-rest",
3
- "version": "0.16.7-alpha.0",
3
+ "version": "0.17.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.16.7-alpha.0",
33
- "@fougere/schema": "0.16.7-alpha.0",
34
- "@fougere/http": "0.16.7-alpha.0"
32
+ "@fougere/core": "0.17.0-alpha.0",
33
+ "@fougere/schema": "0.17.0-alpha.0",
34
+ "@fougere/http": "0.17.0-alpha.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
@@ -1,13 +1,12 @@
1
1
  import type { HttpMethod } from '@fougere/http';
2
- import type { EntityEntry } from './EntityEntry.js';
3
2
 
4
3
  export interface GenerateRoutesOptions {
5
4
  /** Base path prefix (e.g. '/api'). Default: ''. */
6
5
  prefix?: string;
7
- /** Override route config per entity. */
6
+ /** Override route config per address, then per operation. */
8
7
  overrides?: Record<string, Record<string, { method?: HttpMethod; path?: string; status?: number }>>;
9
- /** Filter entities. */
10
- filter?: (entity: EntityEntry, frondName: string) => boolean;
8
+ /** Keep only the addresses this answers true for. */
9
+ filter?: (address: string, frondName: string) => boolean;
11
10
  /** Surface name for filtering (e.g. 'rest', 'graphql'). Uses frond.config.ts surfaces if set. */
12
11
  surface?: string;
13
12
  }
@@ -1,8 +1,7 @@
1
- import type { Fields, SchemaView } from '@fougere/schema';
1
+ import type { Fields } from '@fougere/schema';
2
2
  import { Visibility } from '@fougere/schema';
3
3
  import type { HttpMethod } from '@fougere/http';
4
4
  import type { HandlerEntry as CoreHandlerEntry } from '@fougere/core';
5
- import type { EntityEntry } from './EntityEntry.js';
6
5
  import type { OperationMeta } from './OperationMeta.js';
7
6
  import type { GenerateRoutesOptions } from './GenerateRoutesOptions.js';
8
7
 
@@ -10,7 +9,8 @@ export interface RouteDefinition {
10
9
  method: HttpMethod;
11
10
  path: string;
12
11
  operationName: string;
13
- entityName: string;
12
+ /** Where the operation answers — `checkout` in `checkout.pay`. */
13
+ address: string;
14
14
  /** Handler facade method to call (receives InvocationContext). */
15
15
  handler: (invocation: unknown) => Promise<unknown>;
16
16
  /** Input schema (for validation / JSON schema generation). */
@@ -26,13 +26,8 @@ export interface RouteDefinition {
26
26
  // (`PresenterExecutor`), so the rows arrive computed and a second pass was duplicated work.
27
27
  }
28
28
 
29
- /** Only what this projection reads of a scanned handler — five fields of nine. */
30
- type HandlerEntry = Pick<CoreHandlerEntry, 'address' | 'surface'> & {
31
- /** `Crud(Post, PostPublic)` — the handler-wide output view, scoping every op. */
32
- outputOverride?: SchemaView;
33
- /** The scanned constructor, which carries the same statement made on the class. */
34
- ctor?: (new (...args: never[]) => unknown) & { __output?: SchemaView };
35
- };
29
+ /** Only what this projection reads of a scanned handler. */
30
+ type HandlerEntry = Pick<CoreHandlerEntry, 'address' | 'surface' | 'exposed'>;
36
31
  // No `operations` here, and its absence is the point. It was declared, never read — this
37
32
  // file takes its table from `app.operationsFor()` — and it carried `OperationMeta`, whose
38
33
  // `kind` is REQUIRED because that is true of an EffectiveOperation. A scanned handler's
@@ -48,7 +43,6 @@ interface PresenterEntry {
48
43
 
49
44
  interface FrondLike {
50
45
  name: string;
51
- entities: EntityEntry[];
52
46
  handlers: HandlerEntry[];
53
47
  presenters: PresenterEntry[];
54
48
  surfaces?: Record<string, string[]>;
@@ -58,10 +52,10 @@ interface FrondLike {
58
52
 
59
53
  interface AppLike {
60
54
  fronds: FrondLike[];
61
- /** The façade an entity exposes to one audience — `undefined` when none. */
62
- facadeFor(entity: string, surface?: string): Record<string, Function> | undefined;
55
+ /** The façade an address serves to one audience — `undefined` when none. */
56
+ facadeFor(address: string, surface?: string): Record<string, Function> | undefined;
63
57
  /** Canonical operation table produced by core. */
64
- operationsFor(entity: string, surface?: string): Map<string, OperationMeta> | undefined;
58
+ operationsFor(address: string, surface?: string): Map<string, OperationMeta> | undefined;
65
59
  }
66
60
 
67
61
  type HandlerFacade = Record<string, Function>;
@@ -97,9 +91,9 @@ function deriveMethod(
97
91
  return 'POST';
98
92
  }
99
93
 
100
- /** Derive route path from entity name + operation name. */
101
- function derivePath(entityName: string, opName: string): string {
102
- const base = `/${pluralize(entityName)}`;
94
+ /** Derive route path from the address + operation name. */
95
+ function derivePath(address: string, opName: string): string {
96
+ const base = `/${pluralize(address)}`;
103
97
 
104
98
  // Standard CRUD
105
99
  if (opName === 'list') return base;
@@ -124,66 +118,62 @@ function derivePath(entityName: string, opName: string): string {
124
118
 
125
119
  // ─── Public API ─────────────────────────────────
126
120
 
127
- /** Generate REST route definitions from a fougere App. */
121
+ /** Generate REST route definitions from a fougere App — one route per operation, per address served. */
128
122
  export function generateRoutes(app: AppLike, options: GenerateRoutesOptions = {}): RouteDefinition[] {
129
123
  return app.fronds.flatMap((frond) =>
130
- frond.entities.flatMap((entity) => routesOf(app, frond, entity, options)));
124
+ [...new Set(frond.handlers.map((handler) => handler.address))]
125
+ .flatMap((address) => routesOf(app, frond, address, options)));
131
126
  }
132
127
 
133
- /** What one entity answers on REST — nothing, when this surface does not serve it. */
128
+ /** What one address answers on REST — nothing, when this surface does not serve it. */
134
129
  function routesOf(
135
130
  app: AppLike,
136
131
  frond: FrondLike,
137
- entity: EntityEntry,
132
+ address: string,
138
133
  options: GenerateRoutesOptions,
139
134
  ): RouteDefinition[] {
140
135
  const surface = options.surface;
141
136
  // Membership is core's answer, not ours — one rule, read here (see App.facadeFor).
142
- const facade = app.facadeFor(entity.name, surface) as HandlerFacade | undefined;
137
+ const facade = app.facadeFor(address, surface) as HandlerFacade | undefined;
143
138
  if (!facade) return [];
144
- if (options.filter && !options.filter(entity, frond.name)) return [];
145
- if (!surface && entity.exposed === false) return [];
139
+ if (options.filter && !options.filter(address, frond.name)) return [];
140
+
141
+ const handler = (surface
142
+ ? frond.handlers.find((one) => one.address === address && one.surface === surface)
143
+ : undefined) ?? frond.handlers.find((one) => !one.surface && one.address === address);
144
+ if (!surface && handler?.exposed === false) return [];
146
145
 
147
- const effectiveOperations = app.operationsFor(entity.name, surface);
146
+ const effectiveOperations = app.operationsFor(address, surface);
148
147
  if (!effectiveOperations) {
149
- throw new Error(`REST cannot project '${entity.name}' without its EffectiveOperation table.`);
148
+ throw new Error(`REST cannot project '${address}' without its EffectiveOperation table.`);
150
149
  }
151
150
 
152
- const handler = (surface
153
- ? frond.handlers.find((one) => one.address === entity.name && one.surface === surface)
154
- : undefined) ?? frond.handlers.find((one) => !one.surface && one.address === entity.name);
155
- // The handler's output schema if declared, otherwise the entity. A frond whose class never
156
- // crossed the wire arrives as one too: boot rebuilds the card before here.
157
- const outputSchema: SchemaView = handler?.outputOverride ?? handler?.ctor?.__output ?? entity.entityClass;
158
-
159
151
  // The resolved table defines the public operation set. This also works for remote proxy
160
152
  // facades, which intentionally cannot enumerate their keys before discovery.
161
153
  return [...effectiveOperations.keys()].map((opName) => routeFor({
162
- entity,
154
+ address,
163
155
  frond,
164
156
  facade,
165
- fields: outputSchema.getFields(),
166
157
  opName,
167
158
  meta: effectiveOperations.get(opName),
168
159
  options,
169
160
  }));
170
161
  }
171
162
 
172
- /** One operation of one entity, and everything that decides how it is addressed. */
163
+ /** One operation at one address, and everything that decides how it is addressed. */
173
164
  interface Projecting {
174
- entity: EntityEntry;
165
+ address: string;
175
166
  frond: FrondLike;
176
167
  facade: HandlerFacade;
177
- fields: Fields;
178
168
  opName: string;
179
169
  meta: OperationMeta | undefined;
180
170
  options: GenerateRoutesOptions;
181
171
  }
182
172
 
183
- function routeFor({ entity, frond, facade, fields, opName, meta, options }: Projecting): RouteDefinition {
173
+ function routeFor({ address, frond, facade, opName, meta, options }: Projecting): RouteDefinition {
184
174
  if (!meta) {
185
175
  throw new Error(
186
- `REST facade '${entity.name}' exposes '${opName}' but its EffectiveOperation table does not.`,
176
+ `REST facade '${address}' exposes '${opName}' but its EffectiveOperation table does not.`,
187
177
  );
188
178
  }
189
179
 
@@ -192,7 +182,7 @@ function routeFor({ entity, frond, facade, fields, opName, meta, options }: Proj
192
182
  // which is what `overrides:` is for and why it wins.
193
183
  const stated = frond.operationsOverrides?.[opName]?.rest as
194
184
  { method?: HttpMethod; path?: string; status?: number } | undefined;
195
- const override = { ...stated, ...(options.overrides?.[entity.name] ?? {})[opName] } as
185
+ const override = { ...stated, ...(options.overrides?.[address] ?? {})[opName] } as
196
186
  { method?: HttpMethod; path?: string; status?: number };
197
187
 
198
188
  // Handler and method overrides are already executed by the facade, from the same
@@ -204,27 +194,20 @@ function routeFor({ entity, frond, facade, fields, opName, meta, options }: Proj
204
194
 
205
195
  return {
206
196
  method: override.method ?? deriveMethod(opName, meta.kind),
207
- path: (options.prefix ?? '') + (override.path ?? derivePath(entity.name, opName)),
197
+ path: (options.prefix ?? '') + (override.path ?? derivePath(address, opName)),
208
198
  operationName: opName,
209
- entityName: entity.name,
199
+ address,
210
200
  handler: (invocation) => op(invocation),
211
- ...inputAndOutput(meta, fields, opName),
201
+ ...inputAndOutput(meta),
212
202
  successStatus: override.status,
213
203
  ...(meta.description && { description: meta.description }),
214
204
  };
215
205
  }
216
206
 
217
207
  /** Both pass through the client-surface projections — write-only out, read-only in. */
218
- function inputAndOutput(
219
- meta: OperationMeta,
220
- fields: Fields,
221
- opName: string,
222
- ): { inputFields: Fields | undefined; outputFields: Fields | undefined } {
223
- const inputFields = meta.input?.getFields()
224
- ?? (opName === 'create' || opName === 'update' ? Visibility.of(fields).input : undefined);
225
-
208
+ function inputAndOutput(meta: OperationMeta): { inputFields: Fields | undefined; outputFields: Fields | undefined } {
226
209
  return {
227
- inputFields,
228
- outputFields: meta.output ? Visibility.of(meta.output.getFields()).output : Visibility.of(fields).output,
210
+ inputFields: meta.input ? Visibility.of(meta.input.getFields()).input : undefined,
211
+ outputFields: meta.output ? Visibility.of(meta.output.getFields()).output : undefined,
229
212
  };
230
213
  }
@@ -1,8 +0,0 @@
1
- import type { SchemaView } from '@fougere/schema';
2
- export interface EntityEntry {
3
- name: string;
4
- /** A live class in-process, a card from a frond whose class never crossed. */
5
- entityClass: SchemaView;
6
- exposed?: boolean;
7
- }
8
- //# sourceMappingURL=EntityEntry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EntityEntry.d.ts","sourceRoot":"","sources":["../../src/routes/EntityEntry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,WAAW,EAAE,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=EntityEntry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EntityEntry.js","sourceRoot":"","sources":["../../src/routes/EntityEntry.ts"],"names":[],"mappings":""}
@@ -1,8 +0,0 @@
1
- import type { SchemaView } from '@fougere/schema';
2
-
3
- export interface EntityEntry {
4
- name: string;
5
- /** A live class in-process, a card from a frond whose class never crossed. */
6
- entityClass: SchemaView;
7
- exposed?: boolean;
8
- }