@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.
Files changed (54) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +88 -0
  3. package/dist/client.d.ts +28 -0
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +217 -135
  6. package/dist/helpers/chunk-index.d.ts +95 -0
  7. package/dist/helpers/chunk-index.d.ts.map +1 -0
  8. package/dist/helpers/chunk-index.js +139 -0
  9. package/dist/helpers/detect-changes-proxy.d.ts +7 -6
  10. package/dist/helpers/detect-changes-proxy.d.ts.map +1 -1
  11. package/dist/helpers/detect-changes-proxy.js +83 -38
  12. package/dist/helpers/document-revision.d.ts +26 -0
  13. package/dist/helpers/document-revision.d.ts.map +1 -0
  14. package/dist/helpers/document-revision.js +47 -0
  15. package/dist/helpers/external-examples.d.ts +17 -0
  16. package/dist/helpers/external-examples.d.ts.map +1 -0
  17. package/dist/helpers/external-examples.js +50 -0
  18. package/dist/helpers/get-resolved-ref-deep.d.ts.map +1 -1
  19. package/dist/helpers/get-resolved-ref-deep.js +25 -13
  20. package/dist/helpers/get-resolved-ref.d.ts.map +1 -1
  21. package/dist/helpers/get-resolved-ref.js +61 -3
  22. package/dist/helpers/operation-examples.d.ts +6 -0
  23. package/dist/helpers/operation-examples.d.ts.map +1 -0
  24. package/dist/helpers/operation-examples.js +45 -0
  25. package/dist/helpers/unpack-proxy.d.ts +10 -0
  26. package/dist/helpers/unpack-proxy.d.ts.map +1 -1
  27. package/dist/helpers/unpack-proxy.js +15 -0
  28. package/dist/helpers/use-external-examples.d.ts +15 -0
  29. package/dist/helpers/use-external-examples.d.ts.map +1 -0
  30. package/dist/helpers/use-external-examples.js +55 -0
  31. package/dist/mutators/operation/parameters.d.ts.map +1 -1
  32. package/dist/mutators/operation/parameters.js +28 -9
  33. package/dist/plugins/bundler/index.d.ts +5 -2
  34. package/dist/plugins/bundler/index.d.ts.map +1 -1
  35. package/dist/plugins/bundler/index.js +12 -3
  36. package/dist/request-example/builder/header/is-param-disabled.d.ts +2 -2
  37. package/dist/request-example/builder/header/is-param-disabled.d.ts.map +1 -1
  38. package/dist/request-example/builder/header/is-param-disabled.js +5 -4
  39. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
  40. package/dist/request-example/builder/helpers/get-example-from-schema.js +21 -1
  41. package/dist/request-example/builder/helpers/get-example.d.ts +2 -0
  42. package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
  43. package/dist/request-example/builder/helpers/get-example.js +9 -7
  44. package/dist/request-example/context/headers.d.ts.map +1 -1
  45. package/dist/request-example/context/headers.js +3 -4
  46. package/dist/resolve.d.ts +10 -2
  47. package/dist/resolve.d.ts.map +1 -1
  48. package/dist/resolve.js +9 -1
  49. package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
  50. package/dist/schemas/v3.2/strict/openapi-document.js +17 -1
  51. package/dist/server.d.ts +16 -0
  52. package/dist/server.d.ts.map +1 -1
  53. package/dist/server.js +34 -9
  54. 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
- * An object containing the reactive workspace state.
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 workspace = reactive(createDetectChangesProxy({
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
- hooks: {
207
- onAfterChange(path) {
208
- const type = path[0];
209
- /** Document changes */
210
- if (type === 'documents') {
211
- // We are overriding the while documents object, ignore. This should not happen
212
- if (path.length < 2) {
213
- console.log('[WARN]: Overriding entire documents object is not supported');
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
- const documentName = path[1];
217
- const document = workspace.documents[documentName] ?? {
218
- openapi: '3.1.0',
219
- info: { title: '', version: '' },
220
- 'x-scalar-original-document-hash': '',
221
- };
222
- const event = {
223
- type: 'documents',
224
- documentName,
225
- value: unpackProxyObject(document),
226
- path: path.slice(2),
227
- };
228
- // Don't mark as dirty when the document is first created or when
229
- // only metadata-only fields change. `x-scalar-registry-meta` is
230
- // updated programmatically (commit hash, conflict cache) and
231
- // does not represent a user edit.
232
- if (event.path.length > 0 && !METADATA_ONLY_DOCUMENT_KEYS.has(event.path[0])) {
233
- // The document has been modified since it was last saved
234
- document['x-scalar-is-dirty'] = true;
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
- fireWorkspaceChange(event);
237
- return;
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: 'documents',
250
- documentName,
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
- * An object containing all the workspace state, wrapped in a detect changes proxy.
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 { originalDocuments, intermediateDocuments, overrides } = createDetectChangesProxy({
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
- hooks: {
316
- onAfterChange(path) {
317
- const type = path[0];
318
- if (!type) {
319
- return;
320
- }
321
- if (path.length < 2) {
322
- return;
323
- }
324
- const documentName = path[1];
325
- if (type === 'originalDocuments') {
326
- const event = {
327
- type,
328
- documentName: documentName,
329
- value: unpackProxyObject(originalDocuments[documentName] ?? {}),
330
- path: path.splice(2),
331
- };
332
- fireWorkspaceChange(event);
333
- }
334
- if (type === 'intermediateDocuments') {
335
- const event = {
336
- type,
337
- documentName: documentName,
338
- value: unpackProxyObject(intermediateDocuments[documentName] ?? {}),
339
- path: path.splice(2),
340
- };
341
- fireWorkspaceChange(event);
342
- }
343
- if (type === 'overrides') {
344
- const event = {
345
- type,
346
- documentName: documentName,
347
- value: unpackProxyObject(overrides[documentName] ?? {}),
348
- };
349
- fireWorkspaceChange(event);
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
- workspace.documents[name] = createOverridesProxy(asyncApiDocument, {
501
- overrides: unpackProxyObject(overrides[name]),
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
- workspace.documents[name] = createOverridesProxy(createMagicProxy(getRaw(strictDocument)), {
554
- overrides: unpackProxyObject(overrides[name]),
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: [fetchUrls(), loadingStatus(), externalValueResolver()],
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
- safeAssign(workspace.documents, Object.fromEntries(Object.entries(input.documents).map(([name, doc]) => [
854
- name,
855
- createOverridesProxy(createMagicProxy(doc), {
856
- overrides: input.overrides[name],
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"}