@scalar/workspace-store 0.63.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 (36) hide show
  1. package/CHANGELOG.md +24 -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 +216 -139
  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/external-examples.d.ts +17 -0
  10. package/dist/helpers/external-examples.d.ts.map +1 -0
  11. package/dist/helpers/external-examples.js +50 -0
  12. package/dist/helpers/operation-examples.d.ts +6 -0
  13. package/dist/helpers/operation-examples.d.ts.map +1 -0
  14. package/dist/helpers/operation-examples.js +45 -0
  15. package/dist/helpers/use-external-examples.d.ts +15 -0
  16. package/dist/helpers/use-external-examples.d.ts.map +1 -0
  17. package/dist/helpers/use-external-examples.js +55 -0
  18. package/dist/mutators/operation/parameters.d.ts.map +1 -1
  19. package/dist/mutators/operation/parameters.js +28 -9
  20. package/dist/plugins/bundler/index.d.ts +5 -2
  21. package/dist/plugins/bundler/index.d.ts.map +1 -1
  22. package/dist/plugins/bundler/index.js +12 -3
  23. package/dist/request-example/builder/header/is-param-disabled.d.ts +2 -2
  24. package/dist/request-example/builder/header/is-param-disabled.d.ts.map +1 -1
  25. package/dist/request-example/builder/header/is-param-disabled.js +5 -4
  26. package/dist/request-example/builder/helpers/get-example.d.ts +2 -0
  27. package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
  28. package/dist/request-example/builder/helpers/get-example.js +9 -7
  29. package/dist/request-example/context/headers.d.ts.map +1 -1
  30. package/dist/request-example/context/headers.js +3 -4
  31. package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
  32. package/dist/schemas/v3.2/strict/openapi-document.js +17 -1
  33. package/dist/server.d.ts +16 -0
  34. package/dist/server.d.ts.map +1 -1
  35. package/dist/server.js +34 -9
  36. package/package.json +3 -3
package/dist/client.js CHANGED
@@ -16,9 +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';
21
22
  import { bumpDocumentRevision } from './helpers/document-revision.js';
23
+ import { createExternalExampleResolver } from './helpers/external-examples.js';
22
24
  import { safeAssign } from './helpers/general.js';
23
25
  import { getFetch } from './helpers/get-fetch.js';
24
26
  import { mergeObjects } from './helpers/merge-object.js';
