@owlmeans/entrypoint 0.1.18-rc.2 → 0.1.18-rc.20

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 (44) hide show
  1. package/README.md +272 -60
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/entrypoint/SKILL.md +84 -48
  4. package/build/consts.d.ts +5 -0
  5. package/build/consts.d.ts.map +1 -1
  6. package/build/consts.js +5 -0
  7. package/build/consts.js.map +1 -1
  8. package/build/entrypoint.d.ts +0 -1
  9. package/build/entrypoint.d.ts.map +1 -1
  10. package/build/entrypoint.js +31 -57
  11. package/build/entrypoint.js.map +1 -1
  12. package/build/helper.d.ts +1 -2
  13. package/build/helper.d.ts.map +1 -1
  14. package/build/helper.js +0 -9
  15. package/build/helper.js.map +1 -1
  16. package/build/index.d.ts +3 -3
  17. package/build/index.d.ts.map +1 -1
  18. package/build/index.js +3 -3
  19. package/build/index.js.map +1 -1
  20. package/build/materialize.d.ts +12 -0
  21. package/build/materialize.d.ts.map +1 -0
  22. package/build/materialize.js +34 -0
  23. package/build/materialize.js.map +1 -0
  24. package/build/protocol.d.ts +231 -0
  25. package/build/protocol.d.ts.map +1 -0
  26. package/build/protocol.js +124 -0
  27. package/build/protocol.js.map +1 -0
  28. package/build/types.d.ts +60 -10
  29. package/build/types.d.ts.map +1 -1
  30. package/build/utils/entrypoint.d.ts.map +1 -1
  31. package/build/utils/entrypoint.js +1 -1
  32. package/build/utils/entrypoint.js.map +1 -1
  33. package/build/utils/types.d.ts +2 -2
  34. package/build/utils/types.d.ts.map +1 -1
  35. package/package.json +4 -4
  36. package/src/consts.ts +6 -0
  37. package/src/entrypoint.ts +33 -65
  38. package/src/helper.ts +1 -12
  39. package/src/index.ts +3 -3
  40. package/src/materialize.ts +49 -0
  41. package/src/protocol.ts +402 -0
  42. package/src/types.ts +63 -10
  43. package/src/utils/entrypoint.ts +1 -1
  44. package/src/utils/types.ts +2 -2
package/README.md CHANGED
@@ -1,98 +1,310 @@
1
1
  # @owlmeans/entrypoint
2
2
 
3
- Entrypoint system — the URL unit abstraction shared between server and client in OwlMeans apps.
4
-
5
- ## Overview
6
-
7
- - An **entrypoint** is a URL unit: an alias + path + optional guards/gates/filters
8
- - On the server, entrypoints become API routes with attached handlers
9
- - On the client, entrypoints provide URL generation and navigation
10
- - All AJV validation schemas are defined at entrypoint level, keeping data contracts consistent fullstack
11
- - Most commonly used via re-exports in `@owlmeans/server-app` or `@owlmeans/client-entrypoint`
3
+ Entrypoint protocol system — the typed route contract shared between server and client in OwlMeans
4
+ apps. An application uses it in its shared (`common`) package to declare every addressable unit —
5
+ an HTTP API route, a socket, a queued job or a screen — once, as an immutable protocol with typed
6
+ request sections, a response type, AJV schemas and access rules. Runtime behaviour is not added
7
+ here: the server attaches handlers with `@owlmeans/server-entrypoint` / `@owlmeans/server-api`, the
8
+ client binds calls and screens with `@owlmeans/client-entrypoint`, and route shapes themselves come
9
+ from `@owlmeans/route`. Code that only calls or handles an already declared protocol imports it from
10
+ the shared package rather than declaring anything here.
11
+
12
+ The route declaration an entrypoint carries is immutable: its `path` always stays the segment this
13
+ entrypoint contributes under its parent. Addresses are computed on demand against the context the
14
+ entrypoint is registered in — `path()` walks the parent chain, `mount()` adds the service base,
15
+ `address()` picks host, port and scheme — so one declaration answers correctly on both sides.
12
16
 
13
17
  ## Installation
14
18
 
15
19
  ```bash
16
- bun add @owlmeans/entrypoint
20
+ bun add @owlmeans/entrypoint@^0.1.18-rc.19
17
21
  ```
18
22
 
