@owlmeans/entrypoint 0.1.18-rc.11 → 0.1.18-rc.12

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/README.md CHANGED
@@ -120,7 +120,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
120
120
  your project's skill store (`.agents/skills/`):
121
121
 
122
122
  ```sh
123
- npx @owlmeans/agent-skills@^0.1.18-rc.12
123
+ npx @owlmeans/agent-skills@^0.1.18-rc.13
124
124
  ```
125
125
 
126
126
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/entrypoint",
4
- "version": "0.1.18-rc.11",
5
- "generatedAt": "2026-09-04T22:43:25.452Z",
4
+ "version": "0.1.18-rc.12",
5
+ "generatedAt": "2026-09-10T18:54:42.506Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: entrypoint
3
- description: How to use @owlmeans/entrypoint — declarative entrypoint definitions over immutable route declarations, with entrypoint(), guard(), gate(), filter(), body(), params(), query() builders, the address accessors, and the transport seam. Auto-invoked when importing from this package or defining a service entrypoint declaration.
3
+ description: How to use @owlmeans/entrypoint — immutable typed protocols, adapter references, legacy entrypoint declarations, guards/gates/schemas, address accessors, and the transport seam. Auto-invoked when importing from this package or defining a service entrypoint declaration.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,16 +8,22 @@ user-invocable: false
8
8
  # @owlmeans/entrypoint
9
9
 
10
10
  **Layer:** Core
11
- **Install:** `"@owlmeans/entrypoint": "^0.1.18-rc.11"` in `dependencies`
11
+ **Install:** `"@owlmeans/entrypoint": "^0.1.18-rc.12"` in `dependencies`
12
12
 
13
- An entrypoint is a **URL unit**: an immutable route declaration plus the guards, gates and schemas
14
- it answers under. It is the single concept an application declares once in a shared package and
15
- elevates on either side.
13
+ An entrypoint is an **addressable unit**. New code declares an immutable typed `protocol` once in a
14
+ shared package, then binds that declaration on the client or server. The older mutable
15
+ `entrypoint(...)` + `elevate(...)` form remains supported while applications migrate.
16
16
 
17
17
  ## Key Exports
18
18
 
19
19
  | Export | Description |
20
20
  |--------|-------------|
21
+ | `protocol(route, contract, opts?)` / `openProtocol(route, opts?)` | Declare an immutable shared protocol |
22
+ | `contract(...)` / `contract.request(...)` | Declare the request sections and response together |
23
+ | `schema<T>(value)` / `typed<T>()` | Carry runtime validation plus type inference, or type information only |
24
+ | `protocols(tree)` | Flatten a named protocol tree for a layer binding |
25
+ | `entrypointRef<Request, Response>(alias)` | Typed adapter reference for dynamic registries |
26
+ | `decorateEntrypoint(protocol, opts)` | Return an immutable protocol with added access/options |
21
27
  | `entrypoint(route, opts?)` | Declare an entrypoint on a route model |
22
28
  | `guard(alias, opts?)` | Require a guard; returns options, so it wraps rather than takes them |
23
29
  | `gate(alias, params, opts?)` | Require a gate; passed as the `opts` of `guard(...)` |
@@ -34,6 +40,43 @@ elevates on either side.
34
40
  | `EntrypointTransport` | `{ protocol, handle }` — a carrier bound to a route protocol |
35
41
  | `transportAlias(protocol?)` | The service alias a transport registers under (`transport:<protocol>`) |
36
42
 
43
+ ## Typed protocol form
44
+
45
+ Application contracts export protocol objects, not alias strings or already context-bound
46
+ entrypoints. A protocol owns the route, request/response contract, guards, gate and sticky flag; it
47
+ is frozen and never mutated by either runtime.
48
+
49
+ ```typescript
50
+ import { contract, openProtocol, protocol, protocols, schema, typed } from '@owlmeans/entrypoint'
51
+ import { frontend, route, RouteMethod } from '@owlmeans/route'
52
+
53
+ const ItemParamsSchema = schema<{ id: string }>({
54
+ type: 'object', properties: { id: { type: 'string' } }, required: ['id'], additionalProperties: false,
55
+ })
56
+
57
+ const apiBase = protocol(route('api:item', '/items'), contract())
58
+ export const item = {
59
+ base: apiBase,
60
+ get: protocol(
61
+ route('api:item:get', '/:id', { parent: apiBase, method: RouteMethod.GET }),
62
+ contract.request({ params: ItemParamsSchema }, typed<Item>()),
63
+ ),
64
+ }
65
+ export const web = {
66
+ items: openProtocol(route('web:items', '/items', frontend())),
67
+ }
68
+ export const itemEntrypoints = protocols(item)
69
+ ```
70
+
71
+ `contract()` means no payload and an `undefined` reply. With one source it declares only the
72
+ response; with two it declares body then response. Use `contract.request` when params, query,
73
+ headers or several request sections participate. `schema<T>` is the preferred source when runtime
74
+ validation exists; `typed<T>()` is only the compile-time half.
75
+
76
+ `context.entrypoint(protocolOrRef)` infers the exact registered `call`/`invoke`/`url` signature.
77
+ Use the protocol object inside an application. Use `entrypointRef` only in an adapter package that
78
+ must address a dynamically registered surface without importing its declaration tree.
79
+
37
80
  `guard`, `gate` and `filter` are **options-object combinators, not variadic composers**. Each
38
81
  returns a `CommonEntrypointOptions` and takes the next one as its final argument, so they nest:
39
82
 
@@ -144,9 +187,10 @@ context.registerService(transport)
144
187
  as a broker job. Nothing at the call site changes — which is the point of putting the protocol on
145
188
  the route rather than at the call.
146
189
 
