@scalar/workspace-store 0.63.0 → 0.65.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/CHANGELOG.md +110 -0
- package/README.md +123 -0
- package/dist/client.d.ts +28 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +320 -144
- package/dist/entities/auth/schema.d.ts +236 -0
- package/dist/entities/auth/schema.d.ts.map +1 -1
- package/dist/entities/auth/schema.js +3 -1
- package/dist/helpers/chunk-index.d.ts +113 -0
- package/dist/helpers/chunk-index.d.ts.map +1 -0
- package/dist/helpers/chunk-index.js +152 -0
- package/dist/helpers/external-examples.d.ts +19 -0
- package/dist/helpers/external-examples.d.ts.map +1 -0
- package/dist/helpers/external-examples.js +51 -0
- package/dist/helpers/for-each-path-item-operation.js +4 -4
- package/dist/helpers/get-example-value.d.ts +15 -0
- package/dist/helpers/get-example-value.d.ts.map +1 -0
- package/dist/helpers/get-example-value.js +28 -0
- package/dist/helpers/get-resolved-ref.d.ts.map +1 -1
- package/dist/helpers/get-resolved-ref.js +12 -1
- package/dist/helpers/normalize-boolean-schemas.d.ts +10 -0
- package/dist/helpers/normalize-boolean-schemas.d.ts.map +1 -0
- package/dist/helpers/normalize-boolean-schemas.js +193 -0
- package/dist/helpers/operation-examples.d.ts +6 -0
- package/dist/helpers/operation-examples.d.ts.map +1 -0
- package/dist/helpers/operation-examples.js +48 -0
- package/dist/helpers/serialize-stream-example.d.ts +8 -0
- package/dist/helpers/serialize-stream-example.d.ts.map +1 -0
- package/dist/helpers/serialize-stream-example.js +52 -0
- package/dist/helpers/use-external-examples.d.ts +15 -0
- package/dist/helpers/use-external-examples.d.ts.map +1 -0
- package/dist/helpers/use-external-examples.js +62 -0
- package/dist/mutators/index.d.ts +3 -3
- package/dist/mutators/operation/body.d.ts.map +1 -1
- package/dist/mutators/operation/body.js +9 -2
- package/dist/mutators/operation/parameters.d.ts.map +1 -1
- package/dist/mutators/operation/parameters.js +28 -9
- package/dist/plugins/bundler/index.d.ts +6 -2
- package/dist/plugins/bundler/index.d.ts.map +1 -1
- package/dist/plugins/bundler/index.js +20 -4
- package/dist/plugins/bundler/openapi-document.d.ts +6 -0
- package/dist/plugins/bundler/openapi-document.d.ts.map +1 -0
- package/dist/plugins/bundler/openapi-document.js +57 -0
- package/dist/request-example/builder/body/build-multipart.d.ts +29 -0
- package/dist/request-example/builder/body/build-multipart.d.ts.map +1 -0
- package/dist/request-example/builder/body/build-multipart.js +191 -0
- package/dist/request-example/builder/body/build-request-body.d.ts +6 -1
- package/dist/request-example/builder/body/build-request-body.d.ts.map +1 -1
- package/dist/request-example/builder/body/build-request-body.js +80 -23
- package/dist/request-example/builder/body/encode-multipart-body.d.ts +8 -8
- package/dist/request-example/builder/body/encode-multipart-body.d.ts.map +1 -1
- package/dist/request-example/builder/body/encode-multipart-body.js +56 -18
- package/dist/request-example/builder/body/get-request-body-example.d.ts.map +1 -1
- package/dist/request-example/builder/body/get-request-body-example.js +20 -6
- package/dist/request-example/builder/body/multipart-limits.d.ts +3 -0
- package/dist/request-example/builder/body/multipart-limits.d.ts.map +1 -0
- package/dist/request-example/builder/body/multipart-limits.js +2 -0
- package/dist/request-example/builder/body/serialize-form-property.d.ts +2 -0
- package/dist/request-example/builder/body/serialize-form-property.d.ts.map +1 -1
- package/dist/request-example/builder/body/serialize-form-property.js +3 -2
- package/dist/request-example/builder/body/serialize-multipart-array.d.ts +1 -1
- package/dist/request-example/builder/build-request.js +5 -0
- package/dist/request-example/builder/header/build-request-parameters.d.ts.map +1 -1
- package/dist/request-example/builder/header/build-request-parameters.js +5 -1
- package/dist/request-example/builder/header/is-param-disabled.d.ts +2 -2
- package/dist/request-example/builder/header/is-param-disabled.d.ts.map +1 -1
- package/dist/request-example/builder/header/is-param-disabled.js +5 -4
- package/dist/request-example/builder/header/serialize-parameter.d.ts +11 -0
- package/dist/request-example/builder/header/serialize-parameter.d.ts.map +1 -1
- package/dist/request-example/builder/header/serialize-parameter.js +22 -0
- package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
- package/dist/request-example/builder/helpers/get-example-from-schema.js +42 -3
- package/dist/request-example/builder/helpers/get-example.d.ts +2 -0
- package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
- package/dist/request-example/builder/helpers/get-example.js +14 -9
- package/dist/request-example/builder/index.d.ts +2 -2
- package/dist/request-example/builder/index.d.ts.map +1 -1
- package/dist/request-example/builder/index.js +1 -1
- package/dist/request-example/builder/security/secret-types.d.ts +4 -1
- package/dist/request-example/builder/security/secret-types.d.ts.map +1 -1
- package/dist/request-example/context/headers.d.ts.map +1 -1
- package/dist/request-example/context/headers.js +3 -4
- package/dist/request-example/context/security/extract-security-scheme-secrets.d.ts.map +1 -1
- package/dist/request-example/context/security/extract-security-scheme-secrets.js +15 -0
- package/dist/request-example/index.d.ts +5 -2
- package/dist/request-example/index.d.ts.map +1 -1
- package/dist/request-example/index.js +4 -1
- package/dist/request-example/xml/serialize-xml-part.d.ts +9 -0
- package/dist/request-example/xml/serialize-xml-part.d.ts.map +1 -0
- package/dist/request-example/xml/serialize-xml-part.js +12 -0
- package/dist/schemas/extensions.d.ts +9 -0
- package/dist/schemas/extensions.d.ts.map +1 -1
- package/dist/schemas/extensions.js +9 -0
- package/dist/schemas/reference-config/index.d.ts +1 -0
- package/dist/schemas/reference-config/index.d.ts.map +1 -1
- package/dist/schemas/reference-config/settings.d.ts +1 -0
- package/dist/schemas/reference-config/settings.d.ts.map +1 -1
- package/dist/schemas/v3.1/strict/oauthflows.d.ts +26 -0
- package/dist/schemas/v3.1/strict/oauthflows.d.ts.map +1 -1
- package/dist/schemas/v3.1/strict/oauthflows.js +3 -0
- package/dist/schemas/v3.1/strict/openapi-document.d.ts +812 -7
- package/dist/schemas/v3.1/strict/openapi-document.d.ts.map +1 -1
- package/dist/schemas/v3.2/openapi/index.d.ts +5 -0
- package/dist/schemas/v3.2/openapi/index.d.ts.map +1 -1
- package/dist/schemas/v3.2/openapi/index.js +46 -12
- package/dist/schemas/v3.2/strict/openapi-document.d.ts +37 -0
- package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
- package/dist/schemas/v3.2/strict/openapi-document.js +8 -0
- package/dist/server.d.ts +21 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +60 -34
- package/package.json +19 -9
- package/dist/schemas/v3.1/openapi/index.d.ts +0 -149
- package/dist/schemas/v3.1/openapi/index.d.ts.map +0 -1
- package/dist/schemas/v3.1/openapi/index.js +0 -732
- package/dist/schemas/v3.1/openapi/reference.d.ts +0 -4
- package/dist/schemas/v3.1/openapi/reference.d.ts.map +0 -1
- package/dist/schemas/v3.1/openapi/reference.js +0 -29
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { isHttpMethod } from '@scalar/helpers/http/is-http-method';
|
|
2
|
+
import { isObject } from '@scalar/helpers/object/is-object';
|
|
3
|
+
import { escapeJsonPointer } from '@scalar/json-magic/helpers/escape-json-pointer';
|
|
4
|
+
import { encodeChunkName } from '../helpers/encode-chunk-name.js';
|
|
5
|
+
/**
|
|
6
|
+
* The extension key a compact sparse document carries its chunk index under.
|
|
7
|
+
*
|
|
8
|
+
* It exists on the wire only: the client expands the index back into per-node references while the
|
|
9
|
+
* document is ingested and removes the key, so nothing downstream ever sees it.
|
|
10
|
+
*/
|
|
11
|
+
export const CHUNK_INDEX_KEY = 'x-scalar-chunk-index';
|
|
12
|
+
/** Stands in for an externalized operation on a compact path item. */
|
|
13
|
+
const OPERATION_PLACEHOLDER = 0;
|
|
14
|
+
/** Matches a `{slot}` in a template, or a doubled brace standing for a literal one. */
|
|
15
|
+
const TEMPLATE_SLOT = /\{\{|\}\}|\{(\w+)\}/g;
|
|
16
|
+
/**
|
|
17
|
+
* Fills the `{slot}`s of a chunk-reference template.
|
|
18
|
+
*
|
|
19
|
+
* `{{` and `}}` stand for literal braces, so text the writer inlined into a template — a document
|
|
20
|
+
* name, a base URL — may contain braces without being read back as a slot. An unknown slot is left
|
|
21
|
+
* alone rather than blanked, so a template from a newer writer fails visibly instead of resolving
|
|
22
|
+
* to the wrong chunk.
|
|
23
|
+
*/
|
|
24
|
+
export const fillChunkRef = (template, values = {}) => template.replace(TEMPLATE_SLOT, (match, slot) => slot === undefined ? match.charAt(0) : (values[slot] ?? match));
|
|
25
|
+
/** Escapes text inlined into a template verbatim, so its braces are not read back as slots. */
|
|
26
|
+
const escapeTemplateLiteral = (text) => text.replaceAll('{', '{{').replaceAll('}', '}}');
|
|
27
|
+
const identity = (value) => value;
|
|
28
|
+
/**
|
|
29
|
+
* How each template slot is spelled, per mode.
|
|
30
|
+
*
|
|
31
|
+
* Static references name files, so their variable segments go through `encodeChunkName` — the same
|
|
32
|
+
* call `generateWorkspaceChunks` makes when it writes those files. SSR references name pointers
|
|
33
|
+
* into the store's in-memory assets, which `get()` looks up with JSON Pointer segments, so only the
|
|
34
|
+
* path is escaped. A method is never encoded in either mode: it is written as it was read, and
|
|
35
|
+
* `isHttpMethod` keeps it to letters.
|
|
36
|
+
*
|
|
37
|
+
* Both sides read the table from here. A reader that encoded a segment differently would build a
|
|
38
|
+
* reference to a chunk that does not exist.
|
|
39
|
+
*/
|
|
40
|
+
const SLOT_ENCODERS = {
|
|
41
|
+
static: { type: encodeChunkName, name: encodeChunkName, path: encodeChunkName, method: identity },
|
|
42
|
+
ssr: { type: identity, name: identity, path: escapeJsonPointer, method: identity },
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Builds the reference templates for one document.
|
|
46
|
+
*
|
|
47
|
+
* Filled, these produce exactly the references `externalizeComponentReferences` and
|
|
48
|
+
* `externalizePathReferences` write, so a client that expands the index lands on the same chunk.
|
|
49
|
+
*/
|
|
50
|
+
export const chunkRefTemplates = (meta) => {
|
|
51
|
+
if (meta.mode === 'ssr') {
|
|
52
|
+
const base = `${escapeTemplateLiteral(meta.baseUrl)}/${escapeTemplateLiteral(meta.name)}`;
|
|
53
|
+
return {
|
|
54
|
+
components: `${base}/components/{type}/{name}#`,
|
|
55
|
+
operations: `${base}/operations/{path}/{method}#`,
|
|
56
|
+
navigation: `${base}/navigation#`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const base = `./chunks/${escapeTemplateLiteral(encodeChunkName(meta.name))}`;
|
|
60
|
+
return {
|
|
61
|
+
components: `${base}/components/{type}/{name}.json#`,
|
|
62
|
+
operations: `${base}/operations/{path}/{method}.json#`,
|
|
63
|
+
navigation: `${base}/navigation.json#`,
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
/** The reference a lazily loaded chunk is reached through. */
|
|
67
|
+
export const chunkReference = (ref) => ({ '$ref': ref, $global: true });
|
|
68
|
+
/**
|
|
69
|
+
* The navigation a compact document carries inline: the document entry without its children.
|
|
70
|
+
*
|
|
71
|
+
* Navigation is store metadata rather than part of the description, and every reader takes it by
|
|
72
|
+
* plain property access — `name` keys the auth and history stores, `title` and `icon` render the
|
|
73
|
+
* document header — so it is never a reference in any mode. Only the children are externalized,
|
|
74
|
+
* since they are what makes a navigation tree large. They start as an empty array, so a reader
|
|
75
|
+
* iterating them before they are loaded sees an empty sidebar rather than an error.
|
|
76
|
+
*/
|
|
77
|
+
export const navigationHeader = ({ children: _children, ...header }) => ({
|
|
78
|
+
...header,
|
|
79
|
+
children: [],
|
|
80
|
+
});
|
|
81
|
+
/**
|
|
82
|
+
* Compacts a sparse document's `components` and `paths` into an index.
|
|
83
|
+
*
|
|
84
|
+
* Takes the sections the externalizers produced rather than the document itself, so the index is
|
|
85
|
+
* built from the very references it replaces and the two cannot come to describe different sets of
|
|
86
|
+
* chunks.
|
|
87
|
+
*/
|
|
88
|
+
export const buildChunkIndex = ({ mode, refs, components, paths, }) => ({
|
|
89
|
+
mode,
|
|
90
|
+
refs: { components: refs.components, operations: refs.operations },
|
|
91
|
+
components: Object.fromEntries(Object.entries(components).map(([type, names]) => [type, Object.keys(names)])),
|
|
92
|
+
paths: Object.fromEntries(Object.entries(paths).map(([path, pathItem]) => [
|
|
93
|
+
path,
|
|
94
|
+
Object.fromEntries(Object.entries(pathItem).map(([key, value]) => [key, isHttpMethod(key) ? OPERATION_PLACEHOLDER : value])),
|
|
95
|
+
])),
|
|
96
|
+
});
|
|
97
|
+
/** Whether a value is shaped like an index this build knows how to expand. */
|
|
98
|
+
const isChunkIndex = (value) => isObject(value) &&
|
|
99
|
+
(value['mode'] === 'static' || value['mode'] === 'ssr') &&
|
|
100
|
+
isObject(value['refs']) &&
|
|
101
|
+
isObject(value['components']) &&
|
|
102
|
+
isObject(value['paths']);
|
|
103
|
+
/**
|
|
104
|
+
* Expands a compact sparse document into the one a non-compact server store would have sent.
|
|
105
|
+
*
|
|
106
|
+
* Mutates the document in place and drops the index key, so what the rest of the store sees is an
|
|
107
|
+
* ordinary sparse document: `resolve()`, the bundler and anything enumerating `paths` or
|
|
108
|
+
* `components` are looking at the shape they always have.
|
|
109
|
+
*
|
|
110
|
+
* A document without an index is left alone, which is every document a non-compact server store or
|
|
111
|
+
* an author produces.
|
|
112
|
+
*
|
|
113
|
+
* @param document - The document to expand, mutated in place
|
|
114
|
+
* @returns Whether an index was found and expanded
|
|
115
|
+
*/
|
|
116
|
+
export const expandChunkIndex = (document) => {
|
|
117
|
+
if (!isObject(document) || document[CHUNK_INDEX_KEY] === undefined) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const index = document[CHUNK_INDEX_KEY];
|
|
121
|
+
if (!isChunkIndex(index)) {
|
|
122
|
+
// The key is dropped rather than passed on: it is not part of any document a consumer should
|
|
123
|
+
// see, and the sections it stands for are missing either way, so there is nothing a partial
|
|
124
|
+
// expansion could recover.
|
|
125
|
+
delete document[CHUNK_INDEX_KEY];
|
|
126
|
+
console.warn(`Ignoring an unreadable "${CHUNK_INDEX_KEY}"; this document's chunks cannot be resolved.`);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const encoders = SLOT_ENCODERS[index.mode];
|
|
130
|
+
// Define own properties so document keys such as `__proto__` remain data, without invoking
|
|
131
|
+
// inherited setters or changing the prototype of any expanded object.
|
|
132
|
+
const components = Object.fromEntries(Object.entries(index.components).map(([type, names]) => [
|
|
133
|
+
type,
|
|
134
|
+
Object.fromEntries(names.map((name) => [
|
|
135
|
+
name,
|
|
136
|
+
chunkReference(fillChunkRef(index.refs.components, { type: encoders.type(type), name: encoders.name(name) })),
|
|
137
|
+
])),
|
|
138
|
+
]));
|
|
139
|
+
const paths = Object.fromEntries(Object.entries(index.paths).map(([path, pathItem]) => [
|
|
140
|
+
path,
|
|
141
|
+
Object.fromEntries(Object.entries(pathItem).map(([key, value]) => [
|
|
142
|
+
key,
|
|
143
|
+
isHttpMethod(key)
|
|
144
|
+
? chunkReference(fillChunkRef(index.refs.operations, { path: encoders.path(path), method: encoders.method(key) }))
|
|
145
|
+
: value,
|
|
146
|
+
])),
|
|
147
|
+
]));
|
|
148
|
+
document['components'] = components;
|
|
149
|
+
document['paths'] = paths;
|
|
150
|
+
delete document[CHUNK_INDEX_KEY];
|
|
151
|
+
return true;
|
|
152
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type LoaderPlugin } from '@scalar/json-magic/bundle';
|
|
2
|
+
import { fetchUrls } from '@scalar/json-magic/bundle/plugins/browser';
|
|
3
|
+
import type { ExampleObject } from '../schemas/v3.2/strict/openapi-document.js';
|
|
4
|
+
/** Download state lives outside the document so reading an example never creates an edit. */
|
|
5
|
+
export type ExternalExampleState = {
|
|
6
|
+
status: 'idle' | 'loading' | 'loaded' | 'error';
|
|
7
|
+
value?: unknown;
|
|
8
|
+
/** Preserve wire text when a 3.2 example also supplies structured data. */
|
|
9
|
+
serializedValue?: string;
|
|
10
|
+
load: () => Promise<void>;
|
|
11
|
+
};
|
|
12
|
+
/** A resolver belongs to one document revision and its configured transport. */
|
|
13
|
+
export type ExternalExampleResolver = (example: ExampleObject) => ExternalExampleState;
|
|
14
|
+
/** Cache external payloads and in-flight requests without changing authored examples. */
|
|
15
|
+
export declare const createExternalExampleResolver: (options?: Parameters<typeof fetchUrls>[0] & {
|
|
16
|
+
origin?: string;
|
|
17
|
+
fileLoader?: LoaderPlugin;
|
|
18
|
+
}) => ExternalExampleResolver;
|
|
19
|
+
//# sourceMappingURL=external-examples.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-examples.d.ts","sourceRoot":"","sources":["../../src/helpers/external-examples.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAwB,MAAM,2BAA2B,CAAA;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAA;AAGrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAE3E,6FAA6F;AAC7F,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B,CAAA;AAED,gFAAgF;AAChF,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,EAAE,aAAa,KAAK,oBAAoB,CAAA;AAEtF,yFAAyF;AACzF,eAAO,MAAM,6BAA6B,GACxC,UAAS,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,YAAY,CAAA;CAAO,KAC7F,uBAwCF,CAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { resolveReferencePath } from '@scalar/json-magic/bundle';
|
|
2
|
+
import { fetchUrls } from '@scalar/json-magic/bundle/plugins/browser';
|
|
3
|
+
import { shallowReactive } from 'vue';
|
|
4
|
+
/** Cache external payloads and in-flight requests without changing authored examples. */
|
|
5
|
+
export const createExternalExampleResolver = (options = {}) => {
|
|
6
|
+
const loader = fetchUrls({ ...options, limit: 10 });
|
|
7
|
+
const cache = new Map();
|
|
8
|
+
return (example) => {
|
|
9
|
+
if (example.value !== undefined || !example.externalValue) {
|
|
10
|
+
return { status: 'loaded', value: example.value, load: () => Promise.resolve() };
|
|
11
|
+
}
|
|
12
|
+
const url = resolveReferencePath(options.origin ?? '', example.externalValue) ?? example.externalValue;
|
|
13
|
+
const cached = cache.get(url);
|
|
14
|
+
if (cached)
|
|
15
|
+
return cached;
|
|
16
|
+
let pending;
|
|
17
|
+
const state = shallowReactive({
|
|
18
|
+
status: 'idle',
|
|
19
|
+
load: () => {
|
|
20
|
+
if (pending)
|
|
21
|
+
return pending;
|
|
22
|
+
if (state.status === 'loaded')
|
|
23
|
+
return Promise.resolve();
|
|
24
|
+
state.status = 'loading';
|
|
25
|
+
pending = (async () => {
|
|
26
|
+
try {
|
|
27
|
+
const selectedLoader = [loader, options.fileLoader].find((candidate) => candidate?.validate(url));
|
|
28
|
+
const result = await selectedLoader?.exec(url);
|
|
29
|
+
if (result?.ok) {
|
|
30
|
+
state.serializedValue = typeof result.raw === 'string' ? result.raw : undefined;
|
|
31
|
+
state.value = result.data === undefined ? result.raw : result.data;
|
|
32
|
+
state.status = 'loaded';
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
state.status = 'error';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
state.status = 'error';
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
pending = undefined;
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
45
|
+
return pending;
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
cache.set(url, state);
|
|
49
|
+
return state;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -11,10 +11,10 @@ const MAX_REF_HOPS = 10;
|
|
|
11
11
|
/**
|
|
12
12
|
* Whether a merged path item still carries an unfollowed hop.
|
|
13
13
|
*
|
|
14
|
-
* `mergeSiblingReferences`
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* `mergeSiblingReferences` preserves the resolved target's `$ref-value`, including non-enumerable
|
|
15
|
+
* links in plain documents. That link signals that one more hop is waiting. A fully resolved path
|
|
16
|
+
* item never has one: the `$ref` sibling is kept (it is what the author wrote) but nothing resolves
|
|
17
|
+
* through it any more.
|
|
18
18
|
*/
|
|
19
19
|
const hasUnfollowedRef = (pathItem) => isObjectLike(pathItem) && Object.hasOwn(pathItem, '$ref-value');
|
|
20
20
|
/**
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ExampleObject } from '../schemas/v3.2/strict/example.js';
|
|
2
|
+
import type { ReferenceType } from '../schemas/v3.2/strict/reference.js';
|
|
3
|
+
/** Preserve the source so consumers do not serialize wire text a second time. */
|
|
4
|
+
export type ExampleValue = {
|
|
5
|
+
source: 'serialized';
|
|
6
|
+
value: string;
|
|
7
|
+
} | {
|
|
8
|
+
source: 'data' | 'value';
|
|
9
|
+
value: unknown;
|
|
10
|
+
};
|
|
11
|
+
/** Select an explicit example without confusing false, zero, null, or empty text with absence. */
|
|
12
|
+
export declare const getExampleValue: (input: ReferenceType<ExampleObject> | undefined) => ExampleValue | undefined;
|
|
13
|
+
/** Preserve explicit wire text for any media type, or serialize structured JSON data. */
|
|
14
|
+
export declare const getExplicitExampleText: (example: ExampleValue | undefined, contentType: string, indent?: number) => string | undefined;
|
|
15
|
+
//# sourceMappingURL=get-example-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-example-value.d.ts","sourceRoot":"","sources":["../../src/helpers/get-example-value.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAEpE,iFAAiF;AACjF,MAAM,MAAM,YAAY,GAAG;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAA;AAEjH,kGAAkG;AAClG,eAAO,MAAM,eAAe,GAAI,OAAO,aAAa,CAAC,aAAa,CAAC,GAAG,SAAS,KAAG,YAAY,GAAG,SAahG,CAAA;AAED,yFAAyF;AACzF,eAAO,MAAM,sBAAsB,GACjC,SAAS,YAAY,GAAG,SAAS,EACjC,aAAa,MAAM,EACnB,SAAS,MAAM,KACd,MAAM,GAAG,SASX,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { parseMimeType } from '@scalar/helpers/http/mime-type';
|
|
2
|
+
import { getResolvedRef } from '../helpers/get-resolved-ref.js';
|
|
3
|
+
/** Select an explicit example without confusing false, zero, null, or empty text with absence. */
|
|
4
|
+
export const getExampleValue = (input) => {
|
|
5
|
+
const example = getResolvedRef(input);
|
|
6
|
+
if (example?.serializedValue !== undefined) {
|
|
7
|
+
return { source: 'serialized', value: example.serializedValue };
|
|
8
|
+
}
|
|
9
|
+
if (example?.dataValue !== undefined) {
|
|
10
|
+
return { source: 'data', value: example.dataValue };
|
|
11
|
+
}
|
|
12
|
+
if (example?.value !== undefined) {
|
|
13
|
+
return { source: 'value', value: example.value };
|
|
14
|
+
}
|
|
15
|
+
// externalValue is fetched by the bundler; until then it is not an inline payload.
|
|
16
|
+
return undefined;
|
|
17
|
+
};
|
|
18
|
+
/** Preserve explicit wire text for any media type, or serialize structured JSON data. */
|
|
19
|
+
export const getExplicitExampleText = (example, contentType, indent) => {
|
|
20
|
+
if (example?.source === 'serialized') {
|
|
21
|
+
return example.value;
|
|
22
|
+
}
|
|
23
|
+
const { essence, subtype } = parseMimeType(contentType);
|
|
24
|
+
if (example?.source === 'data' && (essence === 'application/json' || subtype.endsWith('+json'))) {
|
|
25
|
+
return JSON.stringify(example.value, null, indent);
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-resolved-ref.d.ts","sourceRoot":"","sources":["../../src/helpers/get-resolved-ref.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,IAAI,CAAA;CAAE,CAAA;AACjF,MAAM,MAAM,SAAS,CAAC,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AA0ElD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAG,
|
|
1
|
+
{"version":3,"file":"get-resolved-ref.d.ts","sourceRoot":"","sources":["../../src/helpers/get-resolved-ref.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,IAAI,CAAA;CAAE,CAAA;AACjF,MAAM,MAAM,SAAS,CAAC,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AA0ElD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAG,IA2BlE,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EACzC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,EACrB,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,MAAM,GACzC,IAAI,GAAG,MAAM,CAAA;AAChB,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,CAAA;AACtF,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,CAAA;AAW7E;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA"}
|
|
@@ -76,7 +76,18 @@ export const mergeSiblingReferences = (node) => {
|
|
|
76
76
|
if (!isObject(target)) {
|
|
77
77
|
return rest;
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
const merged = { ...target, ...rest };
|
|
80
|
+
// Plain document loaders hide reference links from serialization. Preserve the
|
|
81
|
+
// next hop explicitly so chain resolution does not depend on object spread.
|
|
82
|
+
if (Object.hasOwn(target, '$ref-value') && !Object.hasOwn(merged, '$ref-value')) {
|
|
83
|
+
Object.defineProperty(merged, '$ref-value', {
|
|
84
|
+
value: target['$ref-value'],
|
|
85
|
+
enumerable: false,
|
|
86
|
+
configurable: true,
|
|
87
|
+
writable: true,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return merged;
|
|
80
91
|
};
|
|
81
92
|
export function getResolvedRef(node, transform = defaultTransform) {
|
|
82
93
|
if (typeof node === 'object' && node !== null && '$ref' in node) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize boolean schemas without mutating caller-owned data.
|
|
3
|
+
* Only changed schema containers and their ancestors are copied. Unchanged bundled
|
|
4
|
+
* documents and opaque example/extension payloads retain their identity. Iterative
|
|
5
|
+
* discovery and copy propagation preserve cycles without recursive cloning.
|
|
6
|
+
* The marker represents an untyped schema; false is its negation. additionalProperties
|
|
7
|
+
* already accepts booleans and therefore retains its authored representation.
|
|
8
|
+
*/
|
|
9
|
+
export declare const normalizeBooleanSchemas: <T extends Record<string, unknown>>(document: T) => T;
|
|
10
|
+
//# sourceMappingURL=normalize-boolean-schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-boolean-schemas.d.ts","sourceRoot":"","sources":["../../src/helpers/normalize-boolean-schemas.ts"],"names":[],"mappings":"AAgDA;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,KAAG,CAkJxF,CAAA"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { parseJsonPointerSegments } from '@scalar/helpers/json/parse-json-pointer-segments';
|
|
2
|
+
import { isObject } from '@scalar/helpers/object/is-object';
|
|
3
|
+
import { isSchemaPath } from '@scalar/helpers/openapi/is-schema-path';
|
|
4
|
+
const schemaMaps = new Set(['properties', 'patternProperties', '$defs', 'definitions', 'dependentSchemas']);
|
|
5
|
+
const schemaArrays = new Set(['allOf', 'anyOf', 'oneOf', 'prefixItems']);
|
|
6
|
+
const childSchemas = new Set([
|
|
7
|
+
'items',
|
|
8
|
+
'not',
|
|
9
|
+
'if',
|
|
10
|
+
'then',
|
|
11
|
+
'else',
|
|
12
|
+
'contains',
|
|
13
|
+
'propertyNames',
|
|
14
|
+
'contentSchema',
|
|
15
|
+
'$ref-value',
|
|
16
|
+
]);
|
|
17
|
+
const openApiMaps = new Set([
|
|
18
|
+
'paths',
|
|
19
|
+
'webhooks',
|
|
20
|
+
'responses',
|
|
21
|
+
'content',
|
|
22
|
+
'headers',
|
|
23
|
+
'examples',
|
|
24
|
+
'links',
|
|
25
|
+
'encoding',
|
|
26
|
+
'variables',
|
|
27
|
+
'parameters',
|
|
28
|
+
'requestBodies',
|
|
29
|
+
'securitySchemes',
|
|
30
|
+
'pathItems',
|
|
31
|
+
'mediaTypes',
|
|
32
|
+
'additionalOperations',
|
|
33
|
+
'callbacks',
|
|
34
|
+
'x-ext',
|
|
35
|
+
]);
|
|
36
|
+
const opaqueValues = new Set(['example', 'examples', 'default', 'enum', 'const', 'value', 'dataValue']);
|
|
37
|
+
/**
|
|
38
|
+
* Normalize boolean schemas without mutating caller-owned data.
|
|
39
|
+
* Only changed schema containers and their ancestors are copied. Unchanged bundled
|
|
40
|
+
* documents and opaque example/extension payloads retain their identity. Iterative
|
|
41
|
+
* discovery and copy propagation preserve cycles without recursive cloning.
|
|
42
|
+
* The marker represents an untyped schema; false is its negation. additionalProperties
|
|
43
|
+
* already accepts booleans and therefore retains its authored representation.
|
|
44
|
+
*/
|
|
45
|
+
export const normalizeBooleanSchemas = (document) => {
|
|
46
|
+
const nodes = new WeakMap();
|
|
47
|
+
const changed = new Set();
|
|
48
|
+
const schemas = new WeakSet();
|
|
49
|
+
const documents = new WeakSet();
|
|
50
|
+
const tasks = [];
|
|
51
|
+
const getNode = (source) => {
|
|
52
|
+
const existing = nodes.get(source);
|
|
53
|
+
if (existing) {
|
|
54
|
+
return existing;
|
|
55
|
+
}
|
|
56
|
+
const node = { source };
|
|
57
|
+
nodes.set(source, node);
|
|
58
|
+
return node;
|
|
59
|
+
};
|
|
60
|
+
const root = getNode(document);
|
|
61
|
+
const link = (parent, key, child) => {
|
|
62
|
+
const node = getNode(child);
|
|
63
|
+
if (!node.parent) {
|
|
64
|
+
node.parent = { node: parent, key };
|
|
65
|
+
}
|
|
66
|
+
else if (node.parent.node !== parent || node.parent.key !== key) {
|
|
67
|
+
const parents = (node.otherParents ??= []);
|
|
68
|
+
if (!parents.some((edge) => edge.node === parent && edge.key === key)) {
|
|
69
|
+
parents.push({ node: parent, key });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return node;
|
|
73
|
+
};
|
|
74
|
+
const schema = (parent, key, value) => {
|
|
75
|
+
if (typeof value === 'boolean') {
|
|
76
|
+
;
|
|
77
|
+
(parent.replacements ??= new Map()).set(key, value ? { __scalar_: '' } : { __scalar_: '', not: { __scalar_: '' } });
|
|
78
|
+
changed.add(parent);
|
|
79
|
+
}
|
|
80
|
+
else if (isObject(value)) {
|
|
81
|
+
tasks.push({ node: link(parent, key, value), kind: 'schema' });
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const reference = (pointer) => {
|
|
85
|
+
const segments = parseJsonPointerSegments(pointer);
|
|
86
|
+
let parent = root;
|
|
87
|
+
for (const [index, key] of segments.entries()) {
|
|
88
|
+
// A local reference cannot reach inherited properties or prototype setters.
|
|
89
|
+
if (!Object.hasOwn(parent.source, key)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const value = Reflect.get(parent.source, key);
|
|
93
|
+
if (index === segments.length - 1) {
|
|
94
|
+
schema(parent, key, value);
|
|
95
|
+
}
|
|
96
|
+
else if (value !== null && typeof value === 'object') {
|
|
97
|
+
parent = link(parent, key, value);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
tasks.push({ node: root, kind: 'document', path: [], mapDepth: 0 });
|
|
105
|
+
while (tasks.length > 0) {
|
|
106
|
+
const task = tasks.pop();
|
|
107
|
+
if (!task) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
const { node } = task;
|
|
111
|
+
const visited = task.kind === 'schema' ? schemas : documents;
|
|
112
|
+
if (visited.has(node.source)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
visited.add(node.source);
|
|
116
|
+
if (task.kind === 'schema') {
|
|
117
|
+
const value = node.source;
|
|
118
|
+
if (typeof value.$ref === 'string' && value.$ref.startsWith('#/')) {
|
|
119
|
+
reference(value.$ref.slice(1));
|
|
120
|
+
}
|
|
121
|
+
for (const [key, child] of Object.entries(value)) {
|
|
122
|
+
if ((schemaMaps.has(key) && isObject(child)) || (schemaArrays.has(key) && Array.isArray(child))) {
|
|
123
|
+
const container = link(node, key, child);
|
|
124
|
+
for (const [name, nested] of Object.entries(child)) {
|
|
125
|
+
schema(container, name, nested);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (childSchemas.has(key)) {
|
|
129
|
+
schema(node, key, child);
|
|
130
|
+
}
|
|
131
|
+
else if (['additionalProperties', 'unevaluatedProperties', 'unevaluatedItems'].includes(key) &&
|
|
132
|
+
isObject(child)) {
|
|
133
|
+
schema(node, key, child);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const { path } = task;
|
|
139
|
+
for (const [key, child] of Object.entries(node.source)) {
|
|
140
|
+
const childPath = [...path, key];
|
|
141
|
+
const isMapEntry = task.mapDepth > 0;
|
|
142
|
+
if (key === 'schemas' && path.at(-1) === 'components' && isObject(child)) {
|
|
143
|
+
const container = link(node, key, child);
|
|
144
|
+
for (const [name, nested] of Object.entries(child)) {
|
|
145
|
+
schema(container, name, nested);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else if ((key === 'schema' || key === 'itemSchema') && !isMapEntry && isSchemaPath(childPath)) {
|
|
149
|
+
schema(node, key, child);
|
|
150
|
+
}
|
|
151
|
+
else if (isMapEntry || (!opaqueValues.has(key) && (!key.startsWith('x-') || key === 'x-ext'))) {
|
|
152
|
+
// Vendor extensions are opaque. x-ext additionally contains bundled documents
|
|
153
|
+
// and schema targets whose context can be supplied by a local reference.
|
|
154
|
+
if (child !== null && typeof child === 'object') {
|
|
155
|
+
tasks.push({
|
|
156
|
+
node: link(node, key, child),
|
|
157
|
+
kind: 'document',
|
|
158
|
+
path: childPath,
|
|
159
|
+
mapDepth: isMapEntry ? task.mapDepth - 1 : key === 'callbacks' ? 2 : openApiMaps.has(key) ? 1 : 0,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const forEachParent = (node, callback) => {
|
|
166
|
+
if (node.parent) {
|
|
167
|
+
callback(node.parent);
|
|
168
|
+
}
|
|
169
|
+
node.otherParents?.forEach(callback);
|
|
170
|
+
};
|
|
171
|
+
// Set iteration also visits newly added ancestors, including shared/cyclic parents.
|
|
172
|
+
for (const node of changed) {
|
|
173
|
+
forEachParent(node, (parent) => changed.add(parent.node));
|
|
174
|
+
}
|
|
175
|
+
for (const node of changed) {
|
|
176
|
+
node.copy = Array.isArray(node.source) ? [] : {};
|
|
177
|
+
}
|
|
178
|
+
for (const node of changed) {
|
|
179
|
+
forEachParent(node, (parent) => (parent.node.replacements ??= new Map()).set(parent.key, node.copy));
|
|
180
|
+
}
|
|
181
|
+
for (const node of changed) {
|
|
182
|
+
for (const [key, value] of Object.entries(node.source)) {
|
|
183
|
+
// Define an own data property so authored keys such as __proto__ remain data.
|
|
184
|
+
Object.defineProperty(node.copy, key, {
|
|
185
|
+
value: node.replacements?.has(key) ? node.replacements.get(key) : value,
|
|
186
|
+
enumerable: true,
|
|
187
|
+
writable: true,
|
|
188
|
+
configurable: true,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return (root.copy ?? document);
|
|
193
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ExampleObject, OperationObject } from '../schemas/v3.2/strict/openapi-document.js';
|
|
2
|
+
/** The request body and parameter examples needed by a single request, never its responses. */
|
|
3
|
+
export declare const getOperationExamples: (operation: OperationObject, key: string, contentType?: string) => (ExampleObject | undefined)[];
|
|
4
|
+
/** Overlay fetched values for request consumers without persisting them as document edits. */
|
|
5
|
+
export declare const resolveOperationExamples: (operation: OperationObject, key: string, contentType: string | undefined, resolve: (example: ExampleObject | undefined) => ExampleObject | undefined) => OperationObject;
|
|
6
|
+
//# sourceMappingURL=operation-examples.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation-examples.d.ts","sourceRoot":"","sources":["../../src/helpers/operation-examples.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAmB,eAAe,EAAE,MAAM,wCAAwC,CAAA;AAE7G,+FAA+F;AAC/F,eAAO,MAAM,oBAAoB,GAC/B,WAAW,eAAe,EAC1B,KAAK,MAAM,EACX,cAAc,MAAM,KACnB,CAAC,aAAa,GAAG,SAAS,CAAC,EAS7B,CAAA;AAED,8FAA8F;AAC9F,eAAO,MAAM,wBAAwB,GACnC,WAAW,eAAe,EAC1B,KAAK,MAAM,EACX,aAAa,MAAM,GAAG,SAAS,EAC/B,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,KAAK,aAAa,GAAG,SAAS,KACzE,eAgCF,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getResolvedRef } from '../helpers/get-resolved-ref.js';
|
|
2
|
+
import { getExample } from '../request-example/builder/helpers/get-example.js';
|
|
3
|
+
/** The request body and parameter examples needed by a single request, never its responses. */
|
|
4
|
+
export const getOperationExamples = (operation, key, contentType) => {
|
|
5
|
+
const body = getResolvedRef(operation.requestBody);
|
|
6
|
+
return [
|
|
7
|
+
body ? getExample(body, key, contentType) : undefined,
|
|
8
|
+
...(operation.parameters ?? []).map((parameter) => {
|
|
9
|
+
const resolved = getResolvedRef(parameter);
|
|
10
|
+
return resolved ? getExample(resolved, key, undefined) : undefined;
|
|
11
|
+
}),
|
|
12
|
+
];
|
|
13
|
+
};
|
|
14
|
+
/** Overlay fetched values for request consumers without persisting them as document edits. */
|
|
15
|
+
export const resolveOperationExamples = (operation, key, contentType, resolve) => {
|
|
16
|
+
const mapMedia = (media) => {
|
|
17
|
+
const selected = key || Object.keys(media.examples ?? {})[0];
|
|
18
|
+
const example = selected ? getResolvedRef(media.examples?.[selected]) : undefined;
|
|
19
|
+
const resolved = resolve(example);
|
|
20
|
+
return selected && resolved && resolved !== example
|
|
21
|
+
? { ...media, examples: { ...media.examples, [selected]: resolved } }
|
|
22
|
+
: media;
|
|
23
|
+
};
|
|
24
|
+
const body = getResolvedRef(operation.requestBody);
|
|
25
|
+
const type = contentType ?? Object.keys(body?.content ?? {})[0];
|
|
26
|
+
return {
|
|
27
|
+
...operation,
|
|
28
|
+
...(body && type && body.content[type]
|
|
29
|
+
? {
|
|
30
|
+
requestBody: { ...body, content: { ...body.content, [type]: mapMedia(body.content[type]) } },
|
|
31
|
+
}
|
|
32
|
+
: {}),
|
|
33
|
+
parameters: operation.parameters?.map((parameter) => {
|
|
34
|
+
const resolved = getResolvedRef(parameter);
|
|
35
|
+
if (!resolved)
|
|
36
|
+
return parameter;
|
|
37
|
+
const content = 'content' in resolved ? resolved.content : undefined;
|
|
38
|
+
const mediaType = Object.keys(content ?? {})[0];
|
|
39
|
+
if (mediaType && content?.[mediaType]) {
|
|
40
|
+
const media = mapMedia(content[mediaType]);
|
|
41
|
+
return media === content[mediaType] ? parameter : { ...resolved, content: { ...content, [mediaType]: media } };
|
|
42
|
+
}
|
|
43
|
+
// Keep editable source objects when there is no downloaded example to overlay.
|
|
44
|
+
const media = mapMedia(resolved);
|
|
45
|
+
return media === resolved ? parameter : { ...resolved, ...media };
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame generated values and authored structured examples as sequential content.
|
|
3
|
+
* Callers bypass this helper for authored strings that already contain wire framing.
|
|
4
|
+
* SSE objects with no valid fields are omitted with one warning per call; an empty
|
|
5
|
+
* input sequence is valid and does not produce a warning.
|
|
6
|
+
*/
|
|
7
|
+
export declare const serializeStreamExample: (value: unknown, contentType: string, singleItem: boolean) => string | undefined;
|
|
8
|
+
//# sourceMappingURL=serialize-stream-example.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize-stream-example.d.ts","sourceRoot":"","sources":["../../src/helpers/serialize-stream-example.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GACjC,OAAO,OAAO,EACd,aAAa,MAAM,EACnB,YAAY,OAAO,KAClB,MAAM,GAAG,SA2CX,CAAA"}
|