stitchkit 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/index.js +2 -0
- package/dist/server/openapi.d.ts +17 -2
- package/dist/server/openapi.d.ts.map +1 -1
- package/llms-full.txt +59 -2
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -398,6 +398,8 @@ function generateOpenApiDocument(config) {
|
|
|
398
398
|
for (const [key, method] of Object.entries(service.methods)) {
|
|
399
399
|
if (method.expose && !method.expose.includes("HTTP"))
|
|
400
400
|
continue;
|
|
401
|
+
if (config.includeMethod && !config.includeMethod(method))
|
|
402
|
+
continue;
|
|
401
403
|
const servicePath = joinPath("/", service.prefix, method.path === "/" ? "" : method.path);
|
|
402
404
|
const fullPath = toOpenApiPath(pathPrefix ? joinPath(pathPrefix, servicePath) : servicePath);
|
|
403
405
|
const parameters = [];
|
package/dist/server/openapi.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* emits — not a divergent code path. Schemas are inlined (valid OpenAPI);
|
|
11
11
|
* `$ref` de-duplication can come later if a spec grows unwieldy.
|
|
12
12
|
*/
|
|
13
|
-
import type { RawRoute, ServiceDef } from './types';
|
|
13
|
+
import type { MethodDef, RawRoute, ServiceDef } from './types';
|
|
14
14
|
export interface OpenApiInfo {
|
|
15
15
|
title: string;
|
|
16
16
|
version: string;
|
|
@@ -31,6 +31,19 @@ export interface OpenApiConfig {
|
|
|
31
31
|
}>;
|
|
32
32
|
/** `servers` block for the spec. */
|
|
33
33
|
servers?: OpenApiServer[];
|
|
34
|
+
/**
|
|
35
|
+
* Emit only the methods this predicate keeps — a curated public spec instead
|
|
36
|
+
* of the whole HTTP surface. The predicate decides the policy (the core stays
|
|
37
|
+
* generic): filter on `method.scope`, `method.meta` (the recommended
|
|
38
|
+
* declarative allowlist — mark endpoints `meta: { public: true }` and keep
|
|
39
|
+
* `(m) => m.meta?.public === true`), `method.key`, anything on the method.
|
|
40
|
+
* Omit to include every HTTP method (the default).
|
|
41
|
+
*
|
|
42
|
+
* This controls what the spec **advertises**, not access — a hidden endpoint
|
|
43
|
+
* is still callable; the auth `scope` gate is the actual guard. Build a
|
|
44
|
+
* separate filtered document for a public route (see the guide).
|
|
45
|
+
*/
|
|
46
|
+
includeMethod?: (method: Readonly<MethodDef>) => boolean;
|
|
34
47
|
}
|
|
35
48
|
export interface OpenApiDocument {
|
|
36
49
|
openapi: '3.1.0';
|
|
@@ -41,7 +54,9 @@ export interface OpenApiDocument {
|
|
|
41
54
|
/**
|
|
42
55
|
* Generate an OpenAPI 3.1 document from contract services. Only methods exposed
|
|
43
56
|
* on HTTP are included — a method whose `expose` omits `'HTTP'` (an MCP/agent
|
|
44
|
-
* only tool) is skipped, matching the router's own route-building rule.
|
|
57
|
+
* only tool) is skipped, matching the router's own route-building rule. Pass
|
|
58
|
+
* `includeMethod` to emit a curated subset (a public spec) instead of the whole
|
|
59
|
+
* surface.
|
|
45
60
|
*/
|
|
46
61
|
export declare function generateOpenApiDocument(config: OpenApiConfig): OpenApiDocument;
|
|
47
62
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/server/openapi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../src/server/openapi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE/D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC;IACxB,iFAAiF;IACjF,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC,CAAC;IAChE,oCAAoC;IACpC,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,KAAK,OAAO,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAChD;AAsED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,CAuH9E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,QAAQ,CAM9E"}
|
package/llms-full.txt
CHANGED
|
@@ -355,7 +355,10 @@ beforeHandle: (ctx, endpoint) => {
|
|
|
355
355
|
}
|
|
356
356
|
```
|
|
357
357
|
|
|
358
|
-
`meta` is **app-private** — it is never serialized into the OpenAPI document.
|
|
358
|
+
`meta` is **app-private** — it is never serialized into the OpenAPI document. It
|
|
359
|
+
can still *drive* generation: `generateOpenApiDocument`'s `includeMethod` reads
|
|
360
|
+
it to curate a public spec (e.g. `meta: { public: true }`), without ever emitting
|
|
361
|
+
`meta` itself — see [Curating the spec](./server.md#curating-the-spec--includemethod).
|
|
359
362
|
|
|
360
363
|
> **Declare a meta type as a `type`, an inline literal, or with `satisfies` — not
|
|
361
364
|
> an `interface`.** A TS `interface` has no implicit index signature (it can be
|
|
@@ -856,6 +859,60 @@ bus.emit('user.created', { id: '1' })
|
|
|
856
859
|
A typed in-process pub/sub — decouple a handler from the side effects of its
|
|
857
860
|
write without reaching for an external queue.
|
|
858
861
|
|
|
862
|
+
## OpenAPI
|
|
863
|
+
|
|
864
|
+
`generateOpenApiDocument` builds an OpenAPI 3.1 document straight from the
|
|
865
|
+
contracts — the contract *is* the spec, no decorators or hand-maintained
|
|
866
|
+
annotations (→ ADR 0018). `openApiRoute` serves it as a raw route:
|
|
867
|
+
|
|
868
|
+
```ts
|
|
869
|
+
import { generateOpenApiDocument, openApiRoute } from 'stitchkit/server'
|
|
870
|
+
|
|
871
|
+
const doc = generateOpenApiDocument({
|
|
872
|
+
info: { title: 'My API', version: '1.0.0' },
|
|
873
|
+
services: [users, orders],
|
|
874
|
+
})
|
|
875
|
+
createServer({ services: [users, orders], rawRoutes: [openApiRoute('/openapi.json', doc)] })
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
Only HTTP-exposed methods appear (an MCP/agent-only tool is skipped).
|
|
879
|
+
|
|
880
|
+
### Curating the spec — `includeMethod`
|
|
881
|
+
|
|
882
|
+
To publish a **subset** — a public spec that advertises only some methods
|
|
883
|
+
without revealing the rest — pass `includeMethod`. It keeps the core generic:
|
|
884
|
+
*you* decide the policy, filtering on anything the method carries. The
|
|
885
|
+
recommended declarative allowlist marks endpoints with the existing `meta`
|
|
886
|
+
passthrough and keeps those:
|
|
887
|
+
|
|
888
|
+
```ts
|
|
889
|
+
// contract — declarative, one source of truth
|
|
890
|
+
getBalance: { method: 'GET', path: '/balance', desc: '…', scope: 'account',
|
|
891
|
+
meta: { public: true }, output: BalanceSchema }
|
|
892
|
+
|
|
893
|
+
// generation — the app's policy
|
|
894
|
+
const publicDoc = generateOpenApiDocument({
|
|
895
|
+
info: { title: 'Public API', version: '1.0.0' },
|
|
896
|
+
services: [account],
|
|
897
|
+
includeMethod: (m) => m.meta?.public === true,
|
|
898
|
+
})
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
An excluded method's whole entry — path *and* every schema inlined within it —
|
|
902
|
+
is simply never emitted, so nothing about a hidden endpoint leaks.
|
|
903
|
+
|
|
904
|
+
> **The filter advertises; it does not authorize.** Hiding a method from the
|
|
905
|
+
> spec does **not** protect it — it is still callable, and the auth `scope` gate
|
|
906
|
+
> is the only thing guarding it. And because `openApiRoute` closes over the
|
|
907
|
+
> document you hand it, the filter only matters if you feed it a filtered one:
|
|
908
|
+
> serve **two** documents — a full internal spec and a filtered public spec on
|
|
909
|
+
> separate routes — never one unfiltered `openApiRoute` on a public path.
|
|
910
|
+
|
|
911
|
+
```ts
|
|
912
|
+
const internal = openApiRoute('/internal/openapi.json', fullDoc) // behind auth
|
|
913
|
+
const publicSpec = openApiRoute('/openapi.json', publicDoc) // curated
|
|
914
|
+
```
|
|
915
|
+
|
|
859
916
|
|
|
860
917
|
==============================================================================
|
|
861
918
|
# Guide: Typed client (docs/guide/client.md)
|
|
@@ -3286,7 +3343,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
3286
3343
|
|--------|------|---------|
|
|
3287
3344
|
| `generateOpenApiDocument` | function | an OpenAPI 3.1 document from contract services — [ADR 0018](../decisions/0018-openapi-generation.md) |
|
|
3288
3345
|
| `openApiRoute` | function | a `RawRoute` that serves the document as JSON |
|
|
3289
|
-
| `OpenApiConfig` | _type_ | config for `generateOpenApiDocument` |
|
|
3346
|
+
| `OpenApiConfig` | _type_ | config for `generateOpenApiDocument` (incl. `includeMethod` — curate a public subset) — [guide](../guide/server.md#curating-the-spec--includemethod) |
|
|
3290
3347
|
| `OpenApiDocument` | _type_ | the generated document |
|
|
3291
3348
|
| `OpenApiInfo` | _type_ | the spec `info` block |
|
|
3292
3349
|
| `OpenApiServer` | _type_ | a spec `servers` entry |
|
package/package.json
CHANGED