19
- ## Usage
23
+ ## Concepts
24
+
25
+ - **Protocol** — one immutable URL unit: an alias + route + typed contract + access options, made by
26
+ `protocol(route, contract, options?)`. The frozen object is the only cross-layer reference;
27
+ runtime code passes it (`context.entrypoint(protocols.x)`), never its alias string.
28
+ - **Contract** — the compile-time request/response pair plus the runtime AJV schemas, made by
29
+ `contract(...)` or `contract.request({ body, params, query, headers }, response)` from `typed<T>()`
30
+ and `schema<T>()` sources. All validation lives at protocol level, keeping data contracts
31
+ consistent fullstack.
32
+ - **Protocol tree** — an exported `*Protocols` object whose property names describe the contract.
33
+ Aliases stay private to the declaring module; the tree is flattened with `protocols(tree)` only at
34
+ registration.
35
+ - **Access options** — `guards`, `gate` and `sticky` in `EntrypointOptions`. Guards and gates are
36
+ inherited through the route parent when a runtime binds the protocol.
37
+ - **Binding / materialization** — a side-specific package turns a declaration into a context-bound
38
+ `CommonEntrypoint` (`materializeEntrypoint`) and adds a handler, a call or a screen. The declaration
39
+ itself is never mutated.
40
+ - **Transport** — the route's protocol (`http`, `ws`, `queue`) picks the carrier through a service
41
+ registered under `transportAlias(protocol)`; callers write `call()` and never branch on it.
20
42
 
21
- Define an entrypoint with body validation and a guard:
43
+ ## Usage
22
44
 
23
- ```typescript
24
- import { entrypoint, guard, filter, body, params } from '@owlmeans/server-app'
25
- import { route } from '@owlmeans/route'
45
+ ### 1. Declare a typed protocol tree
46
+
47
+ ```ts
48
+ import { contract, protocol, schema, typed } from '@owlmeans/entrypoint'
49
+ import { backend, route, RouteMethod } from '@owlmeans/route'
50
+ import { DEFAULT_GUARD } from '@owlmeans/auth-common'
51
+ import type { JSONSchemaType } from 'ajv'
52
+
53
+ export interface Story { id: string, title: string }
54
+ export interface CreateStory { title: string }
55
+
56
+ export const CreateStorySchema = schema<CreateStory>({
57
+ type: 'object',
58
+ properties: { title: { type: 'string', minLength: 1 } },
59
+ required: ['title'],
60
+ additionalProperties: false,
61
+ } as JSONSchemaType<CreateStory>)
62
+
63
+ // Private wire names — never exported.
64
+ const aliases = {
65
+ base: 'my-app:story:base',
66
+ create: 'my-app:story:create',
67
+ get: 'my-app:story:get',
68
+ } as const
69
+
70
+ const base = protocol(route(aliases.base, '/stories', backend()), contract(), {
71
+ guards: DEFAULT_GUARD,
72
+ })
26
73
 
27
- const createStoryModule = entrypoint(
28
- route('story-create', '/stories', { method: 'POST', parent: 'api' }),
29
- filter(body({
30
- type: 'object',
31
- properties: { story: { type: 'string' }, projectId: { type: 'string' } },
32
- required: ['story', 'projectId']
33
- }), guard('authenticated'))
34
- )
74
+ export const storyProtocols = {
75
+ base,
76
+ create: protocol(
77
+ route(aliases.create, '/', backend({ parent: base, method: RouteMethod.POST })),
78
+ contract(CreateStorySchema, typed<Story>()),
79
+ ),
80
+ get: protocol(
81
+ route(aliases.get, '/:id', backend({ parent: base })),
82
+ contract.request({ params: typed<{ id: string }>() }, typed<Story>()),
83
+ ),
84
+ } as const
35
85
  ```
36
86
 
37
- Use `AbstractRequest` type in a handler:
87
+ `contract(body, response)` is a body-only contract; `contract.request(...)` types each request
88
+ section independently. The children inherit `DEFAULT_GUARD` from `base`.
38
89
 
39
- ```typescript
40
- import type { AbstractRequest } from '@owlmeans/entrypoint'
90
+ ### 2. Bind handlers on the server
41
91
 
42
- export const create = handleBody(async (body, context, request) => {
43
- const req = request as AbstractRequest<{ id: string }>
44
- const projectId = req.params.id
45
- })
46
- ```
47
-
48
- ## API
92
+ ```ts
93
+ import { handlers } from '@owlmeans/server-api'
94
+ import { bind } from '@owlmeans/server-entrypoint'
95
+ import { requireEntityKey } from '@owlmeans/auth-common'
96
+ import { storyProtocols } from 'my-app-common'
97
+ import type { Context } from 'my-app-backend'
49
98
 
50
- ### `entrypoint(route, opts?): CommonEntrypoint`
99
+ const api = handlers<Context>()
51
100
 
52
- Creates an entrypoint. `opts` is typically produced by `filter()`, `guard()`, or `gate()`.
101
+ const create = api.body(storyProtocols.create, async (body, context, request) =>
102
+ context.story().create(requireEntityKey(request), body))
53
103
 
54
- ### `guard(guard, opts?): CommonEntrypointOptions`
104
+ const get = api.params(storyProtocols.get, async ({ id }, context) =>
105
+ context.story().get(id))
55
106
 
