@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.
Files changed (118) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/README.md +123 -0
  3. package/dist/client.d.ts +28 -0
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +320 -144
  6. package/dist/entities/auth/schema.d.ts +236 -0
  7. package/dist/entities/auth/schema.d.ts.map +1 -1
  8. package/dist/entities/auth/schema.js +3 -1
  9. package/dist/helpers/chunk-index.d.ts +113 -0
  10. package/dist/helpers/chunk-index.d.ts.map +1 -0
  11. package/dist/helpers/chunk-index.js +152 -0
  12. package/dist/helpers/external-examples.d.ts +19 -0
  13. package/dist/helpers/external-examples.d.ts.map +1 -0
  14. package/dist/helpers/external-examples.js +51 -0
  15. package/dist/helpers/for-each-path-item-operation.js +4 -4
  16. package/dist/helpers/get-example-value.d.ts +15 -0
  17. package/dist/helpers/get-example-value.d.ts.map +1 -0
  18. package/dist/helpers/get-example-value.js +28 -0
  19. package/dist/helpers/get-resolved-ref.d.ts.map +1 -1
  20. package/dist/helpers/get-resolved-ref.js +12 -1
  21. package/dist/helpers/normalize-boolean-schemas.d.ts +10 -0
  22. package/dist/helpers/normalize-boolean-schemas.d.ts.map +1 -0
  23. package/dist/helpers/normalize-boolean-schemas.js +193 -0
  24. package/dist/helpers/operation-examples.d.ts +6 -0
  25. package/dist/helpers/operation-examples.d.ts.map +1 -0
  26. package/dist/helpers/operation-examples.js +48 -0
  27. package/dist/helpers/serialize-stream-example.d.ts +8 -0
  28. package/dist/helpers/serialize-stream-example.d.ts.map +1 -0
  29. package/dist/helpers/serialize-stream-example.js +52 -0
  30. package/dist/helpers/use-external-examples.d.ts +15 -0
  31. package/dist/helpers/use-external-examples.d.ts.map +1 -0
  32. package/dist/helpers/use-external-examples.js +62 -0
  33. package/dist/mutators/index.d.ts +3 -3
  34. package/dist/mutators/operation/body.d.ts.map +1 -1
  35. package/dist/mutators/operation/body.js +9 -2
  36. package/dist/mutators/operation/parameters.d.ts.map +1 -1
  37. package/dist/mutators/operation/parameters.js +28 -9
  38. package/dist/plugins/bundler/index.d.ts +6 -2
  39. package/dist/plugins/bundler/index.d.ts.map +1 -1
  40. package/dist/plugins/bundler/index.js +20 -4
  41. package/dist/plugins/bundler/openapi-document.d.ts +6 -0
  42. package/dist/plugins/bundler/openapi-document.d.ts.map +1 -0
  43. package/dist/plugins/bundler/openapi-document.js +57 -0
  44. package/dist/request-example/builder/body/build-multipart.d.ts +29 -0
  45. package/dist/request-example/builder/body/build-multipart.d.ts.map +1 -0
  46. package/dist/request-example/builder/body/build-multipart.js +191 -0
  47. package/dist/request-example/builder/body/build-request-body.d.ts +6 -1
  48. package/dist/request-example/builder/body/build-request-body.d.ts.map +1 -1
  49. package/dist/request-example/builder/body/build-request-body.js +80 -23
  50. package/dist/request-example/builder/body/encode-multipart-body.d.ts +8 -8
  51. package/dist/request-example/builder/body/encode-multipart-body.d.ts.map +1 -1
  52. package/dist/request-example/builder/body/encode-multipart-body.js +56 -18
  53. package/dist/request-example/builder/body/get-request-body-example.d.ts.map +1 -1
  54. package/dist/request-example/builder/body/get-request-body-example.js +20 -6
  55. package/dist/request-example/builder/body/multipart-limits.d.ts +3 -0
  56. package/dist/request-example/builder/body/multipart-limits.d.ts.map +1 -0
  57. package/dist/request-example/builder/body/multipart-limits.js +2 -0
  58. package/dist/request-example/builder/body/serialize-form-property.d.ts +2 -0
  59. package/dist/request-example/builder/body/serialize-form-property.d.ts.map +1 -1
  60. package/dist/request-example/builder/body/serialize-form-property.js +3 -2
  61. package/dist/request-example/builder/body/serialize-multipart-array.d.ts +1 -1
  62. package/dist/request-example/builder/build-request.js +5 -0
  63. package/dist/request-example/builder/header/build-request-parameters.d.ts.map +1 -1
  64. package/dist/request-example/builder/header/build-request-parameters.js +5 -1
  65. package/dist/request-example/builder/header/is-param-disabled.d.ts +2 -2
  66. package/dist/request-example/builder/header/is-param-disabled.d.ts.map +1 -1
  67. package/dist/request-example/builder/header/is-param-disabled.js +5 -4
  68. package/dist/request-example/builder/header/serialize-parameter.d.ts +11 -0
  69. package/dist/request-example/builder/header/serialize-parameter.d.ts.map +1 -1
  70. package/dist/request-example/builder/header/serialize-parameter.js +22 -0
  71. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
  72. package/dist/request-example/builder/helpers/get-example-from-schema.js +42 -3
  73. package/dist/request-example/builder/helpers/get-example.d.ts +2 -0
  74. package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
  75. package/dist/request-example/builder/helpers/get-example.js +14 -9
  76. package/dist/request-example/builder/index.d.ts +2 -2
  77. package/dist/request-example/builder/index.d.ts.map +1 -1
  78. package/dist/request-example/builder/index.js +1 -1
  79. package/dist/request-example/builder/security/secret-types.d.ts +4 -1
  80. package/dist/request-example/builder/security/secret-types.d.ts.map +1 -1
  81. package/dist/request-example/context/headers.d.ts.map +1 -1
  82. package/dist/request-example/context/headers.js +3 -4
  83. package/dist/request-example/context/security/extract-security-scheme-secrets.d.ts.map +1 -1
  84. package/dist/request-example/context/security/extract-security-scheme-secrets.js +15 -0
  85. package/dist/request-example/index.d.ts +5 -2
  86. package/dist/request-example/index.d.ts.map +1 -1
  87. package/dist/request-example/index.js +4 -1
  88. package/dist/request-example/xml/serialize-xml-part.d.ts +9 -0
  89. package/dist/request-example/xml/serialize-xml-part.d.ts.map +1 -0
  90. package/dist/request-example/xml/serialize-xml-part.js +12 -0
  91. package/dist/schemas/extensions.d.ts +9 -0
  92. package/dist/schemas/extensions.d.ts.map +1 -1
  93. package/dist/schemas/extensions.js +9 -0
  94. package/dist/schemas/reference-config/index.d.ts +1 -0
  95. package/dist/schemas/reference-config/index.d.ts.map +1 -1
  96. package/dist/schemas/reference-config/settings.d.ts +1 -0
  97. package/dist/schemas/reference-config/settings.d.ts.map +1 -1
  98. package/dist/schemas/v3.1/strict/oauthflows.d.ts +26 -0
  99. package/dist/schemas/v3.1/strict/oauthflows.d.ts.map +1 -1
  100. package/dist/schemas/v3.1/strict/oauthflows.js +3 -0
  101. package/dist/schemas/v3.1/strict/openapi-document.d.ts +812 -7
  102. package/dist/schemas/v3.1/strict/openapi-document.d.ts.map +1 -1
  103. package/dist/schemas/v3.2/openapi/index.d.ts +5 -0
  104. package/dist/schemas/v3.2/openapi/index.d.ts.map +1 -1
  105. package/dist/schemas/v3.2/openapi/index.js +46 -12
  106. package/dist/schemas/v3.2/strict/openapi-document.d.ts +37 -0
  107. package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
  108. package/dist/schemas/v3.2/strict/openapi-document.js +8 -0
  109. package/dist/server.d.ts +21 -1
  110. package/dist/server.d.ts.map +1 -1
  111. package/dist/server.js +60 -34
  112. package/package.json +19 -9
  113. package/dist/schemas/v3.1/openapi/index.d.ts +0 -149
  114. package/dist/schemas/v3.1/openapi/index.d.ts.map +0 -1
  115. package/dist/schemas/v3.1/openapi/index.js +0 -732
  116. package/dist/schemas/v3.1/openapi/reference.d.ts +0 -4
  117. package/dist/schemas/v3.1/openapi/reference.d.ts.map +0 -1
  118. 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` spreads the resolved target over the siblings. When that target is itself
15
- * a reference, the spread carries its `$ref-value` across as a real key, which is the signal that
16
- * one more hop is waiting. A fully resolved path item never has one: the `$ref` sibling is kept (it
17
- * is what the author wrote) but nothing resolves through it any more.
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,IAclE,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"}
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
- return { ...target, ...rest };
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"}