@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
package/README.md CHANGED
@@ -1,98 +1,300 @@
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 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
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 picks a built-in or package-owned 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 { makeEntityScope } 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(makeEntityScope(request).requireEntityKey(), 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 (`makeEntityScope(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 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, route, socket } from '@owlmeans/route'
148
+ import { DEFAULT_GUARD } from '@owlmeans/auth-common'
149
+ import { PROJECT_GATE } 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',
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
+ } as const
171
+
172
+ type WatchRequest = HandlerRequest<RequestOf<typeof projectProtocols.watch>>
82
173
  ```
83
174
 
84
- ### Types
175
+ ### 5. Decorate a whole tree without flattening it
176
+
177
+ ```ts
178
+ import { decorateEntrypoint, gatesOf, mapProtocols } from '@owlmeans/entrypoint'
179
+ import { AUDIT_GUARD } from './consts.js'
180
+ import { projectProtocols } from './protocols.js'
85
181
 
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`
182
+ // Add a guard to every declaration while keeping the tree paths consumers bind by.
183
+ export const auditedProtocols = mapProtocols(projectProtocols, declaration =>
184
+ decorateEntrypoint(declaration, { guards: [...declaration.guards, AUDIT_GUARD] }))
89
185
 
90
- ## Related Packages
186
+ // Resolve inherited gates without materializing anything (e.g. to hide a menu item).
187
+ const gates = gatesOf(auditedProtocols.build, auditedProtocols)
188
+ ```
189
+
190
+ `decorateEntrypoint` returns a new frozen protocol with the same request/response pair; the input
191
+ tree and its protocols are left untouched.
192
+
193
+ ## API
91
194
 
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
195
+ ### Protocol declaration (`.`)
196
+
197
+ | Symbol | Kind | Purpose |
198
+ |---|---|---|
199
+ | `protocol(route, contract, options?)` | function | Create an immutable, typed `EntrypointProtocol` |
200
+ | `openProtocol(route, options?)` | function | Create an intentionally untyped protocol (`OpenRequest` / `OpenValue`) for escape hatches |
201
+ | `contract(...)` | function | Body-only contract: `contract()`, `contract(response)`, `contract(body, response)` |
202
+ | `contract.request(sections, response)` | function | Contract with independently typed `body`, `params`, `query`, `headers` |
203
+ | `typed<T>(schema?)` | function | Type-only source, optionally paired with an AJV schema |
204
+ | `schema<T>(jsonSchema)` | function | Brand a reusable AJV schema with its model type (`EntrypointSchema<T>`) |
205
+ | `protocols(tree)` | function | Flatten an `EntrypointTree` to a declaration array for registration |
206
+ | `mapProtocols(tree, mapper)` | function | Rebuild a frozen tree, transforming each declaration in place of its path |
207
+ | `decorateEntrypoint(protocol, options)` | function | Clone a protocol with replaced `guards` / `gate` / `sticky` |
208
+ | `gatesOf(protocol, tree)` | function | Gates a protocol inherits through its route parents, without materializing |
209
+ | `isEntrypointProtocol(value)` | function | Type guard for a protocol declaration |
210
+ | `entrypointRef<Request, Response>(alias)` | function | Typed reference for a dynamic remote address whose declaration cannot be imported |
211
+ | `aliasOf(reference)` | function | Alias of a reference or string, for registry/transport adapters |
212
+ | `materializeEntrypoint(protocol)` | function | Make a context-bindable `CommonEntrypoint` from a declaration (used by binding packages) |
213
+ | `provideResponse<T>(original?)` | function | Create an `AbstractResponse<T>` for invoking a handler or guard outside a transport |
214
+ | `transportAlias(protocol = 'http')` | function | Service alias `transport:<protocol>` a transport registers under |
215
+ | `EntrypointOutcome` | enum | `Ok`, `Accepted`, `Created`, `Finished` — returned by `invoke()` and `resolve()` |
216
+
217
+ ### Contract and protocol types
218
+
219
+ | Symbol | Kind | Purpose |
220
+ |---|---|---|
221
+ | `EntrypointProtocol<Request, Response>` | interface | Typed immutable declaration; also a context reference to its registered entrypoint |
222
+ | `EntrypointProtocolDeclaration` | interface | Runtime fields common to every protocol (`alias`, `route`, `contract`, `sticky`, `guards`, `gate`) |
223
+ | `EntrypointContract<Request, Response>` | interface | Runtime schemas plus the compile-time pair |
224
+ | `EntrypointOptions` | interface | `sticky`, `guards`, `gate: { alias, params }` |
225
+ | `EntrypointGate` | interface | A resolved `{ alias, params }` gate |
226
+ | `EntrypointTree` | interface | Nested object of protocols |
227
+ | `RequestShape`, `OpenRequest`, `OpenValue` | types | Request section shape; the untyped request and value |
228
+ | `Typed<T>`, `EntrypointSchema<T>`, `ShapeSource<T>` | types | Contract sources |
229
+ | `RequestSources`, `RuntimeRequestSchemas`, `RuntimeResponseSchemas` | interfaces | Contract internals |
230
+ | `RequestOf`, `ResponseOf`, `BodyOf`, `ParamsOf`, `QueryOf`, `HeadersOf`, `CallRequestOf` | types | Derive contract types from a protocol |
231
+ | `HandlerRequest<Request>` | type | Typed request at a handler boundary, including transport metadata |
232
+ | `EntrypointRequestMeta` | interface | `alias`, `auth`, `entity`, `path`, `canceled`, `cancel` |
233
+ | `RegisteredEntrypoint<Request, Response>` | interface | Context-bound entrypoint with `call`, `invoke`, `url`, `validate` |
234
+ | `CallOptions`, `CallArguments`, `UrlArguments` | types | Addressing/transport controls and argument tuples of `call` / `url` |
235
+ | `EntrypointResult<Response>` | interface | `{ value, outcome }` returned by `invoke()` |
236
+ | `MaterializedEntrypoint<Protocol>` | type | `CommonEntrypoint` carrying its `protocol` |
237
+
238
+ ### Runtime types
239
+
240
+ | Symbol | Kind | Purpose |
241
+ |---|---|---|
242
+ | `CommonEntrypoint` | interface | Bound entrypoint: `alias`, `route`, `sticky`, `guards`, `gate`, `gateParams`, `filter`, `handle`, and context-computed `segment()`, `path()`, `mount()`, `service()`, `address()`, `isLocal()`, `parent()`, `getGuards()`, `getGates()` |
243
+ | `CommonEntrypointOptions` | interface | Partial `CommonEntrypoint` options |
244
+ | `AbstractRequest<T>` | interface | Request with `alias`, `auth`, `entity`, `params`, `body`, `query`, `headers`, `path`, `timeout`, `signal` |
245
+ | `AbstractResponse<T>` | interface | Response with `value`, `outcome`, `error`, `resolve(value, outcome?)`, `reject(error)` |
246
+ | `ResolvedEntity` | interface | Organization as handlers see it: `id` (stable key), `slug`, `iamKey` |
247
+ | `EntrypointHandler`, `EntrypointMatch`, `EntrypointAssert` | interfaces | Handler, guard-match and gate-assert signatures |
248
+ | `GuardService` | interface | Guard service: `match`, `handle`, and client-side `authenticated`, `token` |
249
+ | `GateService` | interface | Lazy gate service with `assert(req, res, params)` |
250
+ | `Filter` | interface | AJV schemas by section (`query`, `params`, `body`, `response`, `headers`) |
251
+ | `EntrypointTransport` | interface | `{ protocol, handle }` — the service that carries a call for one route protocol |
252
+
253
+ ### `@owlmeans/entrypoint/utils`
254
+
255
+ | Symbol | Kind | Purpose |
256
+ |---|---|---|
257
+ | `isEntrypoint(obj)` | function | Type guard for a materialized `CommonEntrypoint` |
258
+ | `CreateEntrypointSignature<M>` | interface | `(route, opts?) => M` factory signature |
259
+
260
+ ### Transport
261
+
262
+ Register a service under `transportAlias(protocol)` implementing `EntrypointTransport` and every
263
+ call to an entrypoint on that route protocol goes through it. The package defining a custom
264
+ protocol owns its transport and option validation. Without a registered transport the call goes
265
+ over HTTP.
266
+
267
+ ## Common pitfalls
268
+
269
+ - Do not export or import alias strings as an API. Keep them private to the declaration module and
270
+ pass protocol objects; raw aliases belong only in dynamic registry or transport adapters.
271
+ - Do not use `openProtocol` just to skip declaring input or output types — it is for intentionally
272
+ untyped boundaries.
273
+ - Use `typed<Model>(schema)` or `schema<Model>(...)` rather than a bare `JSONSchemaType<Model>`; a bare
274
+ AJV generic can widen a section to `OpenValue`.
275
+ - Never mutate guards, gates, schemas or a declaration collection, and never replace an item in an
276
+ entrypoint array. Derive with `decorateEntrypoint` / `mapProtocols` and bind that exact object.
277
+ - Reference a parent by its protocol object (`backend({ parent: base })`), not by its alias.
278
+ - Do not construct contextual compatibility entrypoints or look a protocol up by alias with a generic;
279
+ use `entrypointRef` only when the declaration truly cannot be imported.
280
+ - Callers use `call` / `invoke` / `url` and do not branch on the carrier — the route protocol and its
281
+ transport decide.
282
+ - Inherited gates are deduplicated by gate service: a child declaring a gate under the same service
283
+ replaces its ancestor's gate for that service rather than adding to it.
284
+ - `request.entity` is set only where authentication ran `makeEntityScope(request).attachEntity(context)`; handlers key records by
285
+ `entity.id` (via `makeEntityScope(request).requireEntityKey()` / `.requireEntity()` from `@owlmeans/auth-common`), never by the
286
+ token's `entitySlug`.
287
+
288
+ ## Related packages
289
+
290
+ - [`@owlmeans/route`](../route) — `route()`, `backend()`, `socket()`, `frontend()` used in `protocol(route(...), ...)`
291
+ - [`@owlmeans/context`](../context) — `BasicEntrypoint`, `EntrypointReference` and `context.entrypoint(...)`
292
+ - [`@owlmeans/server-entrypoint`](../server-entrypoint) — server-side `bind()` / `bindAll()` to attach handlers
293
+ - [`@owlmeans/server-api`](../server-api) — `handlers<Context>()` with protocol-inferred `body` / `params` / `request`
294
+ - [`@owlmeans/server-socket`](../server-socket) — `connection(protocol, handler)` for socket protocols
295
+ - [`@owlmeans/client-entrypoint`](../client-entrypoint) — client `bind()`, `bindAll()`, `bindScreen()` with typed calls
296
+ - [`@owlmeans/auth-common`](../auth-common) — guard aliases and entity helpers used in access options and handlers
297
+ - [`@owlmeans/server-app`](../server-app) — re-exports `contract`, `protocol`, `typed`, `EntrypointOutcome` and the request/response types
96
298
 
97
299
  <!-- owlmeans:agent-guidance:start -->
98
300
  ## Agent guidance
@@ -102,7 +304,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
102
304
  your project's skill store (`.agents/skills/`):
103
305
 
104
306
  ```sh
105
- npx @owlmeans/agent-skills
307
+ npx @owlmeans/agent-skills@^0.1.18
106
308
  ```
107
309
 
108
310
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
- "schemaVersion": 2,
2
+ "schemaVersion": 3,
3
3
  "package": "@owlmeans/entrypoint",
4
- "version": "0.1.18-rc.7",
5
- "generatedAt": "2026-08-24T21:14:00.139Z",
4
+ "version": "0.1.18",
5
+ "generatedAt": "2026-10-28T00:31:34.766Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,76 +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, 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`
11
+
10
12
  **Layer:** Core
11
- **Install:** `"@owlmeans/entrypoint": "^0.1.18-rc.7"` in `dependencies`
12
-
13
- ## Key Exports
14
-
15
- | Export | Description |
16
- |--------|-------------|
17
- | `entrypoint(route, opts?)` | Declare an entrypoint on a route |
18
- | `guard(alias, opts?)` | Require a guard; returns options, so it wraps rather than takes them |
19
- | `gate(alias, params, opts?)` | Require a gate; passed as the `opts` of `guard(...)` |
20
- | `filter(filter, opts?)` | 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
- `guard`, `gate` and `filter` are **options-object combinators, not variadic composers**. Each
29
- returns a `CommonEntrypointOptions` and takes the next one as its final argument, so they nest:
30
-
31
- ```typescript
32
- entrypoint(
33
- route(app.api.item.remove, '/:id', { parent: app.api.item, method: RouteMethod.DELETE }),
34
- guard(DEFAULT_GUARD, gate(OIDC_GATE, ['item--delete@id']))
35
- )
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 }
33
+ ),
34
+ get: protocol(
35
+ route(aliases.get, '/:id', backend({ parent: projectBase })),
36
+ contract.request({ params: typed<{ id: string }>() }, typed<Project>())
37
+ ),
38
+ }
36
39
  ```
37
40
 
38
- Guards and gates are **inherited by child entrypoints** and enforced by the framework before a
39
- handler runs — a handler that re-checks them is duplicating an enforced rule, and is wrong even
40
- when it agrees.
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.
41
48
 
42
- ## Subpath Exports
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 transport adapters.
43
52
 
44
- - `./utils` — entrypoint construction helpers (`isEntrypoint`, `CreateEntrypointSignature`)
53
+ ## Contract sources
45
54
 
46
- ## Usage
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. |
47
62
 
48
- Define entrypoints in a shared `common` package, then `elevate()` them with handlers in server/web packages:
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`.
49
65
 
50
- ```typescript
51
- import { entrypoint, guard, gate, filter, body } from '@owlmeans/entrypoint'
52
- import { route, RouteMethod } from '@owlmeans/route'
53
- import { DEFAULT_GUARD } from '@owlmeans/auth-common'
54
- import { OIDC_GATE } from '@owlmeans/oidc'
55
- import { CreateProjectSchema } from './schemas.js'
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.
56
71
 
57
- export const managerModules = [
58
- entrypoint(
59
- route(manager.back.account.base, '/account'),
60
- guard(DEFAULT_GUARD, gate(OIDC_GATE, [`my-service-account-{entity}`]))
61
- ),
62
- entrypoint(
63
- route(manager.back.project.create, '/create', {
64
- parent: manager.back.project.base,
65
- method: RouteMethod.POST,
66
- }),
67
- filter(body(CreateProjectSchema))
68
- ),
69
- ]
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
+ })
70
82
  ```
71
83
 
72
- ## Depends On
84
+ Guards and gates are inherited through the parent route by the bound runtime entrypoint. A route's
85
+ protocol chooses the built-in or package-owned carrier; 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)`.
73
95
 
74
- - `@owlmeans/route` — `route()`, `RouteMethod`
75
- - `@owlmeans/auth-common` — guard aliases (`DEFAULT_GUARD`)
76
- - `@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.
@@ -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;AAGlD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAEjE,eAAO,MAAM,UAAU,EAAE,yBAAyB,CAAC,gBAAgB,CAkElE,CAAA"}