56
- Adds an authentication guard requirement.
107
+ export const serverBindings = [
108
+ bind(storyProtocols.base),
109
+ bind(storyProtocols.create, create),
110
+ bind(storyProtocols.get, get),
111
+ ]
112
+ ```
57
113
 
58
- ### `gate(gate, params, opts?): CommonEntrypointOptions`
114
+ `body`, `params` and `id` are inferred from the protocol; no generic is named. The organization
115
+ record id comes from the request (`requireEntityKey`), never from the payload.
59
116
 
60
- Adds an authorization gate with parameters.
117
+ ### 3. Call the protocol from a client or another service
61
118
 
62
- ### `filter(filter, opts?): CommonEntrypointOptions`
119
+ ```ts
120
+ import { bindAll } from '@owlmeans/client-entrypoint'
121
+ import { EntrypointOutcome } from '@owlmeans/entrypoint'
122
+ import { storyProtocols } from 'my-app-common'
63
123
 
64
- Attaches validation schemas (AJV format) to the entrypoint.
124
+ export const clientBindings = bindAll(storyProtocols)
65
125
 
66
- ### `body(schema, filter?) / query(schema, filter?) / params(schema, filter?)`
126
+ // Later, with a context the bindings were registered in:
127
+ const story = await context.entrypoint(storyProtocols.create).call({ body: { title: 'Invoices' } })
67
128
 
68
- Build a `Filter` object with the given AJV schema applied to the corresponding request part.
129
+ const { value, outcome } = await context.entrypoint(storyProtocols.get)
130
+ .invoke({ params: { id: story.id }, timeout: 5_000 })
131
+ if (outcome !== EntrypointOutcome.Ok) {
132
+ throw new Error(`Story ${story.id} was not loaded`)
133
+ }
69
134
 
70
- ### `parent(entrypoint, parentAlias): CommonEntrypoint`
135
+ const link = await context.entrypoint(storyProtocols.get).url({ params: { id: value.id } })
136
+ ```
71
137
 
72
- Sets a parent-child relationship so child entrypoints inherit guards/gates.
138
+ `call()` resolves the value, `invoke()` also returns the `EntrypointOutcome`, `url()` builds the
139
+ address. `CallOptions` (`auth`, `host`, `base`, `unsecure`, `timeout`, `signal`) ride alongside the
140
+ request sections and are never part of the payload contract.
73
141
 
74
- ### `provideResponse<T>(): AbstractResponse<T>`
142
+ ### 4. Gates, socket and queue carriers, derived types
75
143
 
76
- Creates a response object for use in non-elevated handlers.
144
+ ```ts
145
+ import { contract, openProtocol, protocol, typed } from '@owlmeans/entrypoint'
146
+ import type { HandlerRequest, RequestOf, ResponseOf } from '@owlmeans/entrypoint'
147
+ import { backend, job, route, RouteMethod, socket } from '@owlmeans/route'
148
+ import { DEFAULT_GUARD, GUARD_ED25519 } from '@owlmeans/auth-common'
149
+ import { PROJECT_GATE, WORKER, WORK_QUEUE } from './consts.js'
77
150
 
78
- ### `EntrypointOutcome`
151
+ const aliases = {
152
+ base: 'my-app:project:base', update: 'my-app:project:update',
153
+ watch: 'my-app:project:watch', build: 'my-app:project:build',
154
+ } as const
79
155
 
80
- ```typescript
81
- enum EntrypointOutcome { Ok, Accepted, Created, Finished }
156
+ const base = protocol(route(aliases.base, '/projects', backend()), contract(), {
157
+ guards: DEFAULT_GUARD,
158
+ gate: { alias: PROJECT_GATE, params: 'my-app-project-{entity}' },
159
+ })
160
+ const updates = openProtocol(route(aliases.update, '/update'))
161
+
162
+ export const projectProtocols = {
163
+ base,
164
+ updates,
165
+ // Carried over a WebSocket: the handler receives a connection, not a single reply.
166
+ watch: protocol(
167
+ route(aliases.watch, '/project/:id', socket({ parent: updates })),
168
+ contract.request({ params: typed<{ id: string }>() }, typed<void>()),
169
+ ),
170
+ // Carried as a queued job for a worker service; callers still write `call()`.
171
+ build: protocol(
172
+ route(aliases.build, '/:id/build', job({
173
+ parent: base, method: RouteMethod.POST, service: WORKER, queue: WORK_QUEUE,
174
+ })),
175
+ contract.request({ params: typed<{ id: string }>() }, typed<{ jobId: string }>()),
176
+ { guards: GUARD_ED25519 },
177
+ ),
178
+ } as const
179
+
180
+ type BuildRequest = RequestOf<typeof projectProtocols.build> // { params: { id: string } }
181
+ type BuildReply = ResponseOf<typeof projectProtocols.build> // { jobId: string }
182
+ type WatchRequest = HandlerRequest<RequestOf<typeof projectProtocols.watch>>
82
183
  ```