@@ -156,7 +158,7 @@ const purgeInternalDocumentKeys = (input) => {
156
158
  * @returns An object containing methods and getters for managing the workspace
157
159
  */
158
160
  export const createWorkspaceStore = (workspaceProps) => {
159
- const { verbose = false } = workspaceProps ?? {};
161
+ const { verbose = false, reactive: isReactiveWorkspace = true } = workspaceProps ?? {};
160
162
  const withMeasurementSync = (name, fn) => (verbose ? measureSync(name, fn) : fn());
161
163
  const withMeasurementAsync = (name, fn) => verbose ? measureAsync(name, fn) : fn();
162
164
  /**
@@ -165,6 +167,11 @@ export const createWorkspaceStore = (workspaceProps) => {
165
167
  * This can include settings that can not be persisted between sessions (not JSON serializable)
166
168
  */
167
169
  const extraDocumentConfigurations = {};
170
+ const externalExampleResolvers = new WeakMap();
171
+ const fallbackExternalExamples = createExternalExampleResolver({
172
+ fetch: workspaceProps?.fetch,
173
+ fileLoader: workspaceProps?.fileLoader,
174
+ });
168
175
  /**
169
176
  * Notifies all workspace plugins of a workspace state change event.
170
177
  *
@@ -177,20 +184,9 @@ export const createWorkspaceStore = (workspaceProps) => {
177
184
  workspaceProps?.plugins?.forEach((plugin) => plugin.hooks?.onWorkspaceStateChanges?.(event));
178
185
  };
179
186
  /**
180
- * An object containing the reactive workspace state.
181
- *
182
- * Every change to the workspace, is tracked and broadcast to all registered plugins.
183
- * allowing for change tracking.
184
- *
185
- * NOTE:
186
- * The detect changes proxy is applied separately beacause the vue reactitvity proxy have to be the outer most proxy.
187
- * If the order is reversed, Vue cannot properly track mutations, leading to lost reactivity and bugs.
188
- * By wrapping the contents with the detect changes proxy first, and then passing the result to Vue's `reactive`,
189
- * we ensure that Vue manages its reactivity as expected and our change detection hooks
190
- * are also triggered reliably.
191
- * Do not reverse this order‼️
187
+ * The plain workspace state, before any observability wrappers are applied.
192
188
  */
193
- const workspace = reactive(createDetectChangesProxy({
189
+ const workspaceState = {
194
190
  ...workspaceProps?.meta,
195
191
  documents: {},
196
192
  /**
@@ -203,88 +199,105 @@ export const createWorkspaceStore = (workspaceProps) => {
203
199
  get activeDocument() {
204
200
  return workspace.documents[getActiveDocumentName()];
205
201
  },
206
- }, {
207
- hooks: {
208
- onAfterChange(path) {
209
- const type = path[0];
210
- /** Document changes */
211
- if (type === 'documents') {
212
- // We are overriding the while documents object, ignore. This should not happen
213
- if (path.length < 2) {
214
- 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);
215
257
  return;
216
258
  }
217
- const documentName = path[1];
218
- const document = workspace.documents[documentName] ?? {
219
- openapi: '3.1.0',
220
- info: { title: '', version: '' },
221
- 'x-scalar-original-document-hash': '',
222
- };
223
- // Every write through the store passes here, which is what makes the revision a
224
- // complete record of the document changing.
225
- bumpDocumentRevision(document);
226
- const event = {
227
- type: 'documents',
228
- documentName,
229
- value: unpackProxyObject(document),
230
- path: path.slice(2),
231
- };
232
- // Don't mark as dirty when the document is first created or when
233
- // only metadata-only fields change. `x-scalar-registry-meta` is
234
- // updated programmatically (commit hash, conflict cache) and
235
- // does not represent a user edit.
236
- if (event.path.length > 0 && !METADATA_ONLY_DOCUMENT_KEYS.has(event.path[0])) {
237
- // The document has been modified since it was last saved
238
- 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;
239
285
  }
240
- fireWorkspaceChange(event);
241
- return;
242
- }
243
- /** Active document changes */
244
- if (type === 'activeDocument') {
245
- const documentName = getActiveDocumentName();
246
- const document = workspace.documents[documentName] ?? {
247
- openapi: '3.1.0',
248
- info: { title: '', version: '' },
249
- 'x-scalar-original-document-hash': '',
250
- };
251
- bumpDocumentRevision(document);
252
- // Active document changed
286
+ /** Workspace meta changes */
287
+ const { activeDocument: _a, documents: _d, ...meta } = workspace;
253
288
  const event = {
254
- type: 'documents',
255
- documentName,
256
- value: unpackProxyObject(document),
257
- path: path.slice(2),
289
+ type: 'meta',
290
+ value: unpackProxyObject(meta, { depth: 1 }),
258
291
  };
259
- // Don't mark as dirty when the document is first created or when
260
- // only metadata-only fields change. `x-scalar-registry-meta` is
261
- // updated programmatically (commit hash, conflict cache) and
262
- // does not represent a user edit.
263
- if (event.path.length > 0 && !METADATA_ONLY_DOCUMENT_KEYS.has(event.path[0])) {
264
- // The document has been modified since it was last saved
265
- document['x-scalar-is-dirty'] = true;
266
- }
267
292
  fireWorkspaceChange(event);
268
293
  return;
269
- }
270
- /** Workspace meta changes */
271
- const { activeDocument: _a, documents: _d, ...meta } = workspace;
272
- const event = {
273
- type: 'meta',
274
- value: unpackProxyObject(meta, { depth: 1 }),
275
- };
276
- fireWorkspaceChange(event);
277
- return;
294
+ },
278
295
  },
279
- },
280
- }));
296
+ }));
281
297
  /**
282
- * An object containing all the workspace state, wrapped in a detect changes proxy.
283
- *
284
- * Every change to the workspace state (documents, configs, metadata, etc.) can be detected here,
285
- * allowing for change tracking.
298
+ * The plain document snapshot maps, before the detect changes proxy is applied.
286
299
  */
287
- const { originalDocuments, intermediateDocuments, overrides } = createDetectChangesProxy({
300
+ const documentSnapshots = {
288
301
  /**
289
302
  * Holds the original, unmodified documents as they were initially loaded into the workspace.
290
303
  * These documents are stored in their raw form—prior to any reactive wrapping, dereferencing, or bundling.
@@ -316,46 +329,57 @@ export const createWorkspaceStore = (workspaceProps) => {
316
329
  * OpenAPI document representing the overridden fields.
317
330
  */
318
331
  overrides: {},
319
- }, {
320
- hooks: {
321
- onAfterChange(path) {
322
- const type = path[0];
323
- if (!type) {
324
- return;
325
- }
326
- if (path.length < 2) {
327
- return;
328
- }
329
- const documentName = path[1];
330
- if (type === 'originalDocuments') {
331
- const event = {
332
- type,
333
- documentName: documentName,
334
- value: unpackProxyObject(originalDocuments[documentName] ?? {}),
335
- path: path.splice(2),
336
- };
337
- fireWorkspaceChange(event);
338
- }
339
- if (type === 'intermediateDocuments') {
340
- const event = {
341
- type,
342
- documentName: documentName,
343
- value: unpackProxyObject(intermediateDocuments[documentName] ?? {}),
344
- path: path.splice(2),
345
- };
346
- fireWorkspaceChange(event);
347
- }
348
- if (type === 'overrides') {
349
- const event = {
350
- type,
351
- documentName: documentName,
352
- value: unpackProxyObject(overrides[documentName] ?? {}),
353
- };
354
- fireWorkspaceChange(event);
355
- }
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
+ },
356
381
  },
357
- },
358
- });
382
+ });
359
383
  /**
360
384
  * This store is used to track the history of requests and responses for documents and operations.
361
385
  */
