@scalar/workspace-store 0.67.2 → 0.68.1

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 (35) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +37 -15
  4. package/dist/entities/auth/schema.d.ts +390 -0
  5. package/dist/entities/auth/schema.d.ts.map +1 -1
  6. package/dist/entities/auth/schema.js +12 -8
  7. package/dist/events/definitions/auth.d.ts +7 -0
  8. package/dist/events/definitions/auth.d.ts.map +1 -1
  9. package/dist/helpers/asyncapi-chunk-groups.d.ts +3 -0
  10. package/dist/helpers/asyncapi-chunk-groups.d.ts.map +1 -0
  11. package/dist/helpers/asyncapi-chunk-groups.js +55 -0
  12. package/dist/helpers/asyncapi-model-chunk-reference.d.ts +3 -0
  13. package/dist/helpers/asyncapi-model-chunk-reference.d.ts.map +1 -0
  14. package/dist/helpers/asyncapi-model-chunk-reference.js +11 -0
  15. package/dist/helpers/auth-secret-fields.d.ts +9 -0
  16. package/dist/helpers/auth-secret-fields.d.ts.map +1 -0
  17. package/dist/helpers/auth-secret-fields.js +42 -0
  18. package/dist/mutators/auth.d.ts +1 -0
  19. package/dist/mutators/auth.d.ts.map +1 -1
  20. package/dist/mutators/auth.js +29 -0
  21. package/dist/mutators/index.d.ts +2 -0
  22. package/dist/mutators/index.d.ts.map +1 -1
  23. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts +2 -0
  24. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
  25. package/dist/request-example/builder/helpers/get-example-from-schema.js +25 -4
  26. package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
  27. package/dist/request-example/builder/helpers/get-example.js +30 -6
  28. package/dist/request-example/builder/security/secret-types.d.ts +4 -1
  29. package/dist/request-example/builder/security/secret-types.d.ts.map +1 -1
  30. package/dist/request-example/context/security/extract-security-scheme-secrets.d.ts.map +1 -1
  31. package/dist/request-example/context/security/extract-security-scheme-secrets.js +33 -34
  32. package/dist/server.d.ts +2 -1
  33. package/dist/server.d.ts.map +1 -1
  34. package/dist/server.js +85 -19
  35. package/package.json +9 -9
@@ -1,5 +1,6 @@
1
1
  import { isObject } from '@scalar/helpers/object/is-object';
2
2
  import { objectEntries } from '@scalar/helpers/object/object-entries';