83
184
 
84
- ### Types
185
+ ### 5. Decorate a whole tree without flattening it
186
+
187
+ ```ts
188
+ import { decorateEntrypoint, gatesOf, mapProtocols } from '@owlmeans/entrypoint'
189
+ import { AUDIT_GUARD } from './consts.js'
190
+ import { projectProtocols } from './protocols.js'
85
191
 
86
- - `AbstractRequest<T>` — request with `params`, `body`, `query`, `headers`, `auth`
87
- - `AbstractResponse<T>` — response with `resolve(value, outcome?)` and `reject(error)`
88
- - `CommonEntrypoint` — entrypoint with `getAlias()`, `getPath()`, `getGuards()`, `handle`
192
+ // Add a guard to every declaration while keeping the tree paths consumers bind by.
193
+ export const auditedProtocols = mapProtocols(projectProtocols, declaration =>
194
+ decorateEntrypoint(declaration, { guards: [...declaration.guards, AUDIT_GUARD] }))
89
195
 
90
- ## Related Packages
196
+ // Resolve inherited gates without materializing anything (e.g. to hide a menu item).
197
+ const gates = gatesOf(auditedProtocols.build, auditedProtocols)
198
+ ```
199
+
200
+ `decorateEntrypoint` returns a new frozen protocol with the same request/response pair; the input
201
+ tree and its protocols are left untouched.
202
+
203
+ ## API
91
204
 
