@owlmeans/entrypoint 0.1.18-rc.8 → 0.1.18

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.
Files changed (60) hide show
  1. package/README.md +262 -60
  2. package/agent-meta/manifest.json +3 -3
  3. package/agent-meta/skills/entrypoint/SKILL.md +78 -56
  4. package/build/entrypoint.d.ts +0 -1
  5. package/build/entrypoint.d.ts.map +1 -1
  6. package/build/entrypoint.js +31 -57
  7. package/build/entrypoint.js.map +1 -1
  8. package/build/filter/types.d.ts +16 -0
  9. package/build/filter/types.d.ts.map +1 -0
  10. package/build/filter/types.js +2 -0
  11. package/build/filter/types.js.map +1 -0
  12. package/build/filter.d.ts +3 -7
  13. package/build/filter.d.ts.map +1 -1
  14. package/build/filter.js +22 -18
  15. package/build/filter.js.map +1 -1
  16. package/build/helper.d.ts +1 -2
  17. package/build/helper.d.ts.map +1 -1
  18. package/build/helper.js +0 -9
  19. package/build/helper.js.map +1 -1
  20. package/build/index.d.ts +5 -3
  21. package/build/index.d.ts.map +1 -1
  22. package/build/index.js +4 -3
  23. package/build/index.js.map +1 -1
  24. package/build/materialize.d.ts +7 -0
  25. package/build/materialize.d.ts.map +1 -0
  26. package/build/materialize.js +34 -0
  27. package/build/materialize.js.map +1 -0
  28. package/build/protocol.d.ts +44 -0
  29. package/build/protocol.d.ts.map +1 -0
  30. package/build/protocol.js +124 -0
  31. package/build/protocol.js.map +1 -0
  32. package/build/types.d.ts +201 -12
  33. package/build/types.d.ts.map +1 -1
  34. package/build/types.local.d.ts +27 -0
  35. package/build/types.local.d.ts.map +1 -0
  36. package/build/types.local.js +2 -0
  37. package/build/types.local.js.map +1 -0
  38. package/build/utils/entrypoint.d.ts.map +1 -1
  39. package/build/utils/entrypoint.js +1 -1
  40. package/build/utils/entrypoint.js.map +1 -1
  41. package/build/utils/types.d.ts +2 -2
  42. package/build/utils/types.d.ts.map +1 -1
  43. package/build/utils.d.ts +6 -0
  44. package/build/utils.d.ts.map +1 -0
  45. package/build/utils.js +6 -0
  46. package/build/utils.js.map +1 -0
  47. package/package.json +6 -6
  48. package/src/consts.ts +1 -0
  49. package/src/entrypoint.ts +31 -65
  50. package/src/filter/types.ts +16 -0
  51. package/src/filter.ts +24 -17
  52. package/src/helper.ts +1 -12
  53. package/src/index.ts +5 -3
  54. package/src/materialize.ts +43 -0
  55. package/src/protocol.ts +191 -0
  56. package/src/types.local.ts +24 -0
  57. package/src/types.ts +234 -12
  58. package/src/utils/entrypoint.ts +1 -1
  59. package/src/utils/types.ts +2 -2
  60. package/src/utils.ts +5 -0