3
+ import { isSecretFieldCleared } from '@scalar/workspace-store/helpers/auth-secret-fields';
3
4
  /**
4
5
  * Maps x-scalar-secret fields to their corresponding input field names.
5
6
  * This allows us to fall back to config values when auth store secrets are not available.
@@ -14,24 +15,30 @@ const SECRET_TO_INPUT_FIELD_MAP = {
14
15
  'x-scalar-secret-auth-url': 'authorizationUrl',
15
16
  'x-scalar-secret-token-url': 'tokenUrl',
16
17
  };
17
- const mergeFlowSecrets = (properties, configSecrets, authStoreSecrets = {}, oauth2RedirectUri) => Object.fromEntries(properties.map((property) => {
18
- // Redirect URI is the only OAuth secret where an explicit empty value from
19
- // store must be preserved. Other secrets use falsy fallback behavior for backwards-compatibility
20
- // with config defaults when the casted auth store value is an empty string.
21
- const authStoreValue = typeof authStoreSecrets[property] === 'string' ? authStoreSecrets[property] : undefined;
22
- const configValue = typeof configSecrets[property] === 'string' ? configSecrets[property] : undefined;
23
- const configInputValue = typeof configSecrets[SECRET_TO_INPUT_FIELD_MAP[property]] === 'string'
24
- ? configSecrets[SECRET_TO_INPUT_FIELD_MAP[property]]
25
- : undefined;
26
- // oauth2RedirectUri (top-level config option) is used as a global fallback for the redirect URI,
27
- // applied only when neither the auth store nor per-scheme config have a value. This ensures
28
- // the configured redirect URI persists when switching between documents with the same OAuth config,
29
- // because each document starts with no stored redirect URI (authStoreValue === undefined).
30
- const value = property === 'x-scalar-secret-redirect-uri'
31
- ? (authStoreValue ?? configValue ?? configInputValue ?? oauth2RedirectUri ?? '')
32
- : authStoreValue || configValue || configInputValue || '';
33
- return [property, value];
34
- }));
18
+ const mergeFlowSecrets = (properties, configSecrets, authStoreSecrets = {}, oauth2RedirectUri) => {
19
+ const clearedFields = properties.filter((property) => isSecretFieldCleared(authStoreSecrets, property));
20
+ const values = Object.fromEntries(properties.map((property) => {
21
+ // Preserve explicit clears, while legacy schema-filled empties still inherit defaults.
22
+ const authStoreValue = typeof authStoreSecrets[property] === 'string' ? authStoreSecrets[property] : undefined;
23
+ const configValue = typeof configSecrets[property] === 'string' ? configSecrets[property] : undefined;
24
+ const configInputValue = typeof configSecrets[SECRET_TO_INPUT_FIELD_MAP[property]] === 'string'
25
+ ? configSecrets[SECRET_TO_INPUT_FIELD_MAP[property]]
26
+ : undefined;
27
+ // oauth2RedirectUri (top-level config option) is used as a global fallback for the redirect URI,
28
+ // applied only when neither the auth store nor per-scheme config have a value. This ensures
29
+ // the configured redirect URI persists when switching between documents with the same OAuth config,
30
+ // because each document starts with no stored redirect URI (authStoreValue === undefined).
31
+ const value = property === 'x-scalar-secret-redirect-uri' || isSecretFieldCleared(authStoreSecrets, property)
32
+ ? (authStoreValue ?? configValue ?? configInputValue ?? oauth2RedirectUri ?? '')
33
+ : authStoreValue || configValue || configInputValue || '';
34
+ return [property, value];
35
+ }));
36
+ return { ...values, ...(clearedFields.length ? { 'x-scalar-secret-cleared-fields': clearedFields } : {}) };
37
+ };
38
+ const storedSecret = (secrets, field) => {
39
+ const value = secrets && Reflect.get(secrets, field);
40
+ return typeof value === 'string' && (value || isSecretFieldCleared(secrets ?? {}, field)) ? value : undefined;
41
+ };
35
42
  /** Secret extensions are not part of the strict scheme types, so they are read the same way the OAuth flows read theirs */
36
43
  const documentSecret = (scheme, property) => {
37
44
  const value = property in scheme ? Reflect.get(scheme, property) : undefined;
@@ -151,10 +158,8 @@ scheme, authStore, name, documentSlug, oauth2RedirectUri) => {
151
158
  const storeSecrets = secrets?.type === 'apiKey' ? secrets : undefined;
152
159
  return {
153
160
  ...scheme,
154
- 'x-scalar-secret-token': storeSecrets?.['x-scalar-secret-token'] ||
155
- documentSecret(scheme, 'x-scalar-secret-token') ||
156
- scheme.value ||
157
- '',
161
+ 'x-scalar-secret-token': storedSecret(storeSecrets, 'x-scalar-secret-token') ??
162
+ (documentSecret(scheme, 'x-scalar-secret-token') || scheme.value || ''),
158
163
  };
159
164
  }
160
165
  // Handle HTTP Auth security schemes (e.g., Basic, Bearer)