92
- - [`@owlmeans/route`](../route) — `route()` factory used in `entrypoint(route(...), ...)`
93
- - [`@owlmeans/server-entrypoint`](../server-entrypoint) — server-side `elevate()` to attach handlers
94
- - [`@owlmeans/client-entrypoint`](../client-entrypoint) — client-side entrypoint with API call support
95
- - [`@owlmeans/server-app`](../server-app) — re-exports everything from this package
205
+ ### Protocol declaration (`.`)
206
+
207
+ | Symbol | Kind | Purpose |
208
+ |---|---|---|
209
+ | `protocol(route, contract, options?)` | function | Create an immutable, typed `EntrypointProtocol` |
210
+ | `openProtocol(route, options?)` | function | Create an intentionally untyped protocol (`OpenRequest` / `OpenValue`) for escape hatches |
211
+ | `contract(...)` | function | Body-only contract: `contract()`, `contract(response)`, `contract(body, response)` |
212
+ | `contract.request(sections, response)` | function | Contract with independently typed `body`, `params`, `query`, `headers` |
213
+ | `typed<T>(schema?)` | function | Type-only source, optionally paired with an AJV schema |
214
+ | `schema<T>(jsonSchema)` | function | Brand a reusable AJV schema with its model type (`EntrypointSchema<T>`) |
215
+ | `protocols(tree)` | function | Flatten an `EntrypointTree` to a declaration array for registration |
216
+ | `mapProtocols(tree, mapper)` | function | Rebuild a frozen tree, transforming each declaration in place of its path |
217
+ | `decorateEntrypoint(protocol, options)` | function | Clone a protocol with replaced `guards` / `gate` / `sticky` |
218
+ | `gatesOf(protocol, tree)` | function | Gates a protocol inherits through its route parents, without materializing |
219
+ | `isEntrypointProtocol(value)` | function | Type guard for a protocol declaration |
220
+ | `entrypointRef<Request, Response>(alias)` | function | Typed reference for a dynamic remote address whose declaration cannot be imported |
221
+ | `aliasOf(reference)` | function | Alias of a reference or string, for registry/broker adapters |
222
+ | `materializeEntrypoint(protocol)` | function | Make a context-bindable `CommonEntrypoint` from a declaration (used by binding packages) |
223
+ | `provideResponse<T>(original?)` | function | Create an `AbstractResponse<T>` for invoking a handler or guard outside a transport |
224
+ | `transportAlias(protocol = 'http')` | function | Service alias `transport:<protocol>` a transport registers under |
225
+ | `EntrypointOutcome` | enum | `Ok`, `Accepted`, `Created`, `Finished` — returned by `invoke()` and `resolve()` |
226
+
227
+ ### Contract and protocol types
228
+
229
+ | Symbol | Kind | Purpose |
230
+ |---|---|---|
231
+ | `EntrypointProtocol<Request, Response>` | interface | Typed immutable declaration; also a context reference to its registered entrypoint |
232
+ | `EntrypointProtocolDeclaration` | interface | Runtime fields common to every protocol (`alias`, `route`, `contract`, `sticky`, `guards`, `gate`) |
233
+ | `EntrypointContract<Request, Response>` | interface | Runtime schemas plus the compile-time pair |
234
+ | `EntrypointOptions` | interface | `sticky`, `guards`, `gate: { alias, params }` |
235
+ | `EntrypointGate` | interface | A resolved `{ alias, params }` gate |
236
+ | `EntrypointTree` | interface | Nested object of protocols |
237
+ | `RequestShape`, `OpenRequest`, `OpenValue` | types | Request section shape; the untyped request and value |
238
+ | `Typed<T>`, `EntrypointSchema<T>`, `ShapeSource<T>` | types | Contract sources |
239
+ | `RequestSources`, `RuntimeRequestSchemas`, `RuntimeResponseSchemas` | interfaces | Contract internals |
240
+ | `RequestOf`, `ResponseOf`, `BodyOf`, `ParamsOf`, `QueryOf`, `HeadersOf`, `CallRequestOf` | types | Derive contract types from a protocol |
241
+ | `HandlerRequest<Request>` | type | Typed request at a handler boundary, including transport metadata |
242
+ | `EntrypointRequestMeta` | interface | `alias`, `auth`, `entity`, `path`, `canceled`, `cancel` |
243
+ | `RegisteredEntrypoint<Request, Response>` | interface | Context-bound entrypoint with `call`, `invoke`, `url`, `validate` |
244
+ | `CallOptions`, `CallArguments`, `UrlArguments` | types | Addressing/transport controls and argument tuples of `call` / `url` |
245
+ | `EntrypointResult<Response>` | interface | `{ value, outcome }` returned by `invoke()` |
246
+ | `MaterializedEntrypoint<Protocol>` | type | `CommonEntrypoint` carrying its `protocol` |
247
+
248
+ ### Runtime types
249
+
250
+ | Symbol | Kind | Purpose |
251
+ |---|---|---|
252
+ | `CommonEntrypoint` | interface | Bound entrypoint: `alias`, `route`, `sticky`, `guards`, `gate`, `gateParams`, `filter`, `handle`, and context-computed `segment()`, `path()`, `mount()`, `service()`, `address()`, `isLocal()`, `parent()`, `getGuards()`, `getGates()` |
253
+ | `CommonEntrypointOptions` | interface | Partial `CommonEntrypoint` options |
254
+ | `AbstractRequest<T>` | interface | Request with `alias`, `auth`, `entity`, `params`, `body`, `query`, `headers`, `path`, `timeout`, `signal` |
255
+ | `AbstractResponse<T>` | interface | Response with `value`, `outcome`, `error`, `resolve(value, outcome?)`, `reject(error)` |
256
+ | `ResolvedEntity` | interface | Organization as handlers see it: `id` (stable key), `slug`, `iamKey` |
257
+ | `EntrypointHandler`, `EntrypointMatch`, `EntrypointAssert` | interfaces | Handler, guard-match and gate-assert signatures |
258
+ | `GuardService` | interface | Guard service: `match`, `handle`, and client-side `authenticated`, `token` |
259
+ | `GateService` | interface | Lazy gate service with `assert(req, res, params)` |
260
+ | `Filter` | interface | AJV schemas by section (`query`, `params`, `body`, `response`, `headers`) |
261
+ | `EntrypointTransport` | interface | `{ protocol, handle }` — the service that carries a call for one route protocol |
262
+
263
+ ### `@owlmeans/entrypoint/utils`
264
+
265
+ | Symbol | Kind | Purpose |
266
+ |---|---|---|
267
+ | `isEntrypoint(obj)` | function | Type guard for a materialized `CommonEntrypoint` |
268
+ | `CreateEntrypointSignature<M>` | interface | `(route, opts?) => M` factory signature |
269
+
270
+ ### Transport
271
+
272
+ Register a service under `transportAlias(protocol)` implementing `EntrypointTransport` and every
273
+ call to an entrypoint on that route protocol goes through it — `@owlmeans/queue` does this for
274
+ `RouteProtocols.QUEUE`. Without a registered transport the call goes over HTTP.
275
+
276
+ ## Common pitfalls
277
+
278
+ - Do not export or import alias strings as an API. Keep them private to the declaration module and
279
+ pass protocol objects; raw aliases belong only in dynamic registry or broker adapters.
280
+ - Do not use `openProtocol` just to skip declaring input or output types — it is for intentionally
281
+ untyped boundaries.
282
+ - Use `typed<Model>(schema)` or `schema<Model>(...)` rather than a bare `JSONSchemaType<Model>`; a bare
283
+ AJV generic can widen a section to `OpenValue`.
284
+ - Never mutate guards, gates, schemas or a declaration collection, and never replace an item in an
285
+ entrypoint array. Derive with `decorateEntrypoint` / `mapProtocols` and bind that exact object.
286
+ - Reference a parent by its protocol object (`backend({ parent: base })`), not by its alias.
287
+ - Do not construct contextual compatibility entrypoints or look a protocol up by alias with a generic;
288
+ use `entrypointRef` only when the declaration truly cannot be imported.
289
+ - Callers use `call` / `invoke` / `url` and do not branch on the carrier — the route protocol and its
290
+ transport decide.
291
+ - Inherited gates are deduplicated by gate service: a child declaring a gate under the same service
292
+ replaces its ancestor's gate for that service rather than adding to it.
293
+ - `request.entity` is set only where authentication ran `attachEntity`; handlers key records by
294
+ `entity.id` (via `requireEntityKey` / `requireEntity` from `@owlmeans/auth-common`), never by the
295
+ token's `entitySlug`.
296
+
297
+ ## Related packages
298
+
299
+ - [`@owlmeans/route`](../route) — `route()`, `backend()`, `socket()`, `job()`, `frontend()` used in `protocol(route(...), ...)`
300
+ - [`@owlmeans/context`](../context) — `BasicEntrypoint`, `EntrypointReference` and `context.entrypoint(...)`
301
+ - [`@owlmeans/server-entrypoint`](../server-entrypoint) — server-side `bind()` / `bindAll()` to attach handlers
302
+ - [`@owlmeans/server-api`](../server-api) — `handlers<Context>()` with protocol-inferred `body` / `params` / `request`
303
+ - [`@owlmeans/server-socket`](../server-socket) — `connection(protocol, handler)` for socket protocols
304
+ - [`@owlmeans/client-entrypoint`](../client-entrypoint) — client `bind()`, `bindAll()`, `bindScreen()` with typed calls
305
+ - [`@owlmeans/queue`](../queue) — the QUEUE transport for `job()` routes
306
+ - [`@owlmeans/auth-common`](../auth-common) — guard aliases and entity helpers used in access options and handlers
307
+ - [`@owlmeans/server-app`](../server-app) — re-exports `contract`, `protocol`, `typed`, `EntrypointOutcome` and the request/response types
96
308
 