@@ -0,0 +1,43 @@
1
+ import { entrypoint } from './entrypoint.js'
2
+ import type { CommonEntrypointOptions, Filter, EntrypointProtocolDeclaration, MaterializedEntrypoint } from './types.js'
3
+
4
+ const filterOf = (protocol: EntrypointProtocolDeclaration): Filter | undefined => {
5
+ const request = protocol.contract?.requestSchemas
6
+ const response = protocol.contract?.responseSchemas
7
+
8
+ if (request == null && response == null) return undefined
9
+
10
+ return {
11
+ body: request?.body == null ? undefined : structuredClone(request.body),
12
+ params: request?.params == null ? undefined : structuredClone(request.params),
13
+ query: request?.query == null ? undefined : structuredClone(request.query),
14
+ headers: request?.headers == null ? undefined : structuredClone(request.headers),
15
+ // Fastify requires response serializers to be keyed by a status code. A protocol's
16
+ // ordinary response has no explicit outcome, so it is the normal successful 200 reply.
17
+ response: response == null ? undefined : {
18
+ ...(response.default == null ? {} : { 200: structuredClone(response.default) }),
19
+ ...Object.fromEntries(Object.entries(response.byStatus ?? {}).map(([status, schema]) => [
20
+ status, structuredClone(schema),
21
+ ])),
22
+ },
23
+ }
24
+ }
25
+
26
+ const optionsOf = (protocol: EntrypointProtocolDeclaration): CommonEntrypointOptions => ({
27
+ sticky: protocol.sticky,
28
+ guards: [...protocol.guards],
29
+ gate: protocol.gate?.alias,
30
+ gateParams: protocol.gate == null ? undefined : [...protocol.gate.params],
31
+ filter: filterOf(protocol),
32
+ })
33
+
34
+ /**
35
+ * Make a local, mutable registration from a shared declaration. The protocol is never mutated;
36
+ * guards, route resolution and handlers belong exclusively to this materialized entrypoint.
37
+ */
38
+ export const materializeEntrypoint = <Protocol extends EntrypointProtocolDeclaration>(
39
+ protocol: Protocol,
40
+ ): MaterializedEntrypoint<Protocol> => Object.assign(
41
+ entrypoint({ route: { ...protocol.route.route } }, optionsOf(protocol)),
42
+ { protocol },
43
+ )
@@ -0,0 +1,191 @@
1
+ import type { EntrypointReference } from '@owlmeans/context'
2
+ import type { JSONSchemaType, AnySchemaObject } from 'ajv'
3
+ import type { RouteModel } from '@owlmeans/route'
4
+ import type { AnyShapeSource } from './types.local.js'
5
+ import type { EntrypointContract, EntrypointGate, EntrypointOptions, EntrypointProtocol, EntrypointProtocolDeclaration, EntrypointSchema, EntrypointTree, OpenRequest, OpenValue, RegisteredEntrypoint, RequestFromSources, RequestShape, RequestSources, RuntimeResponseSchemas, ShapeSource, Typed } from './types.js'
6
+
7
+ export const schema = <T>(value: JSONSchemaType<T>): EntrypointSchema<T> =>
8
+ value as EntrypointSchema<T>
9
+
10
+ /**
11
+ * Supplies type information where a response has no runtime schema, or attaches a type to an
12
+ * otherwise untyped schema literal at the declaration boundary.
13
+ */
14
+ export function typed<T>(): Typed<T>
15
+ export function typed<T>(schema: JSONSchemaType<T>): Typed<T>
16
+ export function typed<T>(schema?: JSONSchemaType<T>): Typed<T> {
17
+ return schema == null
18
+ ? { kind: 'entrypoint-type' }
19
+ : { kind: 'entrypoint-type', schema }
20
+ }
21
+
22
+ const schemaOf = (source: AnyShapeSource | undefined): AnySchemaObject | undefined => {
23
+ if (source == null) return undefined
24
+ const schema = 'kind' in source && source.kind === 'entrypoint-type'
25
+ ? source.schema
26
+ : source
27
+
28
+ return schema == null ? undefined : structuredClone(schema)
29
+ }
30
+
31
+ const responseSchemasOf = (source: AnyShapeSource | undefined): RuntimeResponseSchemas | undefined => {
32
+ const schema = schemaOf(source)
33
+ return schema == null ? undefined : { default: schema }
34
+ }
35
+
36
+ /**
37
+ * Declare an exact request and response. The primary overload means a body request; use
38
+ * `contract.request` for independently-shaped request sections.
39
+ */
40
+ export function contract(): EntrypointContract<{}, undefined>
41
+ export function contract<Response>(response: ShapeSource<Response>): EntrypointContract<{}, Response>
42
+ export function contract<Body extends OpenValue, Response>(
43
+ body: ShapeSource<Body>, response: ShapeSource<Response>
44
+ ): EntrypointContract<{ body: Body }, Response>
45
+ export function contract(
46
+ first?: AnyShapeSource, second?: AnyShapeSource
47
+ ): EntrypointContract<RequestShape, OpenValue> {
48
+ const body = second == null ? undefined : first
49
+ const response = second ?? first
50
+
51
+ return {
52
+ kind: 'entrypoint-contract',
53
+ requestSchemas: { body: schemaOf(body) },
54
+ responseSchemas: responseSchemasOf(response),
55
+ }
56
+ }
57
+
58
+ export namespace contract {
59
+ export function request<Sources extends RequestSources, Response>(
60
+ request: Sources,
61
+ response: ShapeSource<Response>
62
+ ): EntrypointContract<RequestFromSources<Sources>, Response> {
63
+ return {
64
+ kind: 'entrypoint-contract',
65
+ requestSchemas: {
66
+ body: schemaOf(request.body),
67
+ params: schemaOf(request.params),
68
+ query: schemaOf(request.query),
69
+ headers: schemaOf(request.headers),
70
+ },
71
+ responseSchemas: responseSchemasOf(response),
72
+ }
73
+ }
74
+ }
75
+
76
+ const guardsOf = (guards: EntrypointOptions['guards']): readonly string[] => guards == null
77
+ ? []
78
+ : typeof guards === 'string' ? [guards] : [...guards]
79
+
80
+ const gateOf = (gate: EntrypointOptions['gate']): EntrypointProtocol['gate'] => gate == null
81
+ ? undefined
82
+ : {
83
+ alias: gate.alias,
84
+ params: gate.params == null ? [] : typeof gate.params === 'string' ? [gate.params] : [...gate.params],
85
+ }
86
+
87
+ /** Create an immutable protocol declaration from a route and its contract. */
88
+ export const protocol = <Request extends RequestShape, Response>(
89
+ route: RouteModel,
90
+ entrypointContract: EntrypointContract<Request, Response>,
91
+ options?: EntrypointOptions
92
+ ): EntrypointProtocol<Request, Response> => Object.freeze({
93
+ kind: 'entrypoint-protocol' as const,
94
+ alias: route.route.alias,
95
+ route: Object.freeze({ route: Object.freeze({ ...route.route }) }),
96
+ contract: entrypointContract,
97
+ sticky: options?.sticky ?? false,
98
+ guards: Object.freeze(guardsOf(options?.guards)),
99
+ gate: gateOf(options?.gate),
100
+ })
101
+
102
+ export const openProtocol = (
103
+ route: RouteModel,
104
+ options?: EntrypointOptions
105
+ ): EntrypointProtocol<OpenRequest, OpenValue> => Object.freeze({
106
+ kind: 'entrypoint-protocol' as const,
107
+ alias: route.route.alias,
108
+ route: Object.freeze({ route: Object.freeze({ ...route.route }) }),
109
+ sticky: options?.sticky ?? false,
110
+ guards: Object.freeze(guardsOf(options?.guards)),
111
+ gate: gateOf(options?.gate),
112
+ })
113
+
114
+ export const entrypointRef = <Request extends RequestShape = OpenRequest, Response = OpenValue>(
115
+ alias: string
116
+ ): EntrypointReference<RegisteredEntrypoint<Request, Response>> => ({ alias })
117
+
118
+ export const aliasOf = (reference: EntrypointReference | string): string =>
119
+ typeof reference === 'string' ? reference : reference.alias
120
+
121
+ export const isEntrypointProtocol = (value: object): value is EntrypointProtocolDeclaration =>
122
+ 'kind' in value && value.kind === 'entrypoint-protocol'
123
+
124
+ /** Flatten an exported protocol tree for a layer-specific materializer. */
125
+ export const protocols = (tree: EntrypointTree): EntrypointProtocolDeclaration[] => {
126
+ const result: EntrypointProtocolDeclaration[] = []
127
+
128
+ const visit = (node: EntrypointTree): void => {
129
+ Object.values(node).forEach((entry) => {
130
+ if (isEntrypointProtocol(entry)) {
131
+ result.push(entry)
132
+ } else {
133
+ visit(entry)
134
+ }
135
+ })
136
+ }
137
+
138
+ visit(tree)
139
+ return result
140
+ }
141
+
142
+ /**
143
+ * Rebuild an immutable protocol tree while transforming its declarations.
144
+ *
145
+ * Protocol consumers keep addressing declarations by their exported tree path; a cross-cutting
146
+ * concern such as a coguard therefore never needs to flatten the tree and recover declarations
147
+ * by alias.
148
+ */
149
+ export const mapProtocols = <Tree extends EntrypointTree>(
150
+ tree: Tree,
151
+ mapper: (protocol: EntrypointProtocolDeclaration) => EntrypointProtocolDeclaration,
152
+ ): Tree => Object.freeze(Object.fromEntries(
153
+ Object.entries(tree).map(([key, entry]) => [
154
+ key,
155
+ isEntrypointProtocol(entry) ? mapper(entry) : mapProtocols(entry, mapper),
156
+ ])
157
+ )) as Tree
158
+
159
+ /** Resolve the gates a protocol inherits through its route parents without materializing it. */
160
+ export const gatesOf = (
161
+ protocol: EntrypointProtocolDeclaration,
162
+ tree: EntrypointTree,
163
+ ): readonly EntrypointGate[] => {
164
+ const byAlias = new Map(protocols(tree).map(candidate => [candidate.alias, candidate]))
165
+ const result: EntrypointGate[] = []
166
+ const visited = new Set<string>()
167
+ let current: EntrypointProtocolDeclaration | undefined = protocol
168
+
169
+ while (current != null && !visited.has(current.alias)) {
170
+ visited.add(current.alias)
171
+ if (current.gate != null && !result.some(gate => gate.alias === current!.gate!.alias)) {
172
+ result.push(current.gate)
173
+ }
174
+ current = current.route.route.parent == null
175
+ ? undefined
176
+ : byAlias.get(current.route.route.parent)
177
+ }
178
+
179
+ return result
180
+ }
181
+
182
+ /** Clone an immutable protocol while preserving its request/response pair. */
183
+ export const decorateEntrypoint = <Protocol extends EntrypointProtocolDeclaration>(
184
+ protocol: Protocol,
185
+ options: EntrypointOptions
186
+ ): Protocol => Object.freeze({
187
+ ...protocol,
188
+ sticky: options.sticky ?? protocol.sticky,
189
+ guards: options.guards == null ? protocol.guards : guardsOf(options.guards),
190
+ gate: options.gate == null ? protocol.gate : gateOf(options.gate),
191
+ })
@@ -0,0 +1,24 @@
1
+ import type { JSONSchemaType, AnySchemaObject } from 'ajv'
2
+ import type { EntrypointSchema, OpenValue, RequestShape, Typed } from './types.js'
3
+
4
+ export interface TypeToken {
5
+ readonly kind: 'entrypoint-type'
6
+ readonly schema?: AnySchemaObject
7
+ }
8
+
9
+ export declare const entrypointSchemaType: unique symbol
10
+
11
+ export type AnyShapeSource = AnySchemaObject | TypeToken
12
+
13
+ export type SourceValue<Source> =
14
+ Source extends Typed<infer Value> ? Value
15
+ : Source extends EntrypointSchema<infer Value> ? Value
16
+ : Source extends JSONSchemaType<infer Value>
17
+ ? Value extends OpenValue ? Value : OpenValue
18
+ : OpenValue
19
+
20
+ export type UrlRequest<Request extends RequestShape> =
21
+ (Request extends { params: infer Params } ? { params: Params }
22
+ : Request extends { params?: infer Params } ? { params?: Params } : {})
23
+ & (Request extends { query: infer Query } ? { query: Query }
24
+ : Request extends { query?: infer Query } ? { query?: Query } : {})
package/src/types.ts CHANGED
@@ -1,29 +1,46 @@
1
- import type { CommonRouteModel } from '@owlmeans/route'
2
- import type { InitializedService, LazyService, BasicEntrypoint } from '@owlmeans/context'
3
- import type { AnySchemaObject } from 'ajv'
1
+ import type { ResolvedServiceRoute, RouteAddress, RouteModel, RouteProtocol } from '@owlmeans/route'
2
+ import type { InitializedService, LazyService, BasicEntrypoint, EntrypointReference } from '@owlmeans/context'
3
+ import type { AnySchemaObject, JSONSchemaType } from 'ajv'
4
4
  import type { EntrypointOutcome } from './consts.js'