@@ -162,18 +167,12 @@ scheme, authStore, name, documentSlug, oauth2RedirectUri) => {
162
167
  const storeSecrets = secrets?.type === 'http' ? secrets : undefined;
163
168
  return {
164
169
  ...scheme,
165
- 'x-scalar-secret-token': storeSecrets?.['x-scalar-secret-token'] ||
166
- documentSecret(scheme, 'x-scalar-secret-token') ||
167
- scheme.token ||
168
- '',
169
- 'x-scalar-secret-username': storeSecrets?.['x-scalar-secret-username'] ||
170
- documentSecret(scheme, 'x-scalar-secret-username') ||
171
- scheme.username ||
172
- '',
173
- 'x-scalar-secret-password': storeSecrets?.['x-scalar-secret-password'] ||
174
- documentSecret(scheme, 'x-scalar-secret-password') ||
175
- scheme.password ||
176
- '',
170
+ 'x-scalar-secret-token': storedSecret(storeSecrets, 'x-scalar-secret-token') ??
171
+ (documentSecret(scheme, 'x-scalar-secret-token') || scheme.token || ''),
172
+ 'x-scalar-secret-username': storedSecret(storeSecrets, 'x-scalar-secret-username') ??
173
+ (documentSecret(scheme, 'x-scalar-secret-username') || scheme.username || ''),
174
+ 'x-scalar-secret-password': storedSecret(storeSecrets, 'x-scalar-secret-password') ??
175
+ (documentSecret(scheme, 'x-scalar-secret-password') || scheme.password || ''),
177
176
  };
178
177
  }
179
178
  // Handle OAuth2 security schemes and all supported flows