97
309
  <!-- owlmeans:agent-guidance:start -->
98
310
  ## Agent guidance
@@ -102,7 +314,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
102
314
  your project's skill store (`.agents/skills/`):
103
315
 
104
316
  ```sh
105
- npx @owlmeans/agent-skills
317
+ npx @owlmeans/agent-skills@^0.1.18-rc.21
106
318
  ```
107
319
 
108
320
  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.0",
5
- "generatedAt": "2026-08-16T22:20:50.515Z",
4
+ "version": "0.1.18-rc.20",
5
+ "generatedAt": "2026-09-15T12:21:31.948Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,62 +1,98 @@
1
1
  ---
2
2
  name: entrypoint
3
- description: How to use @owlmeans/entrypoint — declarative entrypoint/route definitions with entrypoint(), guard(), gate(), filter(), body(), params(), query() builders. Auto-invoked when importing from this package or defining a service entrypoint declaration. Also covers the deprecated @owlmeans/module reexport shim (module() → entrypoint()).
3
+ description: How to declare immutable, typed @owlmeans/entrypoint protocols with protocol(), contract(), typed(), protocol trees, guards, gates, and route transports. Load before defining a shared API, socket, queue, or screen contract.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
7
 
8
8
  # @owlmeans/entrypoint
9
9
 
10
+ **Install:** `bun add @owlmeans/entrypoint@^0.1.18-rc.19`
11
+
10
12
  **Layer:** Core
