@ts-pf/openapi 0.1.0-beta.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/README.md +44 -0
- package/dist/envelope.d.ts +6 -0
- package/dist/envelope.d.ts.map +1 -0
- package/dist/envelope.js +76 -0
- package/dist/envelope.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +79 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/names.d.ts +6 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +23 -0
- package/dist/names.js.map +1 -0
- package/dist/openapi.d.ts +4 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +90 -0
- package/dist/openapi.js.map +1 -0
- package/dist/operation.d.ts +14 -0
- package/dist/operation.d.ts.map +1 -0
- package/dist/operation.js +130 -0
- package/dist/operation.js.map +1 -0
- package/dist/refs.d.ts +4 -0
- package/dist/refs.d.ts.map +1 -0
- package/dist/refs.js +42 -0
- package/dist/refs.js.map +1 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -0
- package/skills/ts-pf-openapi/SKILL.md +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# @ts-pf/openapi
|
|
2
|
+
|
|
3
|
+
Project a `@ts-pf/docs` `catalog()` into an **OpenAPI 3.1** document that describes the existing POST JSON RPC.
|
|
4
|
+
|
|
5
|
+
Agent skill: [`skills/ts-pf-openapi/`](skills/ts-pf-openapi/). Sync with `npx skills experimental_sync -y`.
|
|
6
|
+
|
|
7
|
+
The catalog is the portable spec. This package is the **polyglot** export. For a TypeScript client in another repo, use [`@ts-pf/codegen`](../codegen) (`emit` → `createClient<Contract>`), not OpenAPI-TS.
|
|
8
|
+
|
|
9
|
+
This package does **not** serve HTTP, embed Scalar/Swagger, invent REST paths, or change `FetchHandler`. Path params, GET/PUT, and flattened bodies are out.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { catalog, docs } from '@ts-pf/docs'
|
|
13
|
+
import { openapi } from '@ts-pf/openapi'
|
|
14
|
+
|
|
15
|
+
procedure.meta(docs({ description: 'Find a planet by id' }))
|
|
16
|
+
|
|
17
|
+
const spec = openapi(catalog(contract, { prefix: '/rpc' }), {
|
|
18
|
+
info: { title: 'Planet API', version: '1.0.0' },
|
|
19
|
+
servers: [{ url: 'https://api.example.com' }],
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Every path is `POST {prefix}/{router keys}` with body `{ "input": … }` and `{ "ok": true, "output": … }` / `{ "ok": false, "error": { "code", "message", "data?" } }`. The protocol header comes from `catalog.protocol`.
|
|
24
|
+
|
|
25
|
+
Serve the JSON in userland, **outside** the RPC prefix (`GET /rpc/...` is not a spec):
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
if (url.pathname === '/openapi.json') {
|
|
29
|
+
return Response.json(spec)
|
|
30
|
+
}
|
|
31
|
+
const result = await handler.handle(req, { prefix: '/rpc', context })
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Point Scalar or Swagger UI at that URL. Do not put those UIs in this package.
|
|
35
|
+
|
|
36
|
+
`openapi()` is a dumb projection of the catalog. Filter/hide procedures with `catalog(..., { filter })`. Mutate the returned object for `security`, extra `components`, or operation overrides.
|
|
37
|
+
|
|
38
|
+
Stream procedures advertise `application/jsonl` (item schema from the catalog). Pass `sse: true` to also list `text/event-stream`. Pass `multipart: true` to also list `multipart/form-data` on unary requests. Default is JSON (and JSONL for streams).
|
|
39
|
+
|
|
40
|
+
`$ref` values inside converted JSON Schema (`#/$defs/…`) are rewritten to the component they live under. `$anchor` / `$dynamicAnchor` across schemas are not rewritten.
|
|
41
|
+
|
|
42
|
+
Message transports (WebSocket, stdio, MessagePort) are not HTTP and are not in this document.
|
|
43
|
+
|
|
44
|
+
Typed split-repo clients: [`@ts-pf/codegen`](../codegen).
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CatalogSchema, JsonSchema } from '@ts-pf/docs';
|
|
2
|
+
export declare function innerFromCatalog(schema: CatalogSchema | undefined): JsonSchema | undefined;
|
|
3
|
+
export declare function requestEnvelope(input?: JsonSchema): JsonSchema;
|
|
4
|
+
export declare function successEnvelope(output?: JsonSchema): JsonSchema;
|
|
5
|
+
export declare function failureEnvelope(code: string, data?: JsonSchema): JsonSchema;
|
|
6
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE5D,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,GAAG,SAAS,GAChC,UAAU,GAAG,SAAS,CAcxB;AAED,wBAAgB,eAAe,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,UAAU,CAkB9D;AAED,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,UAAU,CAe/D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAwB3E"}
|
package/dist/envelope.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export function innerFromCatalog(schema) {
|
|
2
|
+
if (schema === undefined) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
if (schema.kind === 'json') {
|
|
6
|
+
return schema.jsonSchema;
|
|
7
|
+
}
|
|
8
|
+
if (schema.kind === 'unavailable') {
|
|
9
|
+
return { description: `Schema unavailable: ${schema.reason}` };
|
|
10
|
+
}
|
|
11
|
+
if (schema.kind === 'stream') {
|
|
12
|
+
return innerFromCatalog(schema.item) ?? {};
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
export function requestEnvelope(input) {
|
|
17
|
+
if (input === undefined) {
|
|
18
|
+
return {
|
|
19
|
+
type: 'object',
|
|
20
|
+
additionalProperties: false,
|
|
21
|
+
properties: {
|
|
22
|
+
input: { type: 'null' },
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
type: 'object',
|
|
28
|
+
additionalProperties: false,
|
|
29
|
+
required: ['input'],
|
|
30
|
+
properties: {
|
|
31
|
+
input,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function successEnvelope(output) {
|
|
36
|
+
const properties = {
|
|
37
|
+
ok: { const: true },
|
|
38
|
+
};
|
|
39
|
+
const required = ['ok'];
|
|
40
|
+
if (output !== undefined) {
|
|
41
|
+
properties.output = output;
|
|
42
|
+
required.push('output');
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
type: 'object',
|
|
46
|
+
additionalProperties: false,
|
|
47
|
+
required,
|
|
48
|
+
properties,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function failureEnvelope(code, data) {
|
|
52
|
+
const properties = {
|
|
53
|
+
code: { const: code },
|
|
54
|
+
message: { type: 'string' },
|
|
55
|
+
};
|
|
56
|
+
const required = ['code', 'message'];
|
|
57
|
+
if (data !== undefined) {
|
|
58
|
+
properties.data = data;
|
|
59
|
+
required.push('data');
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
type: 'object',
|
|
63
|
+
additionalProperties: false,
|
|
64
|
+
required: ['ok', 'error'],
|
|
65
|
+
properties: {
|
|
66
|
+
ok: { const: false },
|
|
67
|
+
error: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
additionalProperties: false,
|
|
70
|
+
required,
|
|
71
|
+
properties,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAC9B,MAAiC;IAEjC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,UAAU,CAAA;IAC1B,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,OAAO,EAAE,WAAW,EAAE,uBAAuB,MAAM,CAAC,MAAM,EAAE,EAAE,CAAA;IAChE,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IAC5C,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,oBAAoB,EAAE,KAAK;YAC3B,UAAU,EAAE;gBACV,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;aACxB;SACF,CAAA;IACH,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,oBAAoB,EAAE,KAAK;QAC3B,QAAQ,EAAE,CAAC,OAAO,CAAC;QACnB,UAAU,EAAE;YACV,KAAK;SACN;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAmB;IACjD,MAAM,UAAU,GAA4B;QAC1C,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;KACpB,CAAA;IACD,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,CAAA;IACvB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,UAAU,CAAC,MAAM,GAAG,MAAM,CAAA;QAC1B,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACzB,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,oBAAoB,EAAE,KAAK;QAC3B,QAAQ;QACR,UAAU;KACX,CAAA;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,IAAiB;IAC7D,MAAM,UAAU,GAA4B;QAC1C,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACrB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC5B,CAAA;IACD,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACpC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,UAAU,CAAC,IAAI,GAAG,IAAI,CAAA;QACtB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACvB,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,oBAAoB,EAAE,KAAK;QAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC;QACzB,UAAU,EAAE;YACV,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;YACpB,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,oBAAoB,EAAE,KAAK;gBAC3B,QAAQ;gBACR,UAAU;aACX;SACF;KACF,CAAA;AACH,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CatalogProcedure, JsonSchema, ProcedureCatalog } from '@ts-pf/docs';
|
|
2
|
+
export declare function protocolErrorRefs(catalog: ProcedureCatalog, schemas: Record<string, JsonSchema>): Map<string, JsonSchema>;
|
|
3
|
+
export declare function operationErrorResponses(proc: CatalogProcedure, options: {
|
|
4
|
+
includeProtocolErrors: boolean;
|
|
5
|
+
catalog: ProcedureCatalog;
|
|
6
|
+
schemas: Record<string, JsonSchema>;
|
|
7
|
+
protocolRefs: Map<string, JsonSchema>;
|
|
8
|
+
}): Record<string, {
|
|
9
|
+
description: string;
|
|
10
|
+
content: {
|
|
11
|
+
'application/json': {
|
|
12
|
+
schema: JsonSchema;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
}>;
|
|
16
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,gBAAgB,EACjB,MAAM,aAAa,CAAA;AAapB,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAClC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAWzB;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE;IACP,qBAAqB,EAAE,OAAO,CAAA;IAC9B,OAAO,EAAE,gBAAgB,CAAA;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACnC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACtC,GACA,MAAM,CACP,MAAM,EACN;IACE,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE;QAAE,kBAAkB,EAAE;YAAE,MAAM,EAAE,UAAU,CAAA;SAAE,CAAA;KAAE,CAAA;CACxD,CACF,CA4EA"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { failureEnvelope, innerFromCatalog } from './envelope.js';
|
|
2
|
+
import { protocolErrorSchemaName, schemaName } from './names.js';
|
|
3
|
+
import { putSchema } from './refs.js';
|
|
4
|
+
const SKIP_PROTOCOL = new Set(['METHOD_NOT_ALLOWED', 'NOT_FOUND']);
|
|
5
|
+
export function protocolErrorRefs(catalog, schemas) {
|
|
6
|
+
const refs = new Map();
|
|
7
|
+
for (const err of catalog.protocolErrors) {
|
|
8
|
+
const name = protocolErrorSchemaName(err.code);
|
|
9
|
+
const data = innerFromCatalog(err.data);
|
|
10
|
+
refs.set(err.code, putSchema(schemas, name, failureEnvelope(err.code, data)));
|
|
11
|
+
}
|
|
12
|
+
return refs;
|
|
13
|
+
}
|
|
14
|
+
export function operationErrorResponses(proc, options) {
|
|
15
|
+
const groups = new Map();
|
|
16
|
+
const add = (status, entry) => {
|
|
17
|
+
const list = groups.get(status) ?? [];
|
|
18
|
+
if (list.some((existing) => existing.code === entry.code)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
list.push(entry);
|
|
22
|
+
groups.set(status, list);
|
|
23
|
+
};
|
|
24
|
+
for (const err of proc.errors) {
|
|
25
|
+
const status = err.status ?? 400;
|
|
26
|
+
const name = schemaName(proc.path, `Error.${err.code}`);
|
|
27
|
+
const data = innerFromCatalog(err.data);
|
|
28
|
+
add(status, {
|
|
29
|
+
code: err.code,
|
|
30
|
+
description: err.message ?? err.code,
|
|
31
|
+
schema: putSchema(options.schemas, name, failureEnvelope(err.code, data)),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (options.includeProtocolErrors) {
|
|
35
|
+
for (const err of options.catalog.protocolErrors) {
|
|
36
|
+
if (SKIP_PROTOCOL.has(err.code)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (err.code === 'VALIDATION' && proc.input === undefined) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const schema = options.protocolRefs.get(err.code);
|
|
43
|
+
if (schema === undefined) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
add(err.status, {
|
|
47
|
+
code: err.code,
|
|
48
|
+
description: err.code,
|
|
49
|
+
schema,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const responses = {};
|
|
54
|
+
const statuses = [...groups.keys()].sort((a, b) => a - b);
|
|
55
|
+
for (const status of statuses) {
|
|
56
|
+
const entries = groups.get(status);
|
|
57
|
+
if (entries === undefined || entries.length === 0) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const first = entries[0];
|
|
61
|
+
if (first === undefined) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const schema = entries.length === 1
|
|
65
|
+
? first.schema
|
|
66
|
+
: { oneOf: entries.map((entry) => entry.schema) };
|
|
67
|
+
const description = entries.length === 1
|
|
68
|
+
? first.description
|
|
69
|
+
: entries.map((entry) => entry.code).join(', ');
|
|
70
|
+
responses[String(status)] = {
|
|
71
|
+
description,
|
|
72
|
+
content: {
|
|
73
|
+
'application/json': { schema },
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return responses;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/names.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function sanitizeSegment(value: string): string;
|
|
2
|
+
export declare function operationId(path: readonly string[]): string;
|
|
3
|
+
export declare function schemaName(path: readonly string[], suffix: string): string;
|
|
4
|
+
export declare function protocolErrorSchemaName(code: string): string;
|
|
5
|
+
export declare function operationPath(href: string | undefined, key: string): string;
|
|
6
|
+
//# sourceMappingURL=names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"AAEA,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAK3D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAG1E;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG3E"}
|
package/dist/names.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const UNSAFE = /[^A-Za-z0-9._-]/g;
|
|
2
|
+
export function sanitizeSegment(value) {
|
|
3
|
+
const next = value.replace(UNSAFE, '_');
|
|
4
|
+
return next.length > 0 ? next : '_';
|
|
5
|
+
}
|
|
6
|
+
export function operationId(path) {
|
|
7
|
+
if (path.length === 0) {
|
|
8
|
+
return '_';
|
|
9
|
+
}
|
|
10
|
+
return path.map(sanitizeSegment).join('.');
|
|
11
|
+
}
|
|
12
|
+
export function schemaName(path, suffix) {
|
|
13
|
+
const safeSuffix = suffix.split('.').map(sanitizeSegment).join('.');
|
|
14
|
+
return `${operationId(path)}.${safeSuffix}`;
|
|
15
|
+
}
|
|
16
|
+
export function protocolErrorSchemaName(code) {
|
|
17
|
+
return `TsPf.Error.${sanitizeSegment(code)}`;
|
|
18
|
+
}
|
|
19
|
+
export function operationPath(href, key) {
|
|
20
|
+
const raw = href ?? (key === '' ? '/' : `/${key}`);
|
|
21
|
+
return raw.startsWith('/') ? raw : `/${raw}`;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"names.js","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,kBAAkB,CAAA;AAEjC,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACvC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAuB;IACjD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC5C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAuB,EAAE,MAAc;IAChE,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACnE,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,UAAU,EAAE,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO,cAAc,eAAe,CAAC,IAAI,CAAC,EAAE,CAAA;AAC9C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAwB,EAAE,GAAW;IACjE,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAA;IAClD,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAA;AAC9C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAG/D,OAAO,KAAK,EACV,eAAe,EAEf,cAAc,EAEf,MAAM,YAAY,CAAA;AAKnB,wBAAgB,OAAO,CACrB,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,cAAc,GACtB,eAAe,CAqEjB"}
|
package/dist/openapi.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { protocolErrorRefs } from './errors.js';
|
|
2
|
+
import { toPathEntry } from './operation.js';
|
|
3
|
+
const JSON_SCHEMA_DIALECT = 'https://json-schema.org/draft/2020-12/schema';
|
|
4
|
+
export function openapi(catalog, options) {
|
|
5
|
+
if (catalog.catalogVersion !== 1) {
|
|
6
|
+
throw new Error(`Unsupported catalogVersion: ${String(catalog.catalogVersion)}`);
|
|
7
|
+
}
|
|
8
|
+
if (!Array.isArray(catalog.procedures)) {
|
|
9
|
+
throw new Error('catalog.procedures must be an array');
|
|
10
|
+
}
|
|
11
|
+
if (options.info.title.trim() === '' || options.info.version.trim() === '') {
|
|
12
|
+
throw new Error('info.title and info.version are required');
|
|
13
|
+
}
|
|
14
|
+
const schemas = {};
|
|
15
|
+
const includeProtocolErrors = options.protocolErrors !== false;
|
|
16
|
+
const protocolRefs = includeProtocolErrors
|
|
17
|
+
? protocolErrorRefs(catalog, schemas)
|
|
18
|
+
: new Map();
|
|
19
|
+
const paths = {};
|
|
20
|
+
const tags = [];
|
|
21
|
+
const seenTags = new Set();
|
|
22
|
+
for (const proc of catalog.procedures) {
|
|
23
|
+
const entry = toPathEntry(proc, {
|
|
24
|
+
catalog,
|
|
25
|
+
openapi: options,
|
|
26
|
+
schemas,
|
|
27
|
+
protocolRefs,
|
|
28
|
+
includeProtocolErrors,
|
|
29
|
+
});
|
|
30
|
+
paths[entry.path] = entry.item;
|
|
31
|
+
if (entry.tags !== undefined) {
|
|
32
|
+
for (const tag of entry.tags) {
|
|
33
|
+
if (!seenTags.has(tag)) {
|
|
34
|
+
seenTags.add(tag);
|
|
35
|
+
tags.push(tag);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const document = {
|
|
41
|
+
openapi: '3.1.0',
|
|
42
|
+
info: copyInfo(options.info),
|
|
43
|
+
jsonSchemaDialect: JSON_SCHEMA_DIALECT,
|
|
44
|
+
paths,
|
|
45
|
+
components: {
|
|
46
|
+
schemas,
|
|
47
|
+
parameters: {
|
|
48
|
+
TsPfProtocol: {
|
|
49
|
+
name: catalog.protocol.header.name,
|
|
50
|
+
in: 'header',
|
|
51
|
+
required: true,
|
|
52
|
+
schema: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
const: catalog.protocol.header.value,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
if (options.servers !== undefined) {
|
|
61
|
+
document.servers = options.servers;
|
|
62
|
+
}
|
|
63
|
+
if (tags.length > 0) {
|
|
64
|
+
document.tags = tags.map((name) => ({ name }));
|
|
65
|
+
}
|
|
66
|
+
return document;
|
|
67
|
+
}
|
|
68
|
+
function copyInfo(info) {
|
|
69
|
+
const out = {
|
|
70
|
+
title: info.title,
|
|
71
|
+
version: info.version,
|
|
72
|
+
};
|
|
73
|
+
if (info.summary !== undefined) {
|
|
74
|
+
out.summary = info.summary;
|
|
75
|
+
}
|
|
76
|
+
if (info.description !== undefined) {
|
|
77
|
+
out.description = info.description;
|
|
78
|
+
}
|
|
79
|
+
if (info.termsOfService !== undefined) {
|
|
80
|
+
out.termsOfService = info.termsOfService;
|
|
81
|
+
}
|
|
82
|
+
if (info.contact !== undefined) {
|
|
83
|
+
out.contact = info.contact;
|
|
84
|
+
}
|
|
85
|
+
if (info.license !== undefined) {
|
|
86
|
+
out.license = info.license;
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=openapi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CatalogProcedure, JsonSchema, ProcedureCatalog } from '@ts-pf/docs';
|
|
2
|
+
import type { OpenAPIOptions, PathItem } from './types.js';
|
|
3
|
+
export declare function toPathEntry(proc: CatalogProcedure, options: {
|
|
4
|
+
catalog: ProcedureCatalog;
|
|
5
|
+
openapi: OpenAPIOptions;
|
|
6
|
+
schemas: Record<string, JsonSchema>;
|
|
7
|
+
protocolRefs: Map<string, JsonSchema>;
|
|
8
|
+
includeProtocolErrors: boolean;
|
|
9
|
+
}): {
|
|
10
|
+
path: string;
|
|
11
|
+
item: PathItem;
|
|
12
|
+
tags?: string[];
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=operation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation.d.ts","sourceRoot":"","sources":["../src/operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAEhB,UAAU,EACV,gBAAgB,EACjB,MAAM,aAAa,CAAA;AAapB,OAAO,KAAK,EAEV,cAAc,EAGd,QAAQ,EACT,MAAM,YAAY,CAAA;AAEnB,wBAAgB,WAAW,CACzB,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE;IACP,OAAO,EAAE,gBAAgB,CAAA;IACzB,OAAO,EAAE,cAAc,CAAA;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACnC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACrC,qBAAqB,EAAE,OAAO,CAAA;CAC/B,GACA;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAWnD"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { innerFromCatalog, requestEnvelope, successEnvelope, } from './envelope.js';
|
|
2
|
+
import { operationErrorResponses } from './errors.js';
|
|
3
|
+
import { operationId as makeOperationId, operationPath, schemaName, } from './names.js';
|
|
4
|
+
import { putSchema } from './refs.js';
|
|
5
|
+
export function toPathEntry(proc, options) {
|
|
6
|
+
const operation = toOperation(proc, options);
|
|
7
|
+
const item = { post: operation };
|
|
8
|
+
const result = {
|
|
9
|
+
path: operationPath(proc.href, proc.key),
|
|
10
|
+
item,
|
|
11
|
+
};
|
|
12
|
+
if (operation.tags !== undefined) {
|
|
13
|
+
result.tags = operation.tags;
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
function toOperation(proc, options) {
|
|
18
|
+
const requestName = schemaName(proc.path, 'Request');
|
|
19
|
+
const successName = schemaName(proc.path, 'Success');
|
|
20
|
+
const requestInner = innerFromCatalog(proc.input);
|
|
21
|
+
const successInner = innerFromCatalog(proc.output);
|
|
22
|
+
const requestSchema = putSchema(options.schemas, requestName, requestEnvelope(hasInput(proc) ? requestInner : undefined));
|
|
23
|
+
const successSchema = putSchema(options.schemas, successName, successEnvelope(hasOutput(proc) ? successInner : undefined));
|
|
24
|
+
const operation = {
|
|
25
|
+
operationId: makeOperationId(proc.path),
|
|
26
|
+
parameters: [{ $ref: '#/components/parameters/TsPfProtocol' }],
|
|
27
|
+
requestBody: requestBody(proc, requestSchema, options.openapi),
|
|
28
|
+
responses: {
|
|
29
|
+
'200': successResponse(proc, successSchema, options),
|
|
30
|
+
...operationErrorResponses(proc, options),
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
if (proc.docs?.summary !== undefined) {
|
|
34
|
+
operation.summary = proc.docs.summary;
|
|
35
|
+
}
|
|
36
|
+
if (proc.docs?.description !== undefined) {
|
|
37
|
+
operation.description = proc.docs.description;
|
|
38
|
+
}
|
|
39
|
+
if (proc.docs?.deprecated === true) {
|
|
40
|
+
operation.deprecated = true;
|
|
41
|
+
}
|
|
42
|
+
const tags = tagsFor(proc);
|
|
43
|
+
if (tags !== undefined) {
|
|
44
|
+
operation.tags = tags;
|
|
45
|
+
}
|
|
46
|
+
return operation;
|
|
47
|
+
}
|
|
48
|
+
function requestBody(proc, requestSchema, options) {
|
|
49
|
+
const content = {};
|
|
50
|
+
if (isStream(proc.input)) {
|
|
51
|
+
content['application/jsonl'] = { schema: requestSchema };
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
content['application/json'] = { schema: requestSchema };
|
|
55
|
+
if (options.multipart === true) {
|
|
56
|
+
content['multipart/form-data'] = {
|
|
57
|
+
schema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
required: ['rpc'],
|
|
60
|
+
properties: { rpc: requestSchema },
|
|
61
|
+
additionalProperties: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
contentMediaType: 'application/octet-stream',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const body = { content };
|
|
70
|
+
if (hasInput(proc)) {
|
|
71
|
+
body.required = true;
|
|
72
|
+
}
|
|
73
|
+
if (options.multipart === true && !isStream(proc.input)) {
|
|
74
|
+
body.description =
|
|
75
|
+
'JSON RPC envelope, or multipart with JSON part `rpc` plus numbered file parts (see ts-pf PROTOCOL.md).';
|
|
76
|
+
}
|
|
77
|
+
return body;
|
|
78
|
+
}
|
|
79
|
+
function successResponse(proc, successSchema, options) {
|
|
80
|
+
const headerName = options.catalog.protocol.header.name;
|
|
81
|
+
const headerValue = options.catalog.protocol.header.value;
|
|
82
|
+
const headers = {
|
|
83
|
+
[headerName]: {
|
|
84
|
+
required: true,
|
|
85
|
+
schema: { type: 'string', const: headerValue },
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
if (isStream(proc.output)) {
|
|
89
|
+
const content = {
|
|
90
|
+
'application/jsonl': { schema: successSchema },
|
|
91
|
+
};
|
|
92
|
+
const response = {
|
|
93
|
+
description: 'RPC success stream. HTTP status stays 200 after the stream starts; in-band `{ ok: false, error }` lines are failures; EOF ends the stream.',
|
|
94
|
+
headers,
|
|
95
|
+
content,
|
|
96
|
+
};
|
|
97
|
+
if (options.openapi.sse === true) {
|
|
98
|
+
content['text/event-stream'] = { schema: successSchema };
|
|
99
|
+
response.description = `${response.description} SSE framing uses event: message / error / close; data: is the same JSONL line.`;
|
|
100
|
+
}
|
|
101
|
+
return response;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
description: 'RPC success',
|
|
105
|
+
headers,
|
|
106
|
+
content: {
|
|
107
|
+
'application/json': { schema: successSchema },
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function tagsFor(proc) {
|
|
112
|
+
if (proc.docs?.tags !== undefined && proc.docs.tags.length > 0) {
|
|
113
|
+
return [...proc.docs.tags];
|
|
114
|
+
}
|
|
115
|
+
const first = proc.path[0];
|
|
116
|
+
if (proc.path.length > 1 && first !== undefined) {
|
|
117
|
+
return [first];
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
function isStream(schema) {
|
|
122
|
+
return schema?.kind === 'stream';
|
|
123
|
+
}
|
|
124
|
+
function hasInput(proc) {
|
|
125
|
+
return proc.input !== undefined;
|
|
126
|
+
}
|
|
127
|
+
function hasOutput(proc) {
|
|
128
|
+
return proc.output !== undefined;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=operation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation.js","sourceRoot":"","sources":["../src/operation.ts"],"names":[],"mappings":"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"}
|
package/dist/refs.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { JsonSchema } from '@ts-pf/docs';
|
|
2
|
+
export declare function relocateRefs(schema: JsonSchema, componentName: string): JsonSchema;
|
|
3
|
+
export declare function putSchema(schemas: Record<string, JsonSchema>, name: string, schema: JsonSchema): JsonSchema;
|
|
4
|
+
//# sourceMappingURL=refs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../src/refs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,wBAAgB,YAAY,CAC1B,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,MAAM,GACpB,UAAU,CAGZ;AAsCD,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,EACnC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,GACjB,UAAU,CAGZ"}
|
package/dist/refs.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function relocateRefs(schema, componentName) {
|
|
2
|
+
const prefix = `#/components/schemas/${componentName}`;
|
|
3
|
+
return walk(schema, prefix);
|
|
4
|
+
}
|
|
5
|
+
function walk(value, prefix) {
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
return value.map((entry) => walk(entry, prefix));
|
|
8
|
+
}
|
|
9
|
+
if (value === null || typeof value !== 'object') {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
const obj = value;
|
|
13
|
+
const out = {};
|
|
14
|
+
for (const [key, child] of Object.entries(obj)) {
|
|
15
|
+
if ((key === '$ref' || key === '$dynamicRef') &&
|
|
16
|
+
typeof child === 'string' &&
|
|
17
|
+
child.startsWith('#')) {
|
|
18
|
+
out[key] = rewriteRef(child, prefix);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
out[key] = walk(child, prefix);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
function rewriteRef(ref, prefix) {
|
|
27
|
+
if (ref.startsWith('#/components/')) {
|
|
28
|
+
return ref;
|
|
29
|
+
}
|
|
30
|
+
if (ref === '#') {
|
|
31
|
+
return prefix;
|
|
32
|
+
}
|
|
33
|
+
if (ref.startsWith('#/')) {
|
|
34
|
+
return `${prefix}${ref.slice(1)}`;
|
|
35
|
+
}
|
|
36
|
+
return ref;
|
|
37
|
+
}
|
|
38
|
+
export function putSchema(schemas, name, schema) {
|
|
39
|
+
schemas[name] = relocateRefs(schema, name);
|
|
40
|
+
return { $ref: `#/components/schemas/${name}` };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=refs.js.map
|
package/dist/refs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refs.js","sourceRoot":"","sources":["../src/refs.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAC1B,MAAkB,EAClB,aAAqB;IAErB,MAAM,MAAM,GAAG,wBAAwB,aAAa,EAAE,CAAA;IACtD,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,CAAe,CAAA;AAC3C,CAAC;AAED,SAAS,IAAI,CAAC,KAAc,EAAE,MAAc;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IAClD,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,GAAG,GAAG,KAAgC,CAAA;IAC5C,MAAM,GAAG,GAA4B,EAAE,CAAA;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,IACE,CAAC,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,aAAa,CAAC;YACzC,OAAO,KAAK,KAAK,QAAQ;YACzB,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EACrB,CAAC;YACD,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QACtC,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QAChC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,UAAU,CAAC,GAAW,EAAE,MAAc;IAC7C,IAAI,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACpC,OAAO,GAAG,CAAA;IACZ,CAAC;IACD,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;QAChB,OAAO,MAAM,CAAA;IACf,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;IACnC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,OAAmC,EACnC,IAAY,EACZ,MAAkB;IAElB,OAAO,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC1C,OAAO,EAAE,IAAI,EAAE,wBAAwB,IAAI,EAAE,EAAE,CAAA;AACjD,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { JsonSchema } from '@ts-pf/docs';
|
|
2
|
+
export type OpenAPIInfo = {
|
|
3
|
+
title: string;
|
|
4
|
+
version: string;
|
|
5
|
+
summary?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
termsOfService?: string;
|
|
8
|
+
contact?: {
|
|
9
|
+
name?: string;
|
|
10
|
+
url?: string;
|
|
11
|
+
email?: string;
|
|
12
|
+
};
|
|
13
|
+
license?: {
|
|
14
|
+
name: string;
|
|
15
|
+
url?: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export type OpenAPIServer = {
|
|
19
|
+
url: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
};
|
|
22
|
+
export type OpenAPIOptions = {
|
|
23
|
+
info: OpenAPIInfo;
|
|
24
|
+
servers?: ReadonlyArray<OpenAPIServer>;
|
|
25
|
+
/** Default true. Attach protocol errors that can occur on a matched POST. */
|
|
26
|
+
protocolErrors?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* For `kind: 'stream'` outputs, also advertise `text/event-stream`.
|
|
29
|
+
* Default false — catalog does not know the codec.
|
|
30
|
+
*/
|
|
31
|
+
sse?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Also advertise `multipart/form-data` request bodies (approximate).
|
|
34
|
+
* Default false — files are a codec, not in the catalog.
|
|
35
|
+
*/
|
|
36
|
+
multipart?: boolean;
|
|
37
|
+
};
|
|
38
|
+
export type OpenAPIMediaType = {
|
|
39
|
+
schema?: JsonSchema;
|
|
40
|
+
};
|
|
41
|
+
export type OpenAPIRequestBody = {
|
|
42
|
+
required?: boolean;
|
|
43
|
+
description?: string;
|
|
44
|
+
content: Record<string, OpenAPIMediaType>;
|
|
45
|
+
};
|
|
46
|
+
export type OpenAPIResponseHeader = {
|
|
47
|
+
schema?: JsonSchema;
|
|
48
|
+
required?: boolean;
|
|
49
|
+
description?: string;
|
|
50
|
+
};
|
|
51
|
+
export type OpenAPIResponse = {
|
|
52
|
+
description: string;
|
|
53
|
+
headers?: Record<string, OpenAPIResponseHeader>;
|
|
54
|
+
content?: Record<string, OpenAPIMediaType>;
|
|
55
|
+
};
|
|
56
|
+
export type OpenAPIParameter = {
|
|
57
|
+
name: string;
|
|
58
|
+
in: 'header' | 'query' | 'path' | 'cookie';
|
|
59
|
+
required?: boolean;
|
|
60
|
+
schema?: JsonSchema;
|
|
61
|
+
};
|
|
62
|
+
export type OpenAPIRef = {
|
|
63
|
+
$ref: string;
|
|
64
|
+
};
|
|
65
|
+
export type OpenAPIOperation = {
|
|
66
|
+
operationId?: string;
|
|
67
|
+
summary?: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
tags?: string[];
|
|
70
|
+
deprecated?: boolean;
|
|
71
|
+
parameters?: Array<OpenAPIParameter | OpenAPIRef>;
|
|
72
|
+
requestBody?: OpenAPIRequestBody;
|
|
73
|
+
responses: Record<string, OpenAPIResponse>;
|
|
74
|
+
};
|
|
75
|
+
export type PathItem = {
|
|
76
|
+
post?: OpenAPIOperation;
|
|
77
|
+
};
|
|
78
|
+
export type OpenAPIDocument = {
|
|
79
|
+
openapi: '3.1.0';
|
|
80
|
+
info: OpenAPIInfo;
|
|
81
|
+
jsonSchemaDialect?: string;
|
|
82
|
+
servers?: ReadonlyArray<OpenAPIServer>;
|
|
83
|
+
tags?: Array<{
|
|
84
|
+
name: string;
|
|
85
|
+
}>;
|
|
86
|
+
paths: Record<string, PathItem>;
|
|
87
|
+
components: {
|
|
88
|
+
schemas: Record<string, JsonSchema>;
|
|
89
|
+
parameters?: Record<string, OpenAPIParameter>;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ts-pf/openapi",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"description": "Opt-in OpenAPI 3.1 document from catalog() (POST JSON RPC)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/crobinson42/ts-pf.git",
|
|
9
|
+
"directory": "packages/openapi"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/crobinson42/ts-pf/tree/main/packages/openapi#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/crobinson42/ts-pf/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./src/index.ts",
|
|
20
|
+
"import": "./src/index.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"skills"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc -p tsconfig.build.json",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"type-check": "tsc --noEmit",
|
|
40
|
+
"lint": "biome check src tests"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@ts-pf/docs": "0.1.0-beta.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@ts-pf/contract": "0.1.0-beta.0",
|
|
47
|
+
"@ts-pf/stream": "0.1.0-beta.0",
|
|
48
|
+
"typebox": "^1.3.24",
|
|
49
|
+
"zod": "^4.0.0"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ts-pf-openapi
|
|
3
|
+
description: Use when projecting a ts-pf catalog() to OpenAPI 3.1 with openapi(). Triggers: @ts-pf/openapi, openapi(), OpenAPI 3.1, POST JSON RPC spec.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @ts-pf/openapi
|
|
7
|
+
|
|
8
|
+
OpenAPI 3.1 projection of `catalog()`. POST JSON RPC only. Not a runtime.
|
|
9
|
+
|
|
10
|
+
Install: `npm i @ts-pf/openapi@beta`
|
|
11
|
+
|
|
12
|
+
Link for agents: `npx skills experimental_sync -y`
|
|
13
|
+
|
|
14
|
+
## Do
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { catalog, docs } from '@ts-pf/docs'
|
|
18
|
+
import { openapi } from '@ts-pf/openapi'
|
|
19
|
+
|
|
20
|
+
procedure.meta(docs({ description: 'Find a planet by id' }))
|
|
21
|
+
|
|
22
|
+
const spec = openapi(catalog(contract, { prefix: '/rpc' }), {
|
|
23
|
+
info: { title: 'Planet API', version: '1.0.0' },
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Every path is `POST {prefix}/{router keys}`. Serve JSON in userland (`GET /openapi.json`), not from `FetchHandler`. Point Scalar/Swagger at that URL — do not embed those UIs here.
|
|
28
|
+
|
|
29
|
+
Typed split-repo clients: `ts-pf-codegen`, not OpenAPI-TS.
|
|
30
|
+
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
- `openapi`
|
|
34
|
+
- `OpenAPIOptions` (`sse: true` / `multipart: true` advertise those content types), `OpenAPIDocument`
|
|
35
|
+
|
|
36
|
+
## Pair with
|
|
37
|
+
|
|
38
|
+
- Catalog: `ts-pf-docs`
|
|
39
|
+
- Typed client `.d.ts`: `ts-pf-codegen`
|
|
40
|
+
|
|
41
|
+
## Don't
|
|
42
|
+
|
|
43
|
+
- GET/PUT/path params or flattened REST bodies.
|
|
44
|
+
- Serve the spec from `FetchHandler`.
|
|
45
|
+
- Message transports (not HTTP) in this document.
|