@scalar/workspace-store 0.62.0 → 0.64.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 +36 -0
- package/README.md +88 -0
- package/dist/client.d.ts +28 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +217 -135
- package/dist/helpers/chunk-index.d.ts +95 -0
- package/dist/helpers/chunk-index.d.ts.map +1 -0
- package/dist/helpers/chunk-index.js +139 -0
- package/dist/helpers/detect-changes-proxy.d.ts +7 -6
- package/dist/helpers/detect-changes-proxy.d.ts.map +1 -1
- package/dist/helpers/detect-changes-proxy.js +83 -38
- package/dist/helpers/document-revision.d.ts +26 -0
- package/dist/helpers/document-revision.d.ts.map +1 -0
- package/dist/helpers/document-revision.js +47 -0
- package/dist/helpers/external-examples.d.ts +17 -0
- package/dist/helpers/external-examples.d.ts.map +1 -0
- package/dist/helpers/external-examples.js +50 -0
- package/dist/helpers/get-resolved-ref-deep.d.ts.map +1 -1
- package/dist/helpers/get-resolved-ref-deep.js +25 -13
- package/dist/helpers/get-resolved-ref.d.ts.map +1 -1
- package/dist/helpers/get-resolved-ref.js +61 -3
- 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 +45 -0
- package/dist/helpers/unpack-proxy.d.ts +10 -0
- package/dist/helpers/unpack-proxy.d.ts.map +1 -1
- package/dist/helpers/unpack-proxy.js +15 -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 +55 -0
- 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 +5 -2
- package/dist/plugins/bundler/index.d.ts.map +1 -1
- package/dist/plugins/bundler/index.js +12 -3
- 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/helpers/get-example-from-schema.d.ts.map +1 -1
- package/dist/request-example/builder/helpers/get-example-from-schema.js +21 -1
- 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 +9 -7
- package/dist/request-example/context/headers.d.ts.map +1 -1
- package/dist/request-example/context/headers.js +3 -4
- package/dist/resolve.d.ts +10 -2
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +9 -1
- package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
- package/dist/schemas/v3.2/strict/openapi-document.js +17 -1
- package/dist/server.d.ts +16 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +34 -9
- package/package.json +3 -3
package/dist/client.js
CHANGED
|
@@ -16,8 +16,11 @@ import { reactive } from 'vue';
|
|
|
16
16
|
import YAML from 'yaml';
|
|
17
17
|
import { createAuthStore } from './entities/auth/index.js';
|
|
18
18
|
import { createHistoryStore } from './entities/history/index.js';
|
|
19
|
+
import { expandChunkIndex } from './helpers/chunk-index.js';
|
|
19
20
|
import { deepClone } from './helpers/deep-clone.js';
|
|
20
21
|
import { createDetectChangesProxy } from './helpers/detect-changes-proxy.js';
|
|
22
|
+
import { bumpDocumentRevision } from './helpers/document-revision.js';
|
|
23
|
+
import { createExternalExampleResolver } from './helpers/external-examples.js';
|
|
21
24
|
import { safeAssign } from './helpers/general.js';
|
|
22
25
|
import { getFetch } from './helpers/get-fetch.js';
|
|
23
26
|
import { mergeObjects } from './helpers/merge-object.js';
|
|
@@ -155,7 +158,7 @@ const purgeInternalDocumentKeys = (input) => {
|
|
|
155
158
|
* @returns An object containing methods and getters for managing the workspace
|
|
156
159
|
*/
|
|
157
160
|
export const createWorkspaceStore = (workspaceProps) => {
|
|
158
|
-
const { verbose = false } = workspaceProps ?? {};
|
|
161
|
+
const { verbose = false, reactive: isReactiveWorkspace = true } = workspaceProps ?? {};
|
|
159
162
|
const withMeasurementSync = (name, fn) => (verbose ? measureSync(name, fn) : fn());
|
|
160
163
|
const withMeasurementAsync = (name, fn) => verbose ? measureAsync(name, fn) : fn();
|
|
161
164
|
/**
|
|
@@ -164,6 +167,11 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
164
167
|
* This can include settings that can not be persisted between sessions (not JSON serializable)
|
|
165
168
|
*/
|
|
166
169
|
const extraDocumentConfigurations = {};
|
|
170
|
+
const externalExampleResolvers = new WeakMap();
|
|
171
|
+
const fallbackExternalExamples = createExternalExampleResolver({
|
|
172
|
+
fetch: workspaceProps?.fetch,
|
|
173
|
+
fileLoader: workspaceProps?.fileLoader,
|
|
174
|
+
});
|
|
167
175
|
/**
|
|
168
176
|
* Notifies all workspace plugins of a workspace state change event.
|
|
169
177
|
*
|
|
@@ -176,20 +184,9 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
176
184
|
workspaceProps?.plugins?.forEach((plugin) => plugin.hooks?.onWorkspaceStateChanges?.(event));
|
|
177
185
|
};
|
|
178
186
|
/**
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* Every change to the workspace, is tracked and broadcast to all registered plugins.
|
|
182
|
-
* allowing for change tracking.
|
|
183
|
-
*
|
|
184
|
-
* NOTE:
|
|
185
|
-
* The detect changes proxy is applied separately beacause the vue reactitvity proxy have to be the outer most proxy.
|
|
186
|
-
* If the order is reversed, Vue cannot properly track mutations, leading to lost reactivity and bugs.
|
|
187
|
-
* By wrapping the contents with the detect changes proxy first, and then passing the result to Vue's `reactive`,
|
|
188
|
-
* we ensure that Vue manages its reactivity as expected and our change detection hooks
|
|
189
|
-
* are also triggered reliably.
|
|
190
|
-
* Do not reverse this order‼️
|
|
187
|
+
* The plain workspace state, before any observability wrappers are applied.
|
|
191
188
|
*/
|
|
192
|
-
const
|
|
189
|
+
const workspaceState = {
|
|
193
190
|
...workspaceProps?.meta,
|
|
194
191
|
documents: {},
|
|
195
192
|
/**
|
|
@@ -202,84 +199,105 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
202
199
|
get activeDocument() {
|
|
203
200
|
return workspace.documents[getActiveDocumentName()];
|
|
204
201
|
},
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* An object containing the reactive workspace state.
|
|
205
|
+
*
|
|
206
|
+
* Every change to the workspace, is tracked and broadcast to all registered plugins.
|
|
207
|
+
* allowing for change tracking.
|
|
208
|
+
*
|
|
209
|
+
* With `reactive: false` the state is used as-is, so reads cost nothing beyond the plain object and
|
|
210
|
+
* nothing observes a write. See the `reactive` option for the full list of what stops happening.
|
|
211
|
+
*
|
|
212
|
+
* NOTE:
|
|
213
|
+
* The detect changes proxy is applied separately beacause the vue reactitvity proxy have to be the outer most proxy.
|
|
214
|
+
* If the order is reversed, Vue cannot properly track mutations, leading to lost reactivity and bugs.
|
|
215
|
+
* By wrapping the contents with the detect changes proxy first, and then passing the result to Vue's `reactive`,
|
|
216
|
+
* we ensure that Vue manages its reactivity as expected and our change detection hooks
|
|
217
|
+
* are also triggered reliably.
|
|
218
|
+
* Do not reverse this order‼️
|
|
219
|
+
*/
|
|
220
|
+
const workspace = !isReactiveWorkspace
|
|
221
|
+
? workspaceState
|
|
222
|
+
: reactive(createDetectChangesProxy(workspaceState, {
|
|
223
|
+
hooks: {
|
|
224
|
+
onAfterChange(path) {
|
|
225
|
+
const type = path[0];
|
|
226
|
+
/** Document changes */
|
|
227
|
+
if (type === 'documents') {
|
|
228
|
+
// We are overriding the while documents object, ignore. This should not happen
|
|
229
|
+
if (path.length < 2) {
|
|
230
|
+
console.log('[WARN]: Overriding entire documents object is not supported');
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const documentName = path[1];
|
|
234
|
+
const document = workspace.documents[documentName] ?? {
|
|
235
|
+
openapi: '3.1.0',
|
|
236
|
+
info: { title: '', version: '' },
|
|
237
|
+
'x-scalar-original-document-hash': '',
|
|
238
|
+
};
|
|
239
|
+
// Every write through the store passes here, which is what makes the revision a
|
|
240
|
+
// complete record of the document changing.
|
|
241
|
+
bumpDocumentRevision(document);
|
|
242
|
+
const event = {
|
|
243
|
+
type: 'documents',
|
|
244
|
+
documentName,
|
|
245
|
+
value: unpackProxyObject(document),
|
|
246
|
+
path: path.slice(2),
|
|
247
|
+
};
|
|
248
|
+
// Don't mark as dirty when the document is first created or when
|
|
249
|
+
// only metadata-only fields change. `x-scalar-registry-meta` is
|
|
250
|
+
// updated programmatically (commit hash, conflict cache) and
|
|
251
|
+
// does not represent a user edit.
|
|
252
|
+
if (event.path.length > 0 && !METADATA_ONLY_DOCUMENT_KEYS.has(event.path[0])) {
|
|
253
|
+
// The document has been modified since it was last saved
|
|
254
|
+
document['x-scalar-is-dirty'] = true;
|
|
255
|
+
}
|
|
256
|
+
fireWorkspaceChange(event);
|
|
214
257
|
return;
|
|
215
258
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
|
|
259
|
+
/** Active document changes */
|
|
260
|
+
if (type === 'activeDocument') {
|
|
261
|
+
const documentName = getActiveDocumentName();
|
|
262
|
+
const document = workspace.documents[documentName] ?? {
|
|
263
|
+
openapi: '3.1.0',
|
|
264
|
+
info: { title: '', version: '' },
|
|
265
|
+
'x-scalar-original-document-hash': '',
|
|
266
|
+
};
|
|
267
|
+
bumpDocumentRevision(document);
|
|
268
|
+
// Active document changed
|
|
269
|
+
const event = {
|
|
270
|
+
type: 'documents',
|
|
271
|
+
documentName,
|
|
272
|
+
value: unpackProxyObject(document),
|
|
273
|
+
path: path.slice(2),
|
|
274
|
+
};
|
|
275
|
+
// Don't mark as dirty when the document is first created or when
|
|
276
|
+
// only metadata-only fields change. `x-scalar-registry-meta` is
|
|
277
|
+
// updated programmatically (commit hash, conflict cache) and
|
|
278
|
+
// does not represent a user edit.
|
|
279
|
+
if (event.path.length > 0 && !METADATA_ONLY_DOCUMENT_KEYS.has(event.path[0])) {
|
|
280
|
+
// The document has been modified since it was last saved
|
|
281
|
+
document['x-scalar-is-dirty'] = true;
|
|
282
|
+
}
|
|
283
|
+
fireWorkspaceChange(event);
|
|
284
|
+
return;
|
|
235
285
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
/** Active document changes */
|
|
240
|
-
if (type === 'activeDocument') {
|
|
241
|
-
const documentName = getActiveDocumentName();
|
|
242
|
-
const document = workspace.documents[documentName] ?? {
|
|
243
|
-
openapi: '3.1.0',
|
|
244
|
-
info: { title: '', version: '' },
|
|
245
|
-
'x-scalar-original-document-hash': '',
|
|
246
|
-
};
|
|
247
|
-
// Active document changed
|
|
286
|
+
/** Workspace meta changes */
|
|
287
|
+
const { activeDocument: _a, documents: _d, ...meta } = workspace;
|
|
248
288
|
const event = {
|
|
249
|
-
type: '
|
|
250
|
-
|
|
251
|
-
value: unpackProxyObject(document),
|
|
252
|
-
path: path.slice(2),
|
|
289
|
+
type: 'meta',
|
|
290
|
+
value: unpackProxyObject(meta, { depth: 1 }),
|
|
253
291
|
};
|
|
254
|
-
// Don't mark as dirty when the document is first created or when
|
|
255
|
-
// only metadata-only fields change. `x-scalar-registry-meta` is
|
|
256
|
-
// updated programmatically (commit hash, conflict cache) and
|
|
257
|
-
// does not represent a user edit.
|
|
258
|
-
if (event.path.length > 0 && !METADATA_ONLY_DOCUMENT_KEYS.has(event.path[0])) {
|
|
259
|
-
// The document has been modified since it was last saved
|
|
260
|
-
document['x-scalar-is-dirty'] = true;
|
|
261
|
-
}
|
|
262
292
|
fireWorkspaceChange(event);
|
|
263
293
|
return;
|
|
264
|
-
}
|
|
265
|
-
/** Workspace meta changes */
|
|
266
|
-
const { activeDocument: _a, documents: _d, ...meta } = workspace;
|
|
267
|
-
const event = {
|
|
268
|
-
type: 'meta',
|
|
269
|
-
value: unpackProxyObject(meta, { depth: 1 }),
|
|
270
|
-
};
|
|
271
|
-
fireWorkspaceChange(event);
|
|
272
|
-
return;
|
|
294
|
+
},
|
|
273
295
|
},
|
|
274
|
-
}
|
|
275
|
-
}));
|
|
296
|
+
}));
|
|
276
297
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* Every change to the workspace state (documents, configs, metadata, etc.) can be detected here,
|
|
280
|
-
* allowing for change tracking.
|
|
298
|
+
* The plain document snapshot maps, before the detect changes proxy is applied.
|
|
281
299
|
*/
|
|
282
|
-
const
|
|
300
|
+
const documentSnapshots = {
|
|
283
301
|
/**
|
|
284
302
|
* Holds the original, unmodified documents as they were initially loaded into the workspace.
|
|
285
303
|
* These documents are stored in their raw form—prior to any reactive wrapping, dereferencing, or bundling.
|
|
@@ -311,46 +329,57 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
311
329
|
* OpenAPI document representing the overridden fields.
|
|
312
330
|
*/
|
|
313
331
|
overrides: {},
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
332
|
+
};
|
|
333
|
+
/**
|
|
334
|
+
* An object containing all the workspace state, wrapped in a detect changes proxy.
|
|
335
|
+
*
|
|
336
|
+
* Every change to the workspace state (documents, configs, metadata, etc.) can be detected here,
|
|
337
|
+
* allowing for change tracking.
|
|
338
|
+
*
|
|
339
|
+
* With `reactive: false` the maps are used as-is and a write to them notifies nobody.
|
|
340
|
+
*/
|
|
341
|
+
const { originalDocuments, intermediateDocuments, overrides } = !isReactiveWorkspace
|
|
342
|
+
? documentSnapshots
|
|
343
|
+
: createDetectChangesProxy(documentSnapshots, {
|
|
344
|
+
hooks: {
|
|
345
|
+
onAfterChange(path) {
|
|
346
|
+
const type = path[0];
|
|
347
|
+
if (!type) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (path.length < 2) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const documentName = path[1];
|
|
354
|
+
if (type === 'originalDocuments') {
|
|
355
|
+
const event = {
|
|
356
|
+
type,
|
|
357
|
+
documentName: documentName,
|
|
358
|
+
value: unpackProxyObject(originalDocuments[documentName] ?? {}),
|
|
359
|
+
path: path.splice(2),
|
|
360
|
+
};
|
|
361
|
+
fireWorkspaceChange(event);
|
|
362
|
+
}
|
|
363
|
+
if (type === 'intermediateDocuments') {
|
|
364
|
+
const event = {
|
|
365
|
+
type,
|
|
366
|
+
documentName: documentName,
|
|
367
|
+
value: unpackProxyObject(intermediateDocuments[documentName] ?? {}),
|
|
368
|
+
path: path.splice(2),
|
|
369
|
+
};
|
|
370
|
+
fireWorkspaceChange(event);
|
|
371
|
+
}
|
|
372
|
+
if (type === 'overrides') {
|
|
373
|
+
const event = {
|
|
374
|
+
type,
|
|
375
|
+
documentName: documentName,
|
|
376
|
+
value: unpackProxyObject(overrides[documentName] ?? {}),
|
|
377
|
+
};
|
|
378
|
+
fireWorkspaceChange(event);
|
|
379
|
+
}
|
|
380
|
+
},
|
|
351
381
|
},
|
|
352
|
-
}
|
|
353
|
-
});
|
|
382
|
+
});
|
|
354
383
|
/**
|
|
355
384
|
* This store is used to track the history of requests and responses for documents and operations.
|
|
356
385
|
*/
|
|
@@ -382,6 +411,17 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
382
411
|
},
|
|
383
412
|
},
|
|
384
413
|
});
|
|
414
|
+
/**
|
|
415
|
+
* Whether a document needs to be wrapped in the overrides proxy.
|
|
416
|
+
*
|
|
417
|
+
* A reactive workspace always wraps, so the default mode keeps every document the same shape it has
|
|
418
|
+
* always had. A non-reactive workspace wraps only a document that actually has overrides: with an
|
|
419
|
+
* empty override map the proxy resolves to the target on every read and write anyway, so all it adds
|
|
420
|
+
* is a proxy hop on every nested read. Nothing outside this file reads the proxy's identity, and the
|
|
421
|
+
* store rebuilds the document from the current override map whenever the map changes, so a document
|
|
422
|
+
* that gains overrides later gains the proxy along with them.
|
|
423
|
+
*/
|
|
424
|
+
const needsOverridesProxy = (documentOverrides) => isReactiveWorkspace || (isObject(documentOverrides) && Object.keys(documentOverrides).length > 0);
|
|
385
425
|
/**
|
|
386
426
|
* Returns the name of the currently active document in the workspace.
|
|
387
427
|
* The active document is determined by the 'x-scalar-active-document' metadata field,
|
|
@@ -435,6 +475,10 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
435
475
|
const { name } = input;
|
|
436
476
|
const meta = deepClone(input.meta);
|
|
437
477
|
const clonedRawInputDocument = withMeasurementSync('deepClone', () => deepClone(input.document));
|
|
478
|
+
// A compact sparse document is expanded back into per-node chunk references before anything
|
|
479
|
+
// else reads it, so every step below — including the check for a server-generated navigation —
|
|
480
|
+
// sees the document a non-compact server store would have sent.
|
|
481
|
+
withMeasurementSync('expandChunkIndex', () => expandChunkIndex(clonedRawInputDocument));
|
|
438
482
|
withMeasurementSync('initialize', () => {
|
|
439
483
|
if (input.initialize !== false) {
|
|
440
484
|
// Store the original document in the originalDocuments map
|
|
@@ -497,9 +541,10 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
497
541
|
const navigation = traverseAsyncApiDocument(name, asyncApiDocument, navigationOptions);
|
|
498
542
|
asyncApiDocument[extensions.document.navigation] = navigation;
|
|
499
543
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
544
|
+
const asyncApiOverrides = unpackProxyObject(overrides[name]);
|
|
545
|
+
workspace.documents[name] = needsOverridesProxy(asyncApiOverrides)
|
|
546
|
+
? createOverridesProxy(asyncApiDocument, { overrides: asyncApiOverrides })
|
|
547
|
+
: asyncApiDocument;
|
|
503
548
|
return;
|
|
504
549
|
}
|
|
505
550
|
const inputDocument = withMeasurementSync('upgrade', () => upgrade(deepClone(clonedRawInputDocument), '3.1'));
|
|
@@ -519,7 +564,7 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
519
564
|
plugins: [
|
|
520
565
|
...loaders,
|
|
521
566
|
normalizeRefs(),
|
|
522
|
-
externalValueResolver(),
|
|
567
|
+
externalValueResolver({ lazy: true }),
|
|
523
568
|
refsEverywhere(),
|
|
524
569
|
normalizeAuthSchemes(),
|
|
525
570
|
syncPathParameters(),
|
|
@@ -550,9 +595,11 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
550
595
|
// Create a proxied document with magic proxy and apply any overrides, then store it in the workspace documents map
|
|
551
596
|
// We create a new proxy here in order to hide internal properties after validation and processing
|
|
552
597
|
// This ensures that the workspace document only exposes the intended OpenAPI properties and extensions
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
598
|
+
const documentOverrides = unpackProxyObject(overrides[name]);
|
|
599
|
+
const magicDocument = createMagicProxy(getRaw(strictDocument));
|
|
600
|
+
workspace.documents[name] = needsOverridesProxy(documentOverrides)
|
|
601
|
+
? createOverridesProxy(magicDocument, { overrides: documentOverrides })
|
|
602
|
+
: magicDocument;
|
|
556
603
|
}
|
|
557
604
|
// Asynchronously adds a new document to the workspace by loading and validating the input.
|
|
558
605
|
// If loading fails, a placeholder error document is added instead.
|
|
@@ -687,6 +734,22 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
687
734
|
// This is needed because we are doing partial bundle operations
|
|
688
735
|
const visitedNodesCache = new Set();
|
|
689
736
|
return {
|
|
737
|
+
externalExamples: (documentName) => {
|
|
738
|
+
const name = documentName ?? getActiveDocumentName();
|
|
739
|
+
const document = workspace.documents[name];
|
|
740
|
+
if (!document)
|
|
741
|
+
return fallbackExternalExamples;
|
|
742
|
+
const existing = externalExampleResolvers.get(document);
|
|
743
|
+
if (existing)
|
|
744
|
+
return existing;
|
|
745
|
+
const resolver = createExternalExampleResolver({
|
|
746
|
+
origin: document['x-scalar-original-source-url'],
|
|
747
|
+
fileLoader: workspaceProps?.fileLoader,
|
|
748
|
+
fetch: extraDocumentConfigurations[name]?.fetch ?? workspaceProps?.fetch,
|
|
749
|
+
});
|
|
750
|
+
externalExampleResolvers.set(document, resolver);
|
|
751
|
+
return resolver;
|
|
752
|
+
},
|
|
690
753
|
get workspace() {
|
|
691
754
|
return workspace;
|
|
692
755
|
},
|
|
@@ -756,7 +819,15 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
756
819
|
root: activeDocument,
|
|
757
820
|
origin: activeDocument?.['x-scalar-original-source-url'],
|
|
758
821
|
treeShake: false,
|
|
759
|
-
plugins: [
|
|
822
|
+
plugins: [
|
|
823
|
+
fetchUrls({
|
|
824
|
+
fetch: extraDocumentConfigurations[getActiveDocumentName()]?.fetch ?? workspaceProps?.fetch,
|
|
825
|
+
limit: EXTERNAL_FETCH_CONCURRENCY_LIMIT,
|
|
826
|
+
}),
|
|
827
|
+
...(workspaceProps?.fileLoader ? [workspaceProps.fileLoader] : []),
|
|
828
|
+
loadingStatus(),
|
|
829
|
+
externalValueResolver({ lazy: true }),
|
|
830
|
+
],
|
|
760
831
|
urlMap: true,
|
|
761
832
|
visitedNodes: visitedNodesCache,
|
|
762
833
|
});
|
|
@@ -850,12 +921,23 @@ export const createWorkspaceStore = (workspaceProps) => {
|
|
|
850
921
|
};
|
|
851
922
|
},
|
|
852
923
|
loadWorkspace(input) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
])
|
|
924
|
+
// A workspace from a compact server store carries an index rather than per-node chunk
|
|
925
|
+
// references; expanded here for the same reason `addInMemoryDocument` expands.
|
|
926
|
+
for (const document of Object.values(input.documents)) {
|
|
927
|
+
expandChunkIndex(document);
|
|
928
|
+
}
|
|
929
|
+
safeAssign(workspace.documents, Object.fromEntries(Object.entries(input.documents).map(([name, doc]) => {
|
|
930
|
+
// Hydration only rewraps: an exported document has already been upgraded, bundled, coerced
|
|
931
|
+
// and given its navigation, so nothing here re-processes it.
|
|
932
|
+
const magicDocument = createMagicProxy(doc);
|
|
933
|
+
const documentOverrides = input.overrides[name];
|
|
934
|
+
return [
|
|
935
|
+
name,
|
|
936
|
+
needsOverridesProxy(documentOverrides)
|
|
937
|
+
? createOverridesProxy(magicDocument, { overrides: documentOverrides })
|
|
938
|
+
: magicDocument,
|
|
939
|
+
];
|
|
940
|
+
})));
|
|
859
941
|
safeAssign(originalDocuments, input.originalDocuments);
|
|
860
942
|
safeAssign(intermediateDocuments, input.intermediateDocuments);
|
|
861
943
|
safeAssign(overrides, input.overrides);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The extension key a compact sparse document carries its chunk index under.
|
|
3
|
+
*
|
|
4
|
+
* It exists on the wire only: the client expands the index back into per-node references while the
|
|
5
|
+
* document is ingested and removes the key, so nothing downstream ever sees it.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CHUNK_INDEX_KEY = "x-scalar-chunk-index";
|
|
8
|
+
/** Where a document's chunks live, which decides how each reference to one is spelled. */
|
|
9
|
+
export type ChunkMode = 'static' | 'ssr';
|
|
10
|
+
/** The `$ref` template for each kind of chunk, with `{…}` slots the reader fills in. */
|
|
11
|
+
export type ChunkRefTemplates = {
|
|
12
|
+
/** Slots: `{type}`, `{name}`. */
|
|
13
|
+
components: string;
|
|
14
|
+
/** Slots: `{path}`, `{method}`. */
|
|
15
|
+
operations: string;
|
|
16
|
+
/** No slots. */
|
|
17
|
+
navigation: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The compact stand-in for a sparse document's `components` and `paths`.
|
|
21
|
+
*
|
|
22
|
+
* Every reference the non-compact form spells out node by node is derivable from where that node
|
|
23
|
+
* sits, so the index carries the positions and one template per kind instead. What a path item
|
|
24
|
+
* keeps besides its operations is not derivable, so it rides along verbatim.
|
|
25
|
+
*/
|
|
26
|
+
export type ChunkIndex = {
|
|
27
|
+
mode: ChunkMode;
|
|
28
|
+
refs: ChunkRefTemplates;
|
|
29
|
+
/** Component names per component type, in document order. */
|
|
30
|
+
components: Record<string, string[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Each path item with its operations replaced by a placeholder.
|
|
33
|
+
*
|
|
34
|
+
* The whole path item is kept rather than a list of methods so that both its key order and the
|
|
35
|
+
* keys that were never externalized survive: `parameters`, `summary`, `servers` and extensions
|
|
36
|
+
* all stay inline in the non-compact form.
|
|
37
|
+
*/
|
|
38
|
+
paths: Record<string, Record<string, unknown>>;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Fills the `{slot}`s of a chunk-reference template.
|
|
42
|
+
*
|
|
43
|
+
* `{{` and `}}` stand for literal braces, so text the writer inlined into a template — a document
|
|
44
|
+
* name, a base URL — may contain braces without being read back as a slot. An unknown slot is left
|
|
45
|
+
* alone rather than blanked, so a template from a newer writer fails visibly instead of resolving
|
|
46
|
+
* to the wrong chunk.
|
|
47
|
+
*/
|
|
48
|
+
export declare const fillChunkRef: (template: string, values?: Record<string, string>) => string;
|
|
49
|
+
/**
|
|
50
|
+
* Builds the reference templates for one document.
|
|
51
|
+
*
|
|
52
|
+
* Filled, these produce exactly the references `externalizeComponentReferences` and
|
|
53
|
+
* `externalizePathReferences` write, so a client that expands the index lands on the same chunk.
|
|
54
|
+
*/
|
|
55
|
+
export declare const chunkRefTemplates: (meta: {
|
|
56
|
+
mode: "ssr";
|
|
57
|
+
name: string;
|
|
58
|
+
baseUrl: string;
|
|
59
|
+
} | {
|
|
60
|
+
mode: "static";
|
|
61
|
+
name: string;
|
|
62
|
+
}) => ChunkRefTemplates;
|
|
63
|
+
/** The reference a lazily loaded chunk is reached through. */
|
|
64
|
+
export declare const chunkReference: (ref: string) => {
|
|
65
|
+
$ref: string;
|
|
66
|
+
$global: true;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Compacts a sparse document's `components` and `paths` into an index.
|
|
70
|
+
*
|
|
71
|
+
* Takes the sections the externalizers produced rather than the document itself, so the index is
|
|
72
|
+
* built from the very references it replaces and the two cannot come to describe different sets of
|
|
73
|
+
* chunks.
|
|
74
|
+
*/
|
|
75
|
+
export declare const buildChunkIndex: ({ mode, refs, components, paths, }: {
|
|
76
|
+
mode: ChunkMode;
|
|
77
|
+
refs: ChunkRefTemplates;
|
|
78
|
+
components: Record<string, Record<string, unknown>>;
|
|
79
|
+
paths: Record<string, Record<string, unknown>>;
|
|
80
|
+
}) => ChunkIndex;
|
|
81
|
+
/**
|
|
82
|
+
* Expands a compact sparse document into the one a non-compact server store would have sent.
|
|
83
|
+
*
|
|
84
|
+
* Mutates the document in place and drops the index key, so what the rest of the store sees is an
|
|
85
|
+
* ordinary sparse document: `resolve()`, the bundler and anything enumerating `paths` or
|
|
86
|
+
* `components` are looking at the shape they always have.
|
|
87
|
+
*
|
|
88
|
+
* A document without an index is left alone, which is every document a non-compact server store or
|
|
89
|
+
* an author produces.
|
|
90
|
+
*
|
|
91
|
+
* @param document - The document to expand, mutated in place
|
|
92
|
+
* @returns Whether an index was found and expanded
|
|
93
|
+
*/
|
|
94
|
+
export declare const expandChunkIndex: (document: unknown) => boolean;
|
|
95
|
+
//# sourceMappingURL=chunk-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-index.d.ts","sourceRoot":"","sources":["../../src/helpers/chunk-index.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,eAAO,MAAM,eAAe,yBAAyB,CAAA;AAKrD,0FAA0F;AAC1F,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,KAAK,CAAA;AAExC,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,gBAAgB;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,iBAAiB,CAAA;IACvB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACpC;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAC/C,CAAA;AAKD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAI,UAAU,MAAM,EAAE,SAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,KAAG,MAGlF,CAAA;AAwBH;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KACtF,iBAkBF,CAAA;AAED,8DAA8D;AAC9D,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,KAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAsC,CAAA;AAEhH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,oCAK7B;IACD,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,iBAAiB,CAAA;IACvB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACnD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAC/C,KAAG,UAYF,CAAA;AAUF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,GAAI,UAAU,OAAO,KAAG,OAqDpD,CAAA"}
|