147
- ## Usage
190
+ ## Legacy declaration form
148
191
 
149
- Define entrypoints in a shared `common` package, then `elevate()` them with handlers in server/web packages:
192
+ Existing applications may define entrypoints in a shared `common` package, then `elevate()` them
193
+ with handlers in server/web packages. Do not introduce this form into a protocol-based surface.
150
194
 
151
195
  ```typescript
152
196
  import { entrypoint, guard, gate, filter, body } from '@owlmeans/entrypoint'
package/build/index.d.ts CHANGED
@@ -3,4 +3,6 @@ export * from './entrypoint.js';
3
3
  export * from './filter.js';
4
4
  export * from './helper.js';
5
5
  export * from './consts.js';
6
+ export * from './protocol.js';
7
+ export * from './materialize.js';
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA"}
package/build/index.js CHANGED
@@ -2,4 +2,6 @@ export * from './entrypoint.js';
2
2
  export * from './filter.js';
3
3
  export * from './helper.js';
4
4
  export * from './consts.js';
5
+ export * from './protocol.js';
6
+ export * from './materialize.js';
5
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA"}
@@ -0,0 +1,12 @@
1
+ import type { CommonEntrypoint } from './types.js';
2
+ import type { EntrypointProtocolDeclaration } from './protocol.js';
3
+ /** A context-bound entrypoint made from an immutable protocol declaration. */
4
+ export type MaterializedEntrypoint<Protocol extends EntrypointProtocolDeclaration> = CommonEntrypoint & {
5
+ readonly protocol: Protocol;
6
+ };
7
+ /**
8
+ * Make a local, mutable registration from a shared declaration. The protocol is never mutated;
9
+ * guards, route resolution and handlers belong exclusively to this materialized entrypoint.
10
+ */
11
+ export declare const materializeEntrypoint: <Protocol extends EntrypointProtocolDeclaration>(protocol: Protocol) => MaterializedEntrypoint<Protocol>;
12
+ //# sourceMappingURL=materialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materialize.d.ts","sourceRoot":"","sources":["../src/materialize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAmC,MAAM,YAAY,CAAA;AACnF,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAA;AAElE,8EAA8E;AAC9E,MAAM,MAAM,sBAAsB,CAAC,QAAQ,SAAS,6BAA6B,IAAI,gBAAgB,GAAG;IACtG,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAC5B,CAAA;AAgCD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAQ,SAAS,6BAA6B,YACxE,QAAQ,KACjB,sBAAsB,CAAC,QAAQ,CAGjC,CAAA"}
@@ -0,0 +1,34 @@
1
+ import { entrypoint } from './entrypoint.js';
2
+ const filterOf = (protocol) => {
3
+ const request = protocol.contract?.requestSchemas;
4
+ const response = protocol.contract?.responseSchemas;
5
+ if (request == null && response == null)
6
+ return undefined;
7
+ return {
8
+ body: request?.body == null ? undefined : structuredClone(request.body),
9
+ params: request?.params == null ? undefined : structuredClone(request.params),
10
+ query: request?.query == null ? undefined : structuredClone(request.query),
11
+ headers: request?.headers == null ? undefined : structuredClone(request.headers),
12
+ response: response?.byStatus == null
13
+ ? response?.default == null ? undefined : structuredClone(response.default)
14
+ : {
15
+ ...(response.default == null ? {} : { default: structuredClone(response.default) }),
16
+ ...Object.fromEntries(Object.entries(response.byStatus).map(([status, schema]) => [
17
+ status, structuredClone(schema),
18
+ ])),
19
+ },
20
+ };
21
+ };
22
+ const optionsOf = (protocol) => ({
23
+ sticky: protocol.sticky,
24
+ guards: [...protocol.guards],
25
+ gate: protocol.gate?.alias,
26
+ gateParams: protocol.gate == null ? undefined : [...protocol.gate.params],
27
+ filter: filterOf(protocol),
28
+ });
29
+ /**
30
+ * Make a local, mutable registration from a shared declaration. The protocol is never mutated;
31
+ * guards, route resolution and handlers belong exclusively to this materialized entrypoint.
32
+ */
33
+ export const materializeEntrypoint = (protocol) => Object.assign(entrypoint(protocol.route, optionsOf(protocol)), { protocol });
34
+ //# sourceMappingURL=materialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materialize.js","sourceRoot":"","sources":["../src/materialize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAS5C,MAAM,QAAQ,GAAG,CAAC,QAAuC,EAAsB,EAAE;IAC/E,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;IAEnD,IAAI,OAAO,IAAI,IAAI,IAAI,QAAQ,IAAI,IAAI;QAAE,OAAO,SAAS,CAAA;IAEzD,OAAO;QACL,IAAI,EAAE,OAAO,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC;QACvE,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC;QAC7E,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC;QAC1E,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC;QAChF,QAAQ,EAAE,QAAQ,EAAE,QAAQ,IAAI,IAAI;YAClC,CAAC,CAAC,QAAQ,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC3E,CAAC,CAAC;gBACA,GAAG,CAAC,QAAQ,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnF,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;oBAChF,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC;iBAChC,CAAC,CAAC;aACN;KACF,CAAA;AACH,CAAC,CAAA;AAED,MAAM,SAAS,GAAG,CAAC,QAAuC,EAA2B,EAAE,CAAC,CAAC;IACvF,MAAM,EAAE,QAAQ,CAAC,MAAM;IACvB,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,KAAK;IAC1B,UAAU,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;IACzE,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC;CAC3B,CAAC,CAAA;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,QAAkB,EACgB,EAAE,CAAC,MAAM,CAAC,MAAM,CAClD,UAAU,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,EAC/C,EAAE,QAAQ,EAAE,CACb,CAAA"}
@@ -0,0 +1,209 @@
1
+ import type { Auth } from '@owlmeans/auth';
2
+ import type { BasicEntrypoint, EntrypointReference } from '@owlmeans/context';
3
+ import type { JSONSchemaType, AnySchemaObject } from 'ajv';
4
+ import type { RouteModel } from '@owlmeans/route';
5
+ import type { EntrypointOutcome } from './consts.js';
6
+ import type { ResolvedEntity } from './types.js';
7
+ /** A deliberately broad value used only by declarations without an I/O contract. */
8
+ export type OpenValue = object | string | number | boolean | bigint | null | undefined;
9
+ /** The request accepted by a declaration that intentionally supplies no contract. */
10
+ export interface OpenRequest {
11
+ body?: object;
12
+ params?: object;
13
+ query?: object;
14
+ headers?: object;
15
+ }
16
+ /** The four independent request sections an entrypoint may declare. */
17
+ export interface RequestShape {
18
+ body?: OpenValue;
19
+ params?: object;
20
+ query?: object;
21
+ headers?: object;
22
+ }
23
+ /** Addressing and transport controls are never part of an entrypoint's payload contract. */
24
+ export interface CallOptions {
25
+ auth?: Auth;
26
+ host?: string;
27
+ base?: string | boolean;
28
+ unsecure?: boolean;
29
+ timeout?: number;
30
+ signal?: AbortSignal;
31
+ }
32
+ interface TypeToken {
33
+ readonly kind: 'entrypoint-type';
34
+ readonly schema?: AnySchemaObject;
35
+ }
36
+ /** A type-only contract source, optionally paired with an AJV schema. */
37
+ export type Typed<T> = TypeToken & {
38
+ readonly value?: T;
39
+ };
40
+ declare const entrypointSchemaType: unique symbol;
41
+ /**
42
+ * A JSON schema carrying the model type it validates. Use `schema<Model>(...)` once beside the
43
+ * model; every protocol that consumes it then infers the model without another generic.
44
+ */
45
+ export type EntrypointSchema<T> = JSONSchemaType<T> & {
46
+ readonly [entrypointSchemaType]: T;
47
+ };
48
+ export declare const schema: <T>(value: JSONSchemaType<T>) => EntrypointSchema<T>;
49
+ /** A schema or an explicit type-only source for one request/response value. */
50
+ export type ShapeSource<T> = EntrypointSchema<T> | JSONSchemaType<T> | Typed<T>;
51
+ type AnyShapeSource = AnySchemaObject | TypeToken;
52
+ /**
53
+ * Supplies type information where a response has no runtime schema, or attaches a type to an
54
+ * otherwise untyped schema literal at the declaration boundary.
55
+ */
56
+ export declare function typed<T>(): Typed<T>;
57
+ export declare function typed<T>(schema: JSONSchemaType<T>): Typed<T>;
58
+ export interface RequestSources {
59
+ body?: AnyShapeSource;
60
+ params?: AnyShapeSource;
61
+ query?: AnyShapeSource;
62
+ headers?: AnyShapeSource;
63
+ }
64
+ export interface RuntimeRequestSchemas {
65
+ body?: AnySchemaObject;
66
+ params?: AnySchemaObject;
67
+ query?: AnySchemaObject;
68
+ headers?: AnySchemaObject;
69
+ }
70
+ export interface RuntimeResponseSchemas {
71
+ default?: AnySchemaObject;
72
+ byStatus?: Readonly<Record<number, AnySchemaObject>>;
73
+ }
74
+ /** The runtime schemas and compile-time request/response pair attached to one protocol. */
75
+ export interface EntrypointContract<Request extends RequestShape, Response> {
76
+ readonly kind: 'entrypoint-contract';
77
+ readonly requestSchemas: RuntimeRequestSchemas;
78
+ readonly responseSchemas?: RuntimeResponseSchemas;
79
+ readonly requestType?: Request;
80
+ readonly responseType?: Response;
81
+ }
82
+ /** Runtime fields common to every protocol, regardless of its compile-time request/reply pair. */
83
+ export interface EntrypointProtocolDeclaration {
84
+ readonly kind: 'entrypoint-protocol';
85
+ readonly alias: string;
86
+ readonly route: RouteModel;
87
+ readonly contract?: Pick<EntrypointContract<RequestShape, OpenValue>, 'kind' | 'requestSchemas' | 'responseSchemas'>;
88
+ readonly sticky: boolean;
89
+ readonly guards: readonly string[];
90
+ readonly gate?: {
91
+ readonly alias: string;
92
+ readonly params: readonly string[];
93
+ };
94
+ }
95
+ type SourceValue<Source> = Source extends Typed<infer Value> ? Value : Source extends EntrypointSchema<infer Value> ? Value : Source extends JSONSchemaType<infer Value> ? Value extends OpenValue ? Value : OpenValue : OpenValue;
96
+ type RequestFromSources<Sources extends RequestSources> = (Sources extends {
97
+ body: infer Source;
98
+ } ? {
99
+ body: SourceValue<Source>;
100
+ } : {}) & (Sources extends {
101
+ params: infer Source;
102
+ } ? {
103
+ params: SourceValue<Source>;
104
+ } : {}) & (Sources extends {
105
+ query: infer Source;
106
+ } ? {
107
+ query: SourceValue<Source>;
108
+ } : {}) & (Sources extends {
109
+ headers: infer Source;
110
+ } ? {
111
+ headers: SourceValue<Source>;
112
+ } : {});
113
+ /**
114
+ * Declare an exact request and response. The primary overload means a body request; use
115
+ * `contract.request` for independently-shaped request sections.
116
+ */
117
+ export declare function contract(): EntrypointContract<{}, undefined>;
118
+ export declare function contract<Response>(response: ShapeSource<Response>): EntrypointContract<{}, Response>;
119
+ export declare function contract<Body extends OpenValue, Response>(body: ShapeSource<Body>, response: ShapeSource<Response>): EntrypointContract<{
120
+ body: Body;
121
+ }, Response>;
122
+ export declare namespace contract {
123
+ function request<Sources extends RequestSources, Response>(request: Sources, response: ShapeSource<Response>): EntrypointContract<RequestFromSources<Sources>, Response>;
124
+ }
125
+ export interface EntrypointOptions {
126
+ sticky?: boolean;
127
+ guards?: string | readonly string[];
128
+ gate?: {
129
+ alias: string;
130
+ params?: string | readonly string[];
131
+ };
132
+ }
133
+ export interface EntrypointResult<Response> {
134
+ value: Response;
135
+ outcome: EntrypointOutcome;
136
+ }
137
+ export type CallArguments<Request extends RequestShape> = {} extends Request ? [request?: Request & CallOptions] : [request: Request & CallOptions];
138
+ type UrlRequest<Request extends RequestShape> = (Request extends {
139
+ params: infer Params;
140
+ } ? {
141
+ params: Params;
142
+ } : Request extends {
143
+ params?: infer Params;
144
+ } ? {
145
+ params?: Params;
146
+ } : {}) & (Request extends {
147
+ query: infer Query;
148
+ } ? {
149
+ query: Query;
150
+ } : Request extends {
151
+ query?: infer Query;
152
+ } ? {
153
+ query?: Query;
154
+ } : {});
155
+ export type UrlArguments<Request extends RequestShape> = {} extends UrlRequest<Request> ? [request?: UrlRequest<Request> & CallOptions] : [request: UrlRequest<Request> & CallOptions];
156
+ export interface EntrypointRequestMeta {
157
+ alias: string;
158
+ auth?: Auth;
159
+ entity?: ResolvedEntity;
160
+ path: string;
161
+ canceled?: boolean;
162
+ cancel?: () => void;
163
+ }
164
+ export type HandlerRequest<Request extends RequestShape> = Request & EntrypointRequestMeta;
165
+ /** The context-bound counterpart of an immutable protocol declaration. */
166
+ export interface RegisteredEntrypoint<Request extends RequestShape, Response> extends BasicEntrypoint {
167
+ readonly protocol: EntrypointProtocol<Request, Response>;
168
+ call: (...args: CallArguments<Request>) => Promise<Response>;
169
+ invoke: (...args: CallArguments<Request>) => Promise<EntrypointResult<Response>>;
170
+ url: (...args: UrlArguments<Request>) => Promise<string>;
171
+ validate: (...args: CallArguments<Request>) => Promise<boolean>;
172
+ }
173
+ /**
174
+ * An immutable shared declaration. Its inherited reference type is what gives a context lookup
175
+ * the exact registered entrypoint type without a consumer-supplied generic.
176
+ */
177
+ export interface EntrypointProtocol<Request extends RequestShape = RequestShape, Response = OpenValue> extends EntrypointProtocolDeclaration, EntrypointReference<RegisteredEntrypoint<Request, Response>> {
178
+ readonly contract?: EntrypointContract<Request, Response>;
179
+ }
180
+ export type RequestOf<Protocol extends EntrypointProtocolDeclaration> = Protocol extends EntrypointProtocol<infer Request, infer _Response> ? Request : OpenRequest;
181
+ export type ResponseOf<Protocol extends EntrypointProtocolDeclaration> = Protocol extends EntrypointProtocol<infer _Request, infer Response> ? Response : OpenValue;
182
+ export type BodyOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends {
183
+ body: infer Body;
184
+ } ? Body : OpenValue;
185
+ export type ParamsOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends {
186
+ params: infer Params;
187
+ } ? Params : object;
188
+ export type QueryOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends {
189
+ query: infer Query;
190
+ } ? Query : object;
191
+ export type HeadersOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends {
192
+ headers: infer Headers;
193
+ } ? Headers : object;
194
+ export type CallRequestOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> & CallOptions;
195
+ /** Create the immutable protocol form while legacy contextual entrypoints are migrated. */
196
+ export declare const protocol: <Request extends RequestShape, Response>(route: RouteModel, entrypointContract: EntrypointContract<Request, Response>, options?: EntrypointOptions) => EntrypointProtocol<Request, Response>;
197
+ export declare const openProtocol: (route: RouteModel, options?: EntrypointOptions) => EntrypointProtocol<OpenRequest, OpenValue>;
198
+ export declare const entrypointRef: <Request extends RequestShape = OpenRequest, Response = OpenValue>(alias: string) => EntrypointReference<RegisteredEntrypoint<Request, Response>>;
199
+ export declare const aliasOf: (reference: EntrypointReference | string) => string;
200
+ export declare const isEntrypointProtocol: (value: object) => value is EntrypointProtocolDeclaration;
201
+ export interface EntrypointTree {
202
+ readonly [key: string]: EntrypointProtocolDeclaration | EntrypointTree;
203
+ }
204
+ /** Flatten an exported protocol tree for a layer-specific materializer. */
205
+ export declare const protocols: (tree: EntrypointTree) => EntrypointProtocolDeclaration[];
206
+ /** Clone an immutable protocol while preserving its request/response pair. */
207
+ export declare const decorateEntrypoint: <Protocol extends EntrypointProtocolDeclaration>(protocol: Protocol, options: EntrypointOptions) => Protocol;
208
+ export {};
209
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ export const schema = (value) => value;
2
+ export function typed(schema) {
3
+ return schema == null
4
+ ? { kind: 'entrypoint-type' }
5
+ : { kind: 'entrypoint-type', schema };
6
+ }
7
+ const schemaOf = (source) => {
8
+ if (source == null)
9
+ return undefined;
10
+ const schema = 'kind' in source && source.kind === 'entrypoint-type'
11
+ ? source.schema
12
+ : source;
13
+ return schema == null ? undefined : structuredClone(schema);
14
+ };
15
+ const responseSchemasOf = (source) => {
16
+ const schema = schemaOf(source);
17
+ return schema == null ? undefined : { default: schema };
18
+ };
19
+ export function contract(first, second) {
20
+ const body = second == null ? undefined : first;
21
+ const response = second ?? first;
22
+ return {
23
+ kind: 'entrypoint-contract',
24
+ requestSchemas: { body: schemaOf(body) },
25
+ responseSchemas: responseSchemasOf(response),
26
+ };
27
+ }
28
+ (function (contract) {
29
+ function request(request, response) {
30
+ return {
31
+ kind: 'entrypoint-contract',
32
+ requestSchemas: {
33
+ body: schemaOf(request.body),
34
+ params: schemaOf(request.params),
35
+ query: schemaOf(request.query),
36
+ headers: schemaOf(request.headers),
37
+ },
38
+ responseSchemas: responseSchemasOf(response),
39
+ };
40
+ }
41
+ contract.request = request;
42
+ })(contract || (contract = {}));
43
+ const guardsOf = (guards) => guards == null
44
+ ? []
45
+ : typeof guards === 'string' ? [guards] : [...guards];
46
+ const gateOf = (gate) => gate == null
47
+ ? undefined
48
+ : {
49
+ alias: gate.alias,
50
+ params: gate.params == null ? [] : typeof gate.params === 'string' ? [gate.params] : [...gate.params],
51
+ };
52
+ /** Create the immutable protocol form while legacy contextual entrypoints are migrated. */
53
+ export const protocol = (route, entrypointContract, options) => Object.freeze({
54
+ kind: 'entrypoint-protocol',
55
+ alias: route.route.alias,
56
+ route: Object.freeze({ route: Object.freeze({ ...route.route }) }),
57
+ contract: entrypointContract,
58
+ sticky: options?.sticky ?? false,
59
+ guards: Object.freeze(guardsOf(options?.guards)),
60
+ gate: gateOf(options?.gate),
61
+ });
62
+ export const openProtocol = (route, options) => Object.freeze({
63
+ kind: 'entrypoint-protocol',
64
+ alias: route.route.alias,
65
+ route: Object.freeze({ route: Object.freeze({ ...route.route }) }),
66
+ sticky: options?.sticky ?? false,
67
+ guards: Object.freeze(guardsOf(options?.guards)),
68
+ gate: gateOf(options?.gate),
69
+ });
70
+ export const entrypointRef = (alias) => ({ alias });
71
+ export const aliasOf = (reference) => typeof reference === 'string' ? reference : reference.alias;
72
+ export const isEntrypointProtocol = (value) => 'kind' in value && value.kind === 'entrypoint-protocol';
73
+ /** Flatten an exported protocol tree for a layer-specific materializer. */
74
+ export const protocols = (tree) => {
75
+ const result = [];
76
+ const visit = (node) => {
77
+ Object.values(node).forEach((entry) => {
78
+ if (isEntrypointProtocol(entry)) {
79
+ result.push(entry);
80
+ }
81
+ else {
82
+ visit(entry);
83
+ }
84
+ });
85
+ };
86
+ visit(tree);
87
+ return result;
88
+ };
89
+ /** Clone an immutable protocol while preserving its request/response pair. */
90
+ export const decorateEntrypoint = (protocol, options) => Object.freeze({
91
+ ...protocol,
92
+ sticky: options.sticky ?? protocol.sticky,
93
+ guards: options.guards == null ? protocol.guards : guardsOf(options.guards),
94
+ gate: options.gate == null ? protocol.gate : gateOf(options.gate),
95
+ });
96
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/entrypoint",
3
- "version": "0.1.18-rc.11",
3
+ "version": "0.1.18-rc.12",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -28,9 +28,9 @@
28
28
  }
29
29
  },
30
30
  "dependencies": {
31
- "@owlmeans/auth": "^0.1.18-rc.9",
32
- "@owlmeans/context": "^0.1.18-rc.8",
33
- "@owlmeans/route": "^0.1.18-rc.9",
31
+ "@owlmeans/auth": "^0.1.18-rc.10",
32
+ "@owlmeans/context": "^0.1.18-rc.9",
33
+ "@owlmeans/route": "^0.1.18-rc.10",
34
34
  "ajv": "^8.17.1"
35
35
  },
36
36
  "devDependencies": {
package/src/index.ts CHANGED
@@ -4,3 +4,5 @@ export * from './entrypoint.js'
4
4
  export * from './filter.js'
5
5
  export * from './helper.js'
6
6
  export * from './consts.js'
7
+ export * from './protocol.js'
8
+ export * from './materialize.js'
@@ -0,0 +1,49 @@
1
+ import { entrypoint } from './entrypoint.js'
2
+ import type { CommonEntrypoint, CommonEntrypointOptions, Filter } from './types.js'
3
+ import type { EntrypointProtocolDeclaration } from './protocol.js'
4
+
5
+ /** A context-bound entrypoint made from an immutable protocol declaration. */
6
+ export type MaterializedEntrypoint<Protocol extends EntrypointProtocolDeclaration> = CommonEntrypoint & {
7
+ readonly protocol: Protocol
8
+ }
9
+
10
+ const filterOf = (protocol: EntrypointProtocolDeclaration): Filter | undefined => {
11
+ const request = protocol.contract?.requestSchemas
12
+ const response = protocol.contract?.responseSchemas
13
+
14
+ if (request == null && response == null) return undefined
15
+
16
+ return {
17
+ body: request?.body == null ? undefined : structuredClone(request.body),
18
+ params: request?.params == null ? undefined : structuredClone(request.params),
19
+ query: request?.query == null ? undefined : structuredClone(request.query),
20
+ headers: request?.headers == null ? undefined : structuredClone(request.headers),
21
+ response: response?.byStatus == null
22
+ ? response?.default == null ? undefined : structuredClone(response.default)
23
+ : {
24
+ ...(response.default == null ? {} : { default: structuredClone(response.default) }),
25
+ ...Object.fromEntries(Object.entries(response.byStatus).map(([status, schema]) => [
26
+ status, structuredClone(schema),
27
+ ])),
28
+ },
29
+ }
30
+ }
31
+
32
+ const optionsOf = (protocol: EntrypointProtocolDeclaration): CommonEntrypointOptions => ({
33
+ sticky: protocol.sticky,
34
+ guards: [...protocol.guards],
35
+ gate: protocol.gate?.alias,
36
+ gateParams: protocol.gate == null ? undefined : [...protocol.gate.params],
37
+ filter: filterOf(protocol),
38
+ })
39
+
40
+ /**
41
+ * Make a local, mutable registration from a shared declaration. The protocol is never mutated;
42
+ * guards, route resolution and handlers belong exclusively to this materialized entrypoint.
43
+ */
44
+ export const materializeEntrypoint = <Protocol extends EntrypointProtocolDeclaration>(
45
+ protocol: Protocol,
46
+ ): MaterializedEntrypoint<Protocol> => Object.assign(
47
+ entrypoint(protocol.route, optionsOf(protocol)),
48
+ { protocol },
49
+ )
@@ -0,0 +1,349 @@
1
+ import type { Auth } from '@owlmeans/auth'
2
+ import type { BasicEntrypoint, EntrypointReference } from '@owlmeans/context'
3
+ import type { JSONSchemaType, AnySchemaObject } from 'ajv'
4
+ import type { RouteModel } from '@owlmeans/route'
5
+ import type { EntrypointOutcome } from './consts.js'
6
+ import type { ResolvedEntity } from './types.js'
7
+
8
+ /** A deliberately broad value used only by declarations without an I/O contract. */
9
+ export type OpenValue = object | string | number | boolean | bigint | null | undefined
10
+
11
+ /** The request accepted by a declaration that intentionally supplies no contract. */
12
+ export interface OpenRequest {
13
+ body?: object
14
+ params?: object
15
+ query?: object
16
+ headers?: object
17
+ }
18
+
19
+ /** The four independent request sections an entrypoint may declare. */
20
+ export interface RequestShape {
21
+ body?: OpenValue
22
+ params?: object
23
+ query?: object
24
+ headers?: object
25
+ }
26
+
27
+ /** Addressing and transport controls are never part of an entrypoint's payload contract. */
28
+ export interface CallOptions {
29
+ auth?: Auth
30
+ host?: string
31
+ base?: string | boolean
32
+ unsecure?: boolean
33
+ timeout?: number
34
+ signal?: AbortSignal
35
+ }
36
+
37
+ interface TypeToken {
38
+ readonly kind: 'entrypoint-type'
39
+ readonly schema?: AnySchemaObject
40
+ }
41
+
42
+ /** A type-only contract source, optionally paired with an AJV schema. */
43
+ export type Typed<T> = TypeToken & { readonly value?: T }
44
+
45
+ declare const entrypointSchemaType: unique symbol
46
+
47
+ /**
48
+ * A JSON schema carrying the model type it validates. Use `schema<Model>(...)` once beside the
49
+ * model; every protocol that consumes it then infers the model without another generic.
50
+ */
51
+ export type EntrypointSchema<T> = JSONSchemaType<T> & {
52
+ readonly [entrypointSchemaType]: T
53
+ }
54
+
55
+ export const schema = <T>(value: JSONSchemaType<T>): EntrypointSchema<T> =>
56
+ value as EntrypointSchema<T>
57
+
58
+ /** A schema or an explicit type-only source for one request/response value. */
59
+ export type ShapeSource<T> = EntrypointSchema<T> | JSONSchemaType<T> | Typed<T>
60
+
61
+ type AnyShapeSource = AnySchemaObject | TypeToken
62
+
63
+ /**
64
+ * Supplies type information where a response has no runtime schema, or attaches a type to an
65
+ * otherwise untyped schema literal at the declaration boundary.
66
+ */
67
+ export function typed<T>(): Typed<T>
68
+ export function typed<T>(schema: JSONSchemaType<T>): Typed<T>
69
+ export function typed<T>(schema?: JSONSchemaType<T>): Typed<T> {
70
+ return schema == null
71
+ ? { kind: 'entrypoint-type' }
72
+ : { kind: 'entrypoint-type', schema }
73
+ }
74
+
75
+ export interface RequestSources {
76
+ body?: AnyShapeSource
77
+ params?: AnyShapeSource
78
+ query?: AnyShapeSource
79
+ headers?: AnyShapeSource
80
+ }
81
+
82
+ export interface RuntimeRequestSchemas {
83
+ body?: AnySchemaObject
84
+ params?: AnySchemaObject
85
+ query?: AnySchemaObject
86
+ headers?: AnySchemaObject
87
+ }
88
+
89
+ export interface RuntimeResponseSchemas {
90
+ default?: AnySchemaObject
91
+ byStatus?: Readonly<Record<number, AnySchemaObject>>
92
+ }
93
+
94
+ /** The runtime schemas and compile-time request/response pair attached to one protocol. */
95
+ export interface EntrypointContract<Request extends RequestShape, Response> {
96
+ readonly kind: 'entrypoint-contract'
97
+ readonly requestSchemas: RuntimeRequestSchemas
98
+ readonly responseSchemas?: RuntimeResponseSchemas
99
+ readonly requestType?: Request
100
+ readonly responseType?: Response
101
+ }
102
+
103
+ /** Runtime fields common to every protocol, regardless of its compile-time request/reply pair. */
104
+ export interface EntrypointProtocolDeclaration {
105
+ readonly kind: 'entrypoint-protocol'
106
+ readonly alias: string
107
+ readonly route: RouteModel
108
+ readonly contract?: Pick<EntrypointContract<RequestShape, OpenValue>,
109
+ 'kind' | 'requestSchemas' | 'responseSchemas'>
110
+ readonly sticky: boolean
111
+ readonly guards: readonly string[]
112
+ readonly gate?: {
113
+ readonly alias: string
114
+ readonly params: readonly string[]
115
+ }
116
+ }
117
+
118
+ type SourceValue<Source> =
119
+ Source extends Typed<infer Value> ? Value
120
+ : Source extends EntrypointSchema<infer Value> ? Value
121
+ : Source extends JSONSchemaType<infer Value>
122
+ ? Value extends OpenValue ? Value : OpenValue
123
+ : OpenValue
124
+
125
+ type RequestFromSources<Sources extends RequestSources> =
126
+ (Sources extends { body: infer Source } ? { body: SourceValue<Source> } : {})
127
+ & (Sources extends { params: infer Source } ? { params: SourceValue<Source> } : {})
128
+ & (Sources extends { query: infer Source } ? { query: SourceValue<Source> } : {})
129
+ & (Sources extends { headers: infer Source } ? { headers: SourceValue<Source> } : {})
130
+
131
+ const schemaOf = (source: AnyShapeSource | undefined): AnySchemaObject | undefined => {
132
+ if (source == null) return undefined
133
+ const schema = 'kind' in source && source.kind === 'entrypoint-type'
134
+ ? source.schema
135
+ : source
136
+
137
+ return schema == null ? undefined : structuredClone(schema)
138
+ }
139
+
140
+ const responseSchemasOf = (source: AnyShapeSource | undefined): RuntimeResponseSchemas | undefined => {
141
+ const schema = schemaOf(source)
142
+ return schema == null ? undefined : { default: schema }
143
+ }
144
+
145
+ /**
146
+ * Declare an exact request and response. The primary overload means a body request; use
147
+ * `contract.request` for independently-shaped request sections.
148
+ */
149
+ export function contract(): EntrypointContract<{}, undefined>
150
+ export function contract<Response>(response: ShapeSource<Response>): EntrypointContract<{}, Response>
151
+ export function contract<Body extends OpenValue, Response>(
152
+ body: ShapeSource<Body>, response: ShapeSource<Response>
153
+ ): EntrypointContract<{ body: Body }, Response>
154
+ export function contract(
155
+ first?: AnyShapeSource, second?: AnyShapeSource
156
+ ): EntrypointContract<RequestShape, OpenValue> {
157
+ const body = second == null ? undefined : first
158
+ const response = second ?? first
159
+
160
+ return {
161
+ kind: 'entrypoint-contract',
162
+ requestSchemas: { body: schemaOf(body) },
163
+ responseSchemas: responseSchemasOf(response),
164
+ }
165
+ }
166
+
167
+ export namespace contract {
168
+ export function request<Sources extends RequestSources, Response>(
169
+ request: Sources,
170
+ response: ShapeSource<Response>
171
+ ): EntrypointContract<RequestFromSources<Sources>, Response> {
172
+ return {
173
+ kind: 'entrypoint-contract',
174
+ requestSchemas: {
175
+ body: schemaOf(request.body),
176
+ params: schemaOf(request.params),
177
+ query: schemaOf(request.query),
178
+ headers: schemaOf(request.headers),
179
+ },
180
+ responseSchemas: responseSchemasOf(response),
181
+ }
182
+ }
183
+ }
184
+
185
+ export interface EntrypointOptions {
186
+ sticky?: boolean
187
+ guards?: string | readonly string[]
188
+ gate?: {
189
+ alias: string
190
+ params?: string | readonly string[]
191
+ }
192
+ }
193
+
194
+ export interface EntrypointResult<Response> {
195
+ value: Response
196
+ outcome: EntrypointOutcome
197
+ }
198
+
199
+ export type CallArguments<Request extends RequestShape> = {} extends Request
200
+ ? [request?: Request & CallOptions]
201
+ : [request: Request & CallOptions]
202
+
203
+ type UrlRequest<Request extends RequestShape> =
204
+ (Request extends { params: infer Params } ? { params: Params }
205
+ : Request extends { params?: infer Params } ? { params?: Params } : {})
206
+ & (Request extends { query: infer Query } ? { query: Query }
207
+ : Request extends { query?: infer Query } ? { query?: Query } : {})
208
+
209
+ export type UrlArguments<Request extends RequestShape> = {} extends UrlRequest<Request>
210
+ ? [request?: UrlRequest<Request> & CallOptions]
211
+ : [request: UrlRequest<Request> & CallOptions]
212
+
213
+ export interface EntrypointRequestMeta {
214
+ alias: string
215
+ auth?: Auth
216
+ entity?: ResolvedEntity
217
+ path: string
218
+ canceled?: boolean
219
+ cancel?: () => void
220
+ }
221
+
222
+ export type HandlerRequest<Request extends RequestShape> = Request & EntrypointRequestMeta
223
+
224
+ /** The context-bound counterpart of an immutable protocol declaration. */
225
+ export interface RegisteredEntrypoint<Request extends RequestShape, Response> extends BasicEntrypoint {
226
+ readonly protocol: EntrypointProtocol<Request, Response>
227
+ call: (...args: CallArguments<Request>) => Promise<Response>
228
+ invoke: (...args: CallArguments<Request>) => Promise<EntrypointResult<Response>>
229
+ url: (...args: UrlArguments<Request>) => Promise<string>
230
+ validate: (...args: CallArguments<Request>) => Promise<boolean>
231
+ }
232
+
233
+ /**
234
+ * An immutable shared declaration. Its inherited reference type is what gives a context lookup
235
+ * the exact registered entrypoint type without a consumer-supplied generic.
236
+ */
237
+ export interface EntrypointProtocol<
238
+ Request extends RequestShape = RequestShape,
239
+ Response = OpenValue,
240
+ > extends EntrypointProtocolDeclaration, EntrypointReference<RegisteredEntrypoint<Request, Response>> {
241
+ readonly contract?: EntrypointContract<Request, Response>
242
+ }
243
+
244
+ export type RequestOf<Protocol extends EntrypointProtocolDeclaration> = Protocol extends EntrypointProtocol<infer Request, infer _Response>
245
+ ? Request
246
+ : OpenRequest
247
+
248
+ export type ResponseOf<Protocol extends EntrypointProtocolDeclaration> = Protocol extends EntrypointProtocol<infer _Request, infer Response>
249
+ ? Response
250
+ : OpenValue
251
+
252
+ export type BodyOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends { body: infer Body }
253
+ ? Body
254
+ : OpenValue
255
+
256
+ export type ParamsOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends { params: infer Params }
257
+ ? Params
258
+ : object
259
+
260
+ export type QueryOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends { query: infer Query }
261
+ ? Query
262
+ : object
263
+
264
+ export type HeadersOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends { headers: infer Headers }
265
+ ? Headers
266
+ : object
267
+
268
+ export type CallRequestOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> & CallOptions
269
+
270
+ const guardsOf = (guards: EntrypointOptions['guards']): readonly string[] => guards == null
271
+ ? []
272
+ : typeof guards === 'string' ? [guards] : [...guards]
273
+
274
+ const gateOf = (gate: EntrypointOptions['gate']): EntrypointProtocol['gate'] => gate == null
275
+ ? undefined
276
+ : {
277
+ alias: gate.alias,
278
+ params: gate.params == null ? [] : typeof gate.params === 'string' ? [gate.params] : [...gate.params],
279
+ }
280
+
281
+ /** Create the immutable protocol form while legacy contextual entrypoints are migrated. */
282
+ export const protocol = <Request extends RequestShape, Response>(
283
+ route: RouteModel,
284
+ entrypointContract: EntrypointContract<Request, Response>,
285
+ options?: EntrypointOptions
286
+ ): EntrypointProtocol<Request, Response> => Object.freeze({
287
+ kind: 'entrypoint-protocol' as const,
288
+ alias: route.route.alias,
289
+ route: Object.freeze({ route: Object.freeze({ ...route.route }) }),
290
+ contract: entrypointContract,
291
+ sticky: options?.sticky ?? false,
292
+ guards: Object.freeze(guardsOf(options?.guards)),
293
+ gate: gateOf(options?.gate),
294
+ })
295
+
296
+ export const openProtocol = (
297
+ route: RouteModel,
298
+ options?: EntrypointOptions
299
+ ): EntrypointProtocol<OpenRequest, OpenValue> => Object.freeze({
300
+ kind: 'entrypoint-protocol' as const,
301
+ alias: route.route.alias,
302
+ route: Object.freeze({ route: Object.freeze({ ...route.route }) }),
303
+ sticky: options?.sticky ?? false,
304
+ guards: Object.freeze(guardsOf(options?.guards)),
305
+ gate: gateOf(options?.gate),
306
+ })
307
+
308
+ export const entrypointRef = <Request extends RequestShape = OpenRequest, Response = OpenValue>(
309
+ alias: string
310
+ ): EntrypointReference<RegisteredEntrypoint<Request, Response>> => ({ alias })
311
+
312
+ export const aliasOf = (reference: EntrypointReference | string): string =>
313
+ typeof reference === 'string' ? reference : reference.alias
314
+
315
+ export const isEntrypointProtocol = (value: object): value is EntrypointProtocolDeclaration =>
316
+ 'kind' in value && value.kind === 'entrypoint-protocol'
317
+
318
+ export interface EntrypointTree {
319
+ readonly [key: string]: EntrypointProtocolDeclaration | EntrypointTree
320
+ }
321
+
322
+ /** Flatten an exported protocol tree for a layer-specific materializer. */
323
+ export const protocols = (tree: EntrypointTree): EntrypointProtocolDeclaration[] => {
324
+ const result: EntrypointProtocolDeclaration[] = []
325
+
326
+ const visit = (node: EntrypointTree): void => {
327
+ Object.values(node).forEach((entry) => {
328
+ if (isEntrypointProtocol(entry)) {
329
+ result.push(entry)
330
+ } else {
331
+ visit(entry)
332
+ }
333
+ })
334
+ }
335
+
336
+ visit(tree)
337
+ return result
338
+ }
339
+
340
+ /** Clone an immutable protocol while preserving its request/response pair. */
341
+ export const decorateEntrypoint = <Protocol extends EntrypointProtocolDeclaration>(
342
+ protocol: Protocol,
343
+ options: EntrypointOptions
344
+ ): Protocol => Object.freeze({
345
+ ...protocol,
346
+ sticky: options.sticky ?? protocol.sticky,
347
+ guards: options.guards == null ? protocol.guards : guardsOf(options.guards),
348
+ gate: options.gate == null ? protocol.gate : gateOf(options.gate),
349
+ })