@@ -387,6 +411,17 @@ export const createWorkspaceStore = (workspaceProps) => {
387
411
  },
388
412
  },
389
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);
390
425
  /**
391
426
  * Returns the name of the currently active document in the workspace.
392
427
  * The active document is determined by the 'x-scalar-active-document' metadata field,
@@ -440,6 +475,10 @@ export const createWorkspaceStore = (workspaceProps) => {
440
475
  const { name } = input;
441
476
  const meta = deepClone(input.meta);
442
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));
443
482
  withMeasurementSync('initialize', () => {
444
483
  if (input.initialize !== false) {
445
484
  // Store the original document in the originalDocuments map
@@ -502,9 +541,10 @@ export const createWorkspaceStore = (workspaceProps) => {
502
541
  const navigation = traverseAsyncApiDocument(name, asyncApiDocument, navigationOptions);
503
542
  asyncApiDocument[extensions.document.navigation] = navigation;
504
543
  }
505
- workspace.documents[name] = createOverridesProxy(asyncApiDocument, {
506
- overrides: unpackProxyObject(overrides[name]),
507
- });
544
+ const asyncApiOverrides = unpackProxyObject(overrides[name]);
545
+ workspace.documents[name] = needsOverridesProxy(asyncApiOverrides)
546
+ ? createOverridesProxy(asyncApiDocument, { overrides: asyncApiOverrides })
547
+ : asyncApiDocument;
508
548
  return;
509
549
  }
510
550
  const inputDocument = withMeasurementSync('upgrade', () => upgrade(deepClone(clonedRawInputDocument), '3.1'));
@@ -524,7 +564,7 @@ export const createWorkspaceStore = (workspaceProps) => {
524
564
  plugins: [
525
565
  ...loaders,
526
566
  normalizeRefs(),
527
- externalValueResolver(),
567
+ externalValueResolver({ lazy: true }),
528
568
  refsEverywhere(),
529
569
  normalizeAuthSchemes(),
530
570
  syncPathParameters(),
@@ -555,9 +595,11 @@ export const createWorkspaceStore = (workspaceProps) => {
555
595
  // Create a proxied document with magic proxy and apply any overrides, then store it in the workspace documents map
556
596
  // We create a new proxy here in order to hide internal properties after validation and processing
557
597
  // This ensures that the workspace document only exposes the intended OpenAPI properties and extensions
558
- workspace.documents[name] = createOverridesProxy(createMagicProxy(getRaw(strictDocument)), {
559
- overrides: unpackProxyObject(overrides[name]),
560
- });
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;
561
603
  }
562
604
  // Asynchronously adds a new document to the workspace by loading and validating the input.
563
605
  // If loading fails, a placeholder error document is added instead.
@@ -692,6 +734,22 @@ export const createWorkspaceStore = (workspaceProps) => {
692
734
  // This is needed because we are doing partial bundle operations
693
735
  const visitedNodesCache = new Set();
694
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
+ },
695
753
  get workspace() {
696
754
  return workspace;
697
755
  },
@@ -761,7 +819,15 @@ export const createWorkspaceStore = (workspaceProps) => {
761
819
  root: activeDocument,
762
820
  origin: activeDocument?.['x-scalar-original-source-url'],
763
821
  treeShake: false,
764
- 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
+ ],
765
831
  urlMap: true,
766
832
  visitedNodes: visitedNodesCache,
767
833
  });
@@ -855,12 +921,23 @@ export const createWorkspaceStore = (workspaceProps) => {
855
921
  };
856
922
  },
857
923
  loadWorkspace(input) {
858
- safeAssign(workspace.documents, Object.fromEntries(Object.entries(input.documents).map(([name, doc]) => [
859
- name,
860
- createOverridesProxy(createMagicProxy(doc), {
861
- overrides: input.overrides[name],
862
- }),
863
- ])));
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
+ })));
864
941
  safeAssign(originalDocuments, input.originalDocuments);
865
942
  safeAssign(intermediateDocuments, input.intermediateDocuments);
866
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"}