11
- **Install:** `"@owlmeans/entrypoint": "^0.1.18-rc.0"` in `dependencies`
12
-
13
- ## Key Exports
14
-
15
- | Export | Description |
16
- |--------|-------------|
17
- | `entrypoint(...builders)` | Compose an entrypoint declaration from builders |
18
- | `guard(alias, ...gates)` | Attach a security guard |
19
- | `gate(alias, scopes[])` | Define an auth gate (used inside a guard) |
20
- | `filter(...validators)` | Attach request validators |
21
- | `body(Schema)` | AJV body validator |
22
- | `params(Schema)` | URL params validator |
23
- | `query(Schema)` | Query string validator |
24
- | `ClientEntrypoint<T>` types | Resolved entrypoint type used for `ctx.entrypoint<...>(alias).call(...)` |
25
- | `EntrypointOutcome` | Enum: Ok, Accepted, Created, Finished |
26
- | Constants | Built-in entrypoint aliases |
27
-
28
- ## Subpath Exports
29
-
30
- - `./utils` — entrypoint construction helpers (`isEntrypoint`, `CreateEntrypointSignature`)
31
-
32
- ## Usage
33
-
34
- Define entrypoints in a shared `common` package, then `elevate()` them with handlers in server/web packages:
35
-
36
- ```typescript
37
- import { entrypoint, guard, gate, filter, body } from '@owlmeans/entrypoint'
38
- import { route, RouteMethod } from '@owlmeans/route'
39
- import { DEFAULT_GUARD } from '@owlmeans/auth-common'
40
- import { OIDC_GATE } from '@owlmeans/oidc'
41
- import { CreateProjectSchema } from './schemas.js'
42
-
43
- export const managerModules = [
44
- entrypoint(
45
- route(manager.back.account.base, '/account'),
46
- guard(DEFAULT_GUARD, gate(OIDC_GATE, [`my-service-account-{entity}`]))
13
+
14
+ An entrypoint protocol is the immutable shared contract for one addressable route. It owns the
15
+ route, typed request sections, response type, schemas, guards, gate, and sticky flag. Server and
16
+ client behaviour is added only by the corresponding binding package.
17
+
18
+ ## Declare a named tree
19
+
20
+ ```ts
21
+ import { contract, protocol, typed } from '@owlmeans/entrypoint'
22
+ import { backend, route, RouteMethod } from '@owlmeans/route'
23
+
24
+ const aliases = { base: 'project', create: 'project:create', get: 'project:get' } as const
25
+ const projectBase = protocol(route(aliases.base, '/projects', backend()), contract(typed<void>()))
26
+
27
+ export const projectProtocols = {
28
+ base: projectBase,
29
+ create: protocol(
30
+ route(aliases.create, '/', backend({ parent: projectBase, method: RouteMethod.POST })),
31
+ contract.request({ body: typed<CreateProject>(CreateProjectSchema) }, typed<Project>()),
32
+ { guards: DEFAULT_GUARD }
47
33
  ),
48
- entrypoint(
49
- route(manager.back.project.create, '/create', {
50
- parent: manager.back.project.base,
51
- method: RouteMethod.POST,
52
- }),
53
- filter(body(CreateProjectSchema))
34
+ get: protocol(
35
+ route(aliases.get, '/:id', backend({ parent: projectBase })),
36
+ contract.request({ params: typed<{ id: string }>() }, typed<Project>())
54
37
  ),
55
- ]
38
+ }
39
+ ```
40
+
41
+ - Export a `*Protocols` object whose property names describe the contract. Its protocol values are
42
+ the only cross-layer references. Flatten it with `protocols(projectProtocols)` only at registration.
43
+ - Use `protocol(route, contract, options?)` for every typed boundary.
44
+ - `openProtocol(route, options?)` is an intentional untyped escape hatch; do not use it merely to
45
+ avoid declaring an input or output type.
46
+ - Use `decorateEntrypoint(protocol, options)` only when deriving an immutable decoration; never
47
+ mutate guards, gates, schemas, or a declaration collection.
48
+
49
+ Keep aliases private to this module. A child should use its parent protocol object
50
+ (`backend({ parent: projectBase })`); route creation stores its alias. Normal application code
51
+ imports protocol objects, while raw aliases are limited to dynamic registry or broker adapters.
52
+
53
+ ## Contract sources
54
+
55
+ | Source | Use |
56
+ |---|---|
57
+ | `typed<Model>(schema)` | A type paired with its AJV runtime validator. Prefer at declaration boundaries. |
58
+ | `typed<Model>()` | A type-only request section or response. |
59
+ | `schema<Model>(schema)` | A reusable named typed AJV schema. |
60
+ | `contract(body, response)` | A body-only contract. |
61
+ | `contract.request({ body, params, query, headers }, response)` | Independently typed request sections. |
62
+
63
+ Use `typed<Model>(schema)` rather than a bare `JSONSchemaType<Model>` when the model must remain
64
+ exact. A bare AJV generic can widen a protocol section to `OpenValue`.
65
+
66
+ `RequestOf<Protocol>`, `ResponseOf<Protocol>`, `BodyOf<Protocol>`, `ParamsOf<Protocol>`,
67
+ `QueryOf<Protocol>`, and `HeadersOf<Protocol>` derive the contract types. A handler receives
68
+ `HandlerRequest<RequestOf<Protocol>>`, which includes the transport metadata as well as the typed
69
+ sections. Use `entrypointRef<Request, Response>(alias)` only for a dynamic remote address whose
70
+ declaration is unavailable to import.
71
+
72
+ ## Authorization and addressing
73
+
74
+ Add `guards`, `gate`, and `sticky` directly in `EntrypointOptions`:
75
+
76
+ ```ts
77
+ protocol(route(...), contract(...), {
78
+ guards: [DEFAULT_GUARD, AUDIT_GUARD],
79
+ gate: { alias: PROJECT_GATE, params: ['project--read@id'] },
80
+ sticky: true,
81
+ })
56
82
  ```
57
83
 
58
- ## Depends On
84
+ Guards and gates are inherited through the parent route by the bound runtime entrypoint. A route's
85
+ protocol chooses the carrier (HTTP, socket, or queue); callers only use `call`, `invoke`, or `url`
86
+ on the client-bound protocol and do not branch on a transport.
87
+
88
+ ## Bind, never replace
89
+
90
+ - Server: `bind(protocol, handlers<Context>().body|params|request(...))` from
91
+ `@owlmeans/server-entrypoint` / `@owlmeans/server-api`.
92
+ - Socket: `bind(protocol, connection(protocol, handler))`.
93
+ - Client API route: `bind(protocol)` or `bindAll(tree)` from `@owlmeans/client-entrypoint`.
94
+ - Client screen: `bindScreen(protocol, componentHandler)`.
59
95
 
60
- - `@owlmeans/route` — `route()`, `RouteMethod`
61
- - `@owlmeans/auth-common` — guard aliases (`DEFAULT_GUARD`)
62
- - `@owlmeans/error`, `@owlmeans/i18n`
96
+ Do not construct contextual compatibility entrypoints, look a protocol up by alias with a generic,
97
+ or replace an item in an entrypoint array. Import the declaration and bind that exact object. This
98
+ keeps declarations immutable and lets TypeScript infer requests and replies end to end.
package/build/consts.d.ts CHANGED
@@ -4,4 +4,9 @@ export declare enum EntrypointOutcome {
4
4
  Created = "created",
5
5
  Finished = "finished"
6
6
  }
7
+ /**
8
+ * Service alias a transport registers under. The default protocol is the web one, so an application
9
+ * that binds nothing keeps talking HTTP.
10
+ */
11
+ export declare const transportAlias: (protocol?: string) => string;
7
12
  //# sourceMappingURL=consts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,oBAAY,iBAAiB;IAC3B,EAAE,OAAO;IACT,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,QAAQ,aAAa;CACtB"}
1
+ {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,oBAAY,iBAAiB;IAC3B,EAAE,OAAO;IACT,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,QAAQ,aAAa;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,cAAc,MAAM,WAAqC,CAAA"}
package/build/consts.js CHANGED
@@ -5,4 +5,9 @@ export var EntrypointOutcome;
5
5
  EntrypointOutcome["Created"] = "created";
6
6
  EntrypointOutcome["Finished"] = "finished";
7
7
  })(EntrypointOutcome || (EntrypointOutcome = {}));
8
+ /**
9
+ * Service alias a transport registers under. The default protocol is the web one, so an application
10
+ * that binds nothing keeps talking HTTP.
11
+ */
12
+ export const transportAlias = (protocol = 'http') => `transport:${protocol}`;
8
13
  //# sourceMappingURL=consts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAN,IAAY,iBAKX;AALD,WAAY,iBAAiB;IAC3B,8BAAS,CAAA;IACT,0CAAqB,CAAA;IACrB,wCAAmB,CAAA;IACnB,0CAAqB,CAAA;AACvB,CAAC,EALW,iBAAiB,KAAjB,iBAAiB,QAK5B"}
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAN,IAAY,iBAKX;AALD,WAAY,iBAAiB;IAC3B,8BAAS,CAAA;IACT,0CAAqB,CAAA;IACrB,wCAAmB,CAAA;IACnB,0CAAqB,CAAA;AACvB,CAAC,EALW,iBAAiB,KAAjB,iBAAiB,QAK5B;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAQ,GAAW,MAAM,EAAE,EAAE,CAAC,aAAa,QAAQ,EAAE,CAAA"}
@@ -1,5 +1,4 @@
1
1
  import type { CommonEntrypoint } from './types.js';
2
2
  import type { CreateEntrypointSignature } from './utils/types.js';
3
3
  export declare const entrypoint: CreateEntrypointSignature<CommonEntrypoint>;
4
- export declare const parent: <T extends CommonEntrypoint | CommonEntrypoint[]>(ep: T, aliasOrParent: string, _parent?: string) => T;
5
4
  //# sourceMappingURL=entrypoint.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entrypoint.d.ts","sourceRoot":"","sources":["../src/entrypoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAEjE,eAAO,MAAM,UAAU,EAAE,yBAAyB,CAAC,gBAAgB,CAqFlE,CAAA;AAED,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,gBAAgB,GAAG,gBAAgB,EAAE,MAAM,CAAC,iBAAiB,MAAM,YAAY,MAAM,KAAG,CAcxH,CAAA"}
1
+ {"version":3,"file":"entrypoint.d.ts","sourceRoot":"","sources":["../src/entrypoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAKlD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAEjE,eAAO,MAAM,UAAU,EAAE,yBAAyB,CAAC,gBAAgB,CAkElE,CAAA"}