5
5
  import type { Auth } from '@owlmeans/auth'
6
6
 
7
+ import type { AnyShapeSource, entrypointSchemaType, SourceValue, TypeToken, UrlRequest } from './types.local.js'
8
+
9
+ /**
10
+ * A URL unit: an immutable route declaration plus what guards, gates and schemas it answers under.
11
+ *
12
+ * Nothing here is rewritten once declared. Every address question — segment, path, mount, service,
13
+ * host — is a question about the declaration asked against the context the entrypoint is registered
14
+ * in, so the same declaration answers differently in a client and in a server without being touched.
15
+ */
7
16
  export interface CommonEntrypoint extends BasicEntrypoint {
8
- route: CommonRouteModel
17
+ route: RouteModel
9
18
  /**
10
19
  * @property {boolean} - if true — router attaches this entrypoint unconditionally
11
20
  * @default false
12
21
  */
13
22
  sticky: boolean
14
- filter?: Filter
23
+ /** The guards declared on this entrypoint alone — see `getGuards()` for the inherited set. */
15
24
  guards?: string[]
16
25
  gate?: string
17
26
  gateParams?: string | string[]
27
+ filter?: Filter
18
28
  handle?: EntrypointHandler
19
- getAlias: () => string
20
- getPath: () => string
21
- getParentAlias: () => string | null
22
- hasParent: () => boolean
23
- resolve: <M extends CommonEntrypoint>() => Promise<M>
24
- getParent: <M extends CommonEntrypoint>() => M
25
- setService: (service: string) => void
29
+
30
+ /** The segment this entrypoint contributes under its parent. */
31
+ segment: () => string
32
+ /** Every ancestor's segment, then this one. */
33
+ path: () => string
34
+ /** `base` + `path()` — what a server mounts and a client requests. */
35
+ mount: () => string
36
+ service: () => ResolvedServiceRoute
37
+ address: () => RouteAddress
38
+ /** Does this entrypoint belong to the service its context IS? */
39
+ isLocal: () => boolean
40
+ parent: () => CommonEntrypoint | null
41
+ /** Own guards and every ancestor's, deduped. Recomputed on each call. */
26
42
  getGuards: () => string[]
43
+ /** Own gate and every ancestor's, as `[gate, params]`. Recomputed on each call. */
27
44
  getGates: () => [string, string[]][]
28
45
  }