package/dist/server.d.ts CHANGED
@@ -32,7 +32,8 @@ type CreateServerWorkspaceStoreBase = {
32
32
  * exactly what it would have held without this option.
33
33
  *
34
34
  * `getResolvedDocument()` is unaffected: it carries the whole document and its navigation either
35
- * way. AsyncAPI documents are unaffected too, since nothing about them is externalized.
35
+ * way. AsyncAPI documents externalize their channels, operations, and components; compact mode
36
+ * moves their navigation children into a chunk too.
36
37
  *
37
38
  * @default false
38
39
  */
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -13,7 +13,8 @@ import { createMagicProxy, getRaw } from '@scalar/json-magic/magic-proxy';
13
13
  import { upgrade } from '@scalar/openapi-upgrader';
14
14
  import { asyncApiObjectSchema } from '@scalar/schemas/asyncapi/3.1';
15
15
  import { coerce } from '@scalar/validation';
16
- import { CHUNK_INDEX_KEY, buildChunkIndex, chunkRefTemplates, fillChunkRef, navigationHeader, } from './helpers/chunk-index.js';
16
+ import { asyncApiChunkGroups } from './helpers/asyncapi-chunk-groups.js';
17
+ import { CHUNK_INDEX_KEY, buildChunkIndex, chunkRefTemplates, chunkReference, fillChunkRef, navigationHeader, } from './helpers/chunk-index.js';
17
18
  import { createChunkWriter } from './helpers/create-chunk-writer.js';
18
19
  import { deepClone } from './helpers/deep-clone.js';
19
20
  import { encodeChunkName } from './helpers/encode-chunk-name.js';
@@ -277,11 +278,9 @@ export async function createServerWorkspaceStore(workspaceProps) {
277
278
  /**
278
279
  * Adds an AsyncAPI document to the workspace.
279
280
  *
280
- * AsyncAPI keeps its content under `channels` and `operations` instead of `paths`, so none of the
281
- * OpenAPI externalization applies: there are no path operations to split into chunks, and the
282
- * consumers read channels and operations straight off the stored document. The document is
283
- * therefore kept whole, and only the AsyncAPI upgrader runs so 1.x/2.x documents reach the 3.x
284
- * shape the traversal and renderer expect.
281
+ * Channels, operations and components become global chunk references. References inside each
282
+ * chunk retain their document-relative pointers so shared and recursive dependencies resolve
283
+ * against the same root. The whole document remains available for server rendering.
285
284
  *
286
285
  * @param document - The AsyncAPI document to process and add
287
286
  * @param meta - The document name plus any metadata to merge onto the stored document
@@ -300,9 +299,6 @@ export async function createServerWorkspaceStore(workspaceProps) {
300
299
  // shapes like `channels: null` straight through to the browser. Merged rather than assigned, so
301
300
  // nothing the schema does not model is dropped.
302
301
  mergeObjects(asyncApiDocument, coerce(asyncApiObjectSchema, deepClone(asyncApiDocument)));
303
- // Nothing is externalized, so the document owns no chunks. The empty entry keeps `get()` and
304
- // chunk generation well defined for the document name.
305
- assets[name] = {};
306
302
  // Traversed before the spread below: its last act is a top-level `x-scalar-order` write on the
307
303
  // document, and a snapshot taken first would both miss it and preserve whatever stale order the
308
304
  // input arrived with.
@@ -312,19 +308,73 @@ export async function createServerWorkspaceStore(workspaceProps) {
312
308
  // traversal falls back to the map key — so the sidebar reads `planetCreated` where the
313
309
  // rendered page reads "Planet Created".
314
310
  resolveLocalReferences(asyncApiDocument), navigationOptions ?? workspaceProps.navigationOptions);
315
- workspace.documents[name] = {
311
+ const whole = {
316
312
  ...documentMeta,
317
313
  ...asyncApiDocument,
318
314
  'x-original-aas-version': originalAasVersion,
319
315
  [extensions.document.navigation]: navigation,
320
316
  };
321
- // A document carrying both discriminators is ingested as AsyncAPI here, but `getDocumentType`
322
- // checks OpenAPI first — so leaving `openapi` in place would hand an OpenAPI renderer a
323
- // navigation tree of channel entries. The document is stored as the type it was read as.
324
- delete workspace.documents[name]['openapi'];
325
- // Nothing was externalized, so the stored document already is the whole document; it only needs
326
- // its references resolving to be rendered from.
327
- resolvedDocuments[name] = resolveLocalReferences(workspace.documents[name]);
317
+ delete whole['openapi'];
318
+ const options = workspaceProps.mode === 'static'
319
+ ? { mode: 'static', name }
320
+ : { mode: 'ssr', name, baseUrl: workspaceProps.baseUrl };
321
+ const refs = chunkRefTemplates(options);
322
+ const asyncapi = {};
323
+ const resolvedComponents = getRaw(getResolvedRef(resolveLocalReferences(asyncApiDocument).components));
324
+ const groups = asyncApiChunkGroups({ ...asyncApiDocument, components: resolvedComponents });
325
+ const externalize = (section, entries = {}) => {
326
+ const chunks = {};
327
+ const references = Object.entries(entries).map(([key, value]) => {
328
+ const group = groups.get(`${section}/${key}`);
329
+ const filename = group ?? key;
330
+ if (group) {
331
+ const values = (chunks[escapeJsonPointer(group)] ??= {});
332
+ Object.defineProperty(values, key, { value, enumerable: true, configurable: true, writable: true });
333
+ }
334
+ else
335
+ Object.defineProperty(chunks, escapeJsonPointer(key), {
336
+ value,
337
+ enumerable: true,
338
+ configurable: true,
339
+ writable: true,
340
+ });
341
+ const fragment = group ? `#/${encodeURIComponent(escapeJsonPointer(key))}` : '#';
342
+ return [
343
+ key,
344
+ chunkReference(workspaceProps.mode === 'static'
345
+ ? `./chunks/${encodeChunkName(name)}/asyncapi/${section}/${encodeChunkName(filename)}.json${fragment}`
346
+ : `${workspaceProps.baseUrl}/${name}/asyncapi/${section}/${encodeURIComponent(escapeJsonPointer(filename))}${fragment}`),
347
+ ];
348
+ });
349
+ asyncapi[section] = chunks;
350
+ return Object.fromEntries(references);
351
+ };
352
+ const components = Object.fromEntries(Object.entries(resolvedComponents ?? {}).map(([type, entries]) => [
353
+ type,
354
+ type === 'servers' || type === 'securitySchemes'
355
+ ? entries
356
+ : externalize(`components-${type}`, entries),
357
+ ]));
358
+ asyncapi.models = Object.fromEntries(Object.entries(resolvedComponents?.schemas ?? {}).map(([key, value]) => [escapeJsonPointer(key), value]));
359
+ const channels = externalize('channels', asyncApiDocument.channels);
360
+ const operations = externalize('operations', asyncApiDocument.operations);
361
+ assets[name] = {
362
+ asyncapi,
363
+ ...(workspaceProps.compact ? { navigation } : {}),
364
+ };
365
+ workspace.documents[name] = {
366
+ ...whole,
367
+ components,
368
+ channels,
369
+ operations,
370
+ ...(workspaceProps.compact
371
+ ? {
372
+ [extensions.document.navigation]: navigationHeader(navigation),
373
+ [extensions.document.navigationChunk]: fillChunkRef(refs.navigation),
374
+ }
375
+ : {}),
376
+ };
377
+ resolvedDocuments[name] = resolveLocalReferences(whole);
328
378
  };
329
379
  /**
330
380
  * Adds a new document to the workspace.
@@ -479,11 +529,24 @@ export async function createServerWorkspaceStore(workspaceProps) {
479
529
  const basePath = resolve(workspaceProps.directory ?? DEFAULT_ASSETS_FOLDER);
480
530
  const writeChunk = await createChunkWriter(basePath);
481
531
  await writeChunk([WORKSPACE_FILE_NAME], workspace);
482
- for (const [name, { components, operations, navigation }] of Object.entries(assets)) {
532
+ for (const [name, { components, operations, navigation, asyncapi }] of Object.entries(assets)) {
483
533
  // Only compact documents chunk their navigation; otherwise it stays whole on the document.
484
534
  if (navigation !== undefined) {
485
535
  await writeChunk(['chunks', encodeChunkName(name), 'navigation.json'], navigation);
486
536
  }
537
+ if (asyncapi) {
538
+ for (const [section, entries] of Object.entries(asyncapi)) {
539
+ for (const [key, value] of Object.entries(entries)) {
540
+ await writeChunk([
541
+ 'chunks',
542
+ encodeChunkName(name),
543
+ 'asyncapi',
544
+ section,
545
+ `${encodeChunkName(unescapeJsonPointer(key))}.json`,
546
+ ], value);
547
+ }
548
+ }
549
+ }
487
550
  if (components) {
488
551
  for (const [type, component] of Object.entries(components)) {
489
552
  for (const [key, value] of Object.entries(component)) {
@@ -526,7 +589,10 @@ export async function createServerWorkspaceStore(workspaceProps) {
526
589
  }
527
590
  })();
528
591
  // Keep the path segments escaped cuz we store them on the filesystem as escaped sequences
529
- const path = parseJsonPointerSegments(pointerPath).map(escapeJsonPointer);
592
+ const segments = pointerPath.split('/').slice(1);
593
+ const path = segments[1] === 'asyncapi'
594
+ ? segments.map((segment) => escapeJsonPointer(decodeURIComponent(segment).replaceAll('~1', '/').replaceAll('~0', '~')))
595
+ : parseJsonPointerSegments(pointerPath).map(escapeJsonPointer);
530
596
  return getValueAtPath(assets, path);
531
597
  },
532
598
  addDocument,
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "openapi",
17
17
  "scalar"
18
18
  ],
19
- "version": "0.67.2",
19
+ "version": "0.68.1",
20
20
  "engines": {
21
21
  "node": ">=22"
22
22
  },
@@ -153,15 +153,15 @@
153
153
  "CHANGELOG.md"
154
154
  ],
155
155
  "dependencies": {
156
- "@scalar/asyncapi-upgrader": "0.1.14",
157
- "@scalar/helpers": "0.16.0",
158
- "@scalar/json-magic": "0.15.3",
159
- "@scalar/openapi-upgrader": "0.4.1",
160
- "@scalar/schemas": "0.12.2",
161
- "@scalar/snippetz": "0.10.4",
156
+ "@scalar/asyncapi-upgrader": "0.1.15",
157
+ "@scalar/helpers": "0.16.1",
158
+ "@scalar/json-magic": "0.15.5",
159
+ "@scalar/openapi-upgrader": "0.4.2",
160
+ "@scalar/schemas": "0.13.0",
161
+ "@scalar/snippetz": "0.10.6",
162
162
  "@scalar/typebox": "0.1.3",
163
- "@scalar/types": "0.22.3",
164
- "@scalar/validation": "0.6.5",
163
+ "@scalar/types": "0.23.0",
164
+ "@scalar/validation": "0.6.6",
165
165
  "js-base64": "^3.9.2",
166
166
  "type-fest": "^5.8.0",
167
167
  "vue": "^3.5.40",