29
46
 
@@ -124,3 +141,208 @@ export interface Filter {
124
141
  response?: AnySchemaObject
125
142
  headers?: AnySchemaObject
126
143
  }
144
+
145
+ /**
146
+ * How a call to an entrypoint is actually carried.
147
+ *
148
+ * The transport is chosen by the route's protocol, so a consumer always writes `ep.call(...)` and
149
+ * never learns which built-in or package-owned carrier handled it. An
150
+ * application binds a protocol by registering a transport service under `transportAlias(protocol)`.
151
+ */
152
+ export interface EntrypointTransport extends InitializedService {
153
+ readonly protocol: RouteProtocol
154
+ handle: EntrypointHandler
155
+ }
156
+
157
+ /** A deliberately broad value used only by declarations without an I/O contract. */
158
+ export type OpenValue = object | string | number | boolean | bigint | null | undefined
159
+
160
+ /** The four independent request sections an entrypoint may declare. */
161
+ export interface RequestShape {
162
+ body?: OpenValue
163
+ params?: object
164
+ query?: object
165
+ headers?: object
166
+ }
167
+
168
+ export interface RuntimeRequestSchemas {
169
+ body?: AnySchemaObject
170
+ params?: AnySchemaObject
171
+ query?: AnySchemaObject
172
+ headers?: AnySchemaObject
173
+ }
174
+
175
+ export interface RuntimeResponseSchemas {
176
+ default?: AnySchemaObject
177
+ byStatus?: Readonly<Record<number, AnySchemaObject>>
178
+ }
179
+
180
+ /** The runtime schemas and compile-time request/response pair attached to one protocol. */
181
+ export interface EntrypointContract<Request extends RequestShape, Response> {
182
+ readonly kind: 'entrypoint-contract'
183
+ readonly requestSchemas: RuntimeRequestSchemas
184
+ readonly responseSchemas?: RuntimeResponseSchemas
185
+ readonly requestType?: Request
186
+ readonly responseType?: Response
187
+ }
188
+
189
+ /** Runtime fields common to every protocol, regardless of its compile-time request/reply pair. */
190
+ export interface EntrypointProtocolDeclaration {
191
+ readonly kind: 'entrypoint-protocol'
192
+ readonly alias: string
193
+ readonly route: RouteModel
194
+ readonly contract?: Pick<EntrypointContract<RequestShape, OpenValue>,
195
+ 'kind' | 'requestSchemas' | 'responseSchemas'>
196
+ readonly sticky: boolean
197
+ readonly guards: readonly string[]
198
+ readonly gate?: {
199
+ readonly alias: string
200
+ readonly params: readonly string[]
201
+ }
202
+ }
203
+
204
+ /** A context-bound entrypoint made from an immutable protocol declaration. */
205
+ export interface MaterializedEntrypoint<Protocol extends EntrypointProtocolDeclaration> extends CommonEntrypoint { readonly protocol: Protocol }
206
+
207
+ /** The request accepted by a declaration that intentionally supplies no contract. */
208
+ export interface OpenRequest {
209
+ body?: object
210
+ params?: object
211
+ query?: object
212
+ headers?: object
213
+ }
214
+
215
+ /** Addressing and transport controls are never part of an entrypoint's payload contract. */
216
+ export interface CallOptions {
217
+ auth?: Auth
218
+ host?: string
219
+ base?: string | boolean
220
+ unsecure?: boolean
221
+ timeout?: number
222
+ signal?: AbortSignal
223
+ }
224
+
225
+ /** A type-only contract source, optionally paired with an AJV schema. */
226
+ export interface Typed<T> extends TypeToken { readonly value?: T }
227
+
228
+ // Kept as a type: `JSONSchemaType` is a conditional type; an interface cannot extend it.
229
+ /**
230
+ * A JSON schema carrying the model type it validates. Use `schema<Model>(...)` once beside the
231
+ * model; every protocol that consumes it then infers the model without another generic.
232
+ */
233
+ export type EntrypointSchema<T> = JSONSchemaType<T> & {
234
+ readonly [entrypointSchemaType]: T
235
+ }
236
+
237
+ /** A schema or an explicit type-only source for one request/response value. */
238
+ export type ShapeSource<T> = EntrypointSchema<T> | JSONSchemaType<T> | Typed<T>
239
+
240
+ export interface RequestSources {
241
+ body?: AnyShapeSource
242
+ params?: AnyShapeSource
243
+ query?: AnyShapeSource
244
+ headers?: AnyShapeSource
245
+ }
246
+
247
+ /** An access gate declared by a protocol, including inherited parent declarations. */
248
+ export interface EntrypointGate {
249
+ readonly alias: string
250
+ readonly params: readonly string[]
251
+ }
252
+
253
+ export type RequestFromSources<Sources extends RequestSources> =
254
+ (Sources extends { body: infer Source } ? { body: SourceValue<Source> } : {})
255
+ & (Sources extends { params: infer Source } ? { params: SourceValue<Source> } : {})
256
+ & (Sources extends { query: infer Source } ? { query: SourceValue<Source> } : {})
257
+ & (Sources extends { headers: infer Source } ? { headers: SourceValue<Source> } : {})
258
+
259
+ export interface EntrypointOptions {
260
+ sticky?: boolean
261
+ guards?: string | readonly string[]
262
+ gate?: {
263
+ alias: string
264
+ params?: string | readonly string[]
265
+ }
266
+ }
267
+
268
+ export interface EntrypointResult<Response> {
269
+ value: Response
270
+ outcome: EntrypointOutcome
271
+ }
272
+
273
+ export type CallArguments<Request extends RequestShape> = {} extends Request
274
+ ? [request?: Request & CallOptions]
275
+ : [request: Request & CallOptions]
276
+
277
+ export type UrlArguments<Request extends RequestShape> = {} extends UrlRequest<Request>
278
+ ? [request?: UrlRequest<Request> & CallOptions]
279
+ : [request: UrlRequest<Request> & CallOptions]
280
+
281
+ export interface EntrypointRequestMeta {
282
+ alias: string
283
+ auth?: Auth
284
+ entity?: ResolvedEntity
285
+ path: string
286
+ canceled?: boolean
287
+ cancel?: () => void
288
+ }
289
+
290
+ // Kept as a type: it intersects the `Request` type parameter, which an interface cannot extend.
291
+ /**
292
+ * A protocol request at an implementation boundary.
293
+ *
294
+ * Transport metadata and empty request sections remain available to handlers; a declared section
295
+ * then refines that base shape to its protocol contract.
296
+ */
297
+ export type HandlerRequest<Request extends RequestShape> = AbstractRequest & Request & EntrypointRequestMeta
298
+
299
+ /**
300
+ * An immutable shared declaration. Its inherited reference type is what gives a context lookup
301
+ * the exact registered entrypoint type without a consumer-supplied generic.
302
+ */
303
+ export interface EntrypointProtocol<
304
+ Request extends RequestShape = RequestShape,
305
+ Response = OpenValue,
306
+ > extends EntrypointProtocolDeclaration, EntrypointReference<RegisteredEntrypoint<Request, Response>> {
307
+ readonly contract?: EntrypointContract<Request, Response>
308
+ }
309
+
310
+ /** The context-bound counterpart of an immutable protocol declaration. */
311
+ export interface RegisteredEntrypoint<Request extends RequestShape, Response> extends BasicEntrypoint {
312
+ readonly protocol: EntrypointProtocol<Request, Response>
313
+ call: (...args: CallArguments<Request>) => Promise<Response>
314
+ invoke: (...args: CallArguments<Request>) => Promise<EntrypointResult<Response>>
315
+ url: (...args: UrlArguments<Request>) => Promise<string>
316
+ validate: (...args: CallArguments<Request>) => Promise<boolean>
317
+ }
318
+
319
+ export type RequestOf<Protocol extends EntrypointProtocolDeclaration> = Protocol extends EntrypointProtocol<infer Request, infer _Response>
320
+ ? Request
321
+ : OpenRequest
322
+
323
+ export type ResponseOf<Protocol extends EntrypointProtocolDeclaration> = Protocol extends EntrypointProtocol<infer _Request, infer Response>
324
+ ? Response
325
+ : OpenValue
326
+
327
+ export type BodyOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends { body: infer Body }
328
+ ? Body
329
+ : OpenValue
330
+
331
+ export type ParamsOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends { params: infer Params }
332
+ ? Params
333
+ : object
334
+
335
+ export type QueryOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends { query: infer Query }
336
+ ? Query
337
+ : object
338
+
339
+ export type HeadersOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> extends { headers: infer Headers }
340
+ ? Headers
341
+ : object
342
+
343
+ // Kept as a type: `RequestOf` is a conditional type; an interface cannot extend it.
344
+ export type CallRequestOf<Protocol extends EntrypointProtocolDeclaration> = RequestOf<Protocol> & CallOptions
345
+
346
+ export interface EntrypointTree {
347
+ readonly [key: string]: EntrypointProtocolDeclaration | EntrypointTree
348
+ }
@@ -1,4 +1,4 @@
1
1
  import { CommonEntrypoint } from '../types.js'
2
2
 
3
3
  export const isEntrypoint = (obj: object): obj is CommonEntrypoint =>
4
- '_entrypoint' in obj || '_module' in obj
4
+ '_entrypoint' in obj
@@ -1,6 +1,6 @@
1
- import { CommonRouteModel } from '@owlmeans/route'
1
+ import { RouteModel } from '@owlmeans/route'
2
2
  import { CommonEntrypointOptions } from '../types.js'
3
3
 
4
4
  export interface CreateEntrypointSignature<M> {
5
- (route: CommonRouteModel, opts?: CommonEntrypointOptions): M,
5
+ (route: RouteModel, opts?: CommonEntrypointOptions): M,
6
6
  }
package/src/utils.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Service alias a transport registers under. The default protocol is the web one, so an application
3
+ * that binds nothing keeps talking HTTP.
4
+ */
5
+ export const transportAlias = (protocol: string = 'http') => `transport:${protocol}`