@scalar/workspace-store 0.10.2 → 0.12.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 (45) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +115 -3
  3. package/dist/client.d.ts +14 -7
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +175 -69
  6. package/dist/client.js.map +3 -3
  7. package/dist/helpers/apply-selective-updates.d.ts +1 -1
  8. package/dist/helpers/apply-selective-updates.d.ts.map +1 -1
  9. package/dist/helpers/apply-selective-updates.js +1 -1
  10. package/dist/helpers/apply-selective-updates.js.map +1 -1
  11. package/dist/helpers/overrides-proxy.d.ts +5 -0
  12. package/dist/helpers/overrides-proxy.d.ts.map +1 -0
  13. package/dist/helpers/overrides-proxy.js +48 -0
  14. package/dist/helpers/overrides-proxy.js.map +7 -0
  15. package/dist/mutators/index.js +1 -1
  16. package/dist/mutators/index.js.map +2 -2
  17. package/dist/navigation/helpers/traverse-schemas.js +2 -2
  18. package/dist/navigation/helpers/traverse-schemas.js.map +2 -2
  19. package/dist/navigation/helpers/traverse-tags.js +2 -2
  20. package/dist/navigation/helpers/traverse-tags.js.map +2 -2
  21. package/dist/schemas/inmemory-workspace.d.ts +5633 -0
  22. package/dist/schemas/inmemory-workspace.d.ts.map +1 -1
  23. package/dist/schemas/inmemory-workspace.js +3 -1
  24. package/dist/schemas/inmemory-workspace.js.map +2 -2
  25. package/dist/schemas/reference-config/index.d.ts +2 -2
  26. package/dist/schemas/reference-config/index.d.ts.map +1 -1
  27. package/dist/schemas/reference-config/index.js.map +2 -2
  28. package/dist/schemas/typebox-types.d.ts +14 -0
  29. package/dist/schemas/typebox-types.d.ts.map +1 -0
  30. package/dist/schemas/typebox-types.js +19 -0
  31. package/dist/schemas/typebox-types.js.map +7 -0
  32. package/dist/schemas/v3.1/type-guard.d.ts +4 -2
  33. package/dist/schemas/v3.1/type-guard.d.ts.map +1 -1
  34. package/dist/schemas/v3.1/type-guard.js +3 -1
  35. package/dist/schemas/v3.1/type-guard.js.map +2 -2
  36. package/dist/server.d.ts.map +1 -1
  37. package/dist/server.js +6 -4
  38. package/dist/server.js.map +2 -2
  39. package/dist/types.d.ts +6 -39
  40. package/dist/types.d.ts.map +1 -1
  41. package/package.json +18 -9
  42. package/dist/helpers/proxy.d.ts +0 -63
  43. package/dist/helpers/proxy.d.ts.map +0 -1
  44. package/dist/helpers/proxy.js +0 -103
  45. package/dist/helpers/proxy.js.map +0 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @scalar/workspace-store
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 952bde2: feat(json-magic): move json tooling to the new package
8
+ - ae8d1b9: feat(workspace-store): rebase document origin with a remote origin
9
+ - 2888e18: feat(openapi-parser): partial bundle to a depth
10
+
11
+ ### Patch Changes
12
+
13
+ - 5301a80: feat: make content reactive and update workspace store
14
+ - 8199955: fix(workspace-store): never write overrides back to the intermediate state
15
+ - Updated dependencies [952bde2]
16
+ - Updated dependencies [2888e18]
17
+ - @scalar/openapi-parser@0.19.0
18
+ - @scalar/json-magic@0.1.0
19
+
20
+ ## 0.11.0
21
+
22
+ ### Minor Changes
23
+
24
+ - 9924c47: feat(workspace-store): support replacing the whole document
25
+ - a0c92d9: feat(workspace-store): create store from workspace specification object
26
+
27
+ ### Patch Changes
28
+
29
+ - a5534e6: fix: show path parameters on operation
30
+ - 6b6c72c: fix: hiddenClients: true and move clients to workspace store
31
+ - Updated dependencies [ccf875a]
32
+ - Updated dependencies [d4cb86b]
33
+ - Updated dependencies [94d6d0c]
34
+ - Updated dependencies [c345d2c]
35
+ - Updated dependencies [c0d6793]
36
+ - Updated dependencies [f3d0216]
37
+ - @scalar/openapi-types@0.3.7
38
+ - @scalar/types@0.2.11
39
+ - @scalar/code-highlight@0.1.9
40
+ - @scalar/openapi-parser@0.18.3
41
+ - @scalar/helpers@0.0.7
42
+
3
43
  ## 0.10.2
4
44
 
5
45
  ### Patch Changes
package/README.md CHANGED
@@ -196,7 +196,7 @@ const store = createWorkspaceStore({
196
196
  })
197
197
 
198
198
  // Add another OpenAPI document to the workspace
199
- store.addDocumentSync({
199
+ await store.addDocument({
200
200
  document: {
201
201
  info: {
202
202
  title: 'OpenApi document',
@@ -261,7 +261,7 @@ const store = createWorkspaceStore({
261
261
  You can override specific document configuration when you add the document to the store
262
262
 
263
263
  ```ts
264
- store.addDocumentSync({
264
+ await store.addDocument({
265
265
  name: 'example',
266
266
  document: {
267
267
  openapi: '3.0.0',
@@ -276,7 +276,7 @@ store.addDocumentSync({
276
276
  })
277
277
  ```
278
278
 
279
- To get the active document configuration you can use config getter\
279
+ To get the active document configuration you can use config getter
280
280
 
281
281
  ```ts
282
282
  // Get the configuration for the active document
@@ -368,4 +368,116 @@ const currentWorkspaceState = client.exportWorkspace()
368
368
 
369
369
  // Reload the workspace state
370
370
  client.loadWorkspace(currentWorkspaceState)
371
+ ```
372
+
373
+ ### Replace the Entire Document
374
+
375
+ When you have a new or updated OpenAPI document and want to overwrite the existing one—regardless of which parts have changed—you can use the `replaceDocument` method. This method efficiently and atomically updates the entire document in place, ensuring that only the necessary changes are applied for optimal performance.
376
+
377
+ ```ts
378
+ const client = createWorkspaceStore({
379
+ documents: [
380
+ {
381
+ name: 'document-name',
382
+ document: {
383
+ openapi: '3.1.0',
384
+ info: {
385
+ title: 'Document Title',
386
+ version: '1.0.0',
387
+ },
388
+ paths: {},
389
+ components: {
390
+ schemas: {},
391
+ },
392
+ servers: [],
393
+ },
394
+ },
395
+ ],
396
+ })
397
+
398
+ // Update the document with the new changes
399
+ client.replaceDocument('document-name', {
400
+ openapi: '3.1.0',
401
+ info: {
402
+ title: 'Updated Document',
403
+ version: '1.0.0',
404
+ },
405
+ paths: {},
406
+ components: {
407
+ schemas: {},
408
+ },
409
+ servers: [],
410
+ })
411
+ ```
412
+
413
+ ### Create workspace from specification
414
+
415
+ Create the workspace from a specification object
416
+
417
+ ```ts
418
+ await store.importWorkspaceFromSpecification({
419
+ workspace: 'draft',
420
+ info: { title: 'My Workspace' },
421
+ documents: {
422
+ api: { $ref: '/examples/api.yaml' },
423
+ petstore: { $ref: '/examples/petstore.yaml' },
424
+ },
425
+ overrides: {
426
+ api: {
427
+ servers: [
428
+ {
429
+ url: 'http://localhost:9090',
430
+ },
431
+ ],
432
+ },
433
+ },
434
+ 'x-scalar-dark-mode': true,
435
+ })
436
+ ```
437
+
438
+ ### Override specific fields from the document and it's metadata
439
+
440
+ This feature is helpful when you want to override specific fields in a document without altering the original source. Overrides allow you to customize certain values in-memory, ensuring the original document remains unchanged.
441
+
442
+ ```ts
443
+ const store = createWorkspaceStore()
444
+ await store.addDocument({
445
+ name: 'default',
446
+ document: {
447
+ openapi: '3.1.0',
448
+ info: {
449
+ title: 'Document Title',
450
+ version: '1.0.0',
451
+ },
452
+ paths: {},
453
+ components: {
454
+ schemas: {},
455
+ },
456
+ servers: [],
457
+ },
458
+ // Override the servers field
459
+ overrides: {
460
+ servers: [
461
+ {
462
+ url: 'http://localhost:8080',
463
+ description: 'Default dev server'
464
+ }
465
+ ]
466
+ }
467
+ })
468
+ ```
469
+
470
+ When you override specific fields, those changes are applied only in-memory and will never be written back to the original document. The original source remains unchanged, and any modifications made through overrides are isolated to the current session.
471
+
472
+ ### Rebase document origin with the updated remote origin
473
+
474
+ Rebases a document in the workspace with a new origin, resolving conflicts if provided.
475
+
476
+ ```ts
477
+ // Example: Rebase a document with a new origin and resolve conflicts
478
+ const conflicts = store.rebaseDocument('api', newOriginDoc)
479
+ if (conflicts && conflicts.length > 0) {
480
+ // User resolves conflicts here...
481
+ store.rebaseDocument('api', newOriginDoc, userResolvedConflicts)
482
+ }
371
483
  ```
package/dist/client.d.ts CHANGED
@@ -1,7 +1,10 @@
1
+ import type { DeepPartial, DeepRequired } from './types.js';
1
2
  import { type createNavigationOptions } from './navigation/index.js';
2
- import type { Workspace, WorkspaceDocumentMeta, WorkspaceMeta } from './schemas/workspace.js';
3
+ import { type OpenApiDocument } from './schemas/v3.1/strict/openapi-document.js';
3
4
  import type { Config } from './schemas/workspace-specification/config.js';
4
- import type { DeepTransform } from './types.js';
5
+ import type { WorkspaceSpecification } from './schemas/workspace-specification/index.js';
6
+ import type { Workspace, WorkspaceDocumentMeta, WorkspaceMeta } from './schemas/workspace.js';
7
+ import { merge, type Difference } from '@scalar/json-magic/diff';
5
8
  /**
6
9
  * Input type for workspace document metadata and configuration.
7
10
  * This type defines the required and optional fields for initializing a document in the workspace.
@@ -15,6 +18,8 @@ type WorkspaceDocumentMetaInput = {
15
18
  name: string;
16
19
  /** Optional configuration for generating navigation structure */
17
20
  config?: Config & Partial<createNavigationOptions>;
21
+ /** Overrides for the document */
22
+ overrides?: DeepPartial<OpenApiDocument>;
18
23
  };
19
24
  /**
20
25
  * Represents a document that is loaded from a URL.
@@ -37,7 +42,7 @@ export type ObjectDoc = {
37
42
  * - ObjectDoc: Direct document object with metadata
38
43
  */
39
44
  export type WorkspaceDocumentInput = UrlDoc | ObjectDoc;
40
- declare const defaultConfig: DeepTransform<Config, 'NonNullable'>;
45
+ declare const defaultConfig: DeepRequired<Config>;
41
46
  /**
42
47
  * Configuration object for initializing a workspace store.
43
48
  * Defines the initial state and documents for the workspace.
@@ -45,8 +50,6 @@ declare const defaultConfig: DeepTransform<Config, 'NonNullable'>;
45
50
  type WorkspaceProps = {
46
51
  /** Optional metadata for the workspace including theme, active document, etc */
47
52
  meta?: WorkspaceMeta;
48
- /** In-mem open api documents. Async source documents (like URLs) can be loaded after initialization */
49
- documents?: ObjectDoc[];
50
53
  /** Workspace configuration */
51
54
  config?: Config;
52
55
  };
@@ -63,12 +66,12 @@ export type WorkspaceStore = {
63
66
  update<K extends keyof WorkspaceMeta>(key: K, value: WorkspaceMeta[K]): void;
64
67
  /** Updates a specific metadata field in a document */
65
68
  updateDocument<K extends keyof WorkspaceDocumentMeta>(name: 'active' | (string & {}), key: K, value: WorkspaceDocumentMeta[K]): void;
69
+ /** Replaces the content of a specific document in the workspace with the provided input */
70
+ replaceDocument(documentName: string, input: Record<string, unknown>): void;
66
71
  /** Resolves a reference in the active document by following the provided path and resolving any external $ref references */
67
72
  resolve(path: string[]): Promise<unknown>;
68
73
  /** Adds a new document to the workspace */
69
74
  addDocument(input: WorkspaceDocumentInput): Promise<void>;
70
- /** Similar to addDocument but requires and in-mem object to be provided and loads the document synchronously */
71
- addDocumentSync(input: ObjectDoc): void;
72
75
  /** Returns the merged configuration for the active document */
73
76
  readonly config: typeof defaultConfig;
74
77
  /** Downloads the specified document in the requested format */
@@ -83,6 +86,10 @@ export type WorkspaceStore = {
83
86
  exportWorkspace(): string;
84
87
  /** Imports a workspace from a serialized JSON string. */
85
88
  loadWorkspace(input: string): void;
89
+ /** Imports a workspace from a specification object */
90
+ importWorkspaceFromSpecification(specification: WorkspaceSpecification): Promise<void[]>;
91
+ /** Rebase document with a remote origin */
92
+ rebaseDocument: (documentName: string, newDocumentOrigin: Record<string, unknown>, resolvedConflicts?: Difference[]) => void | ReturnType<typeof merge>['conflicts'];
86
93
  };
87
94
  /**
88
95
  * Creates a reactive workspace store that manages documents and their metadata.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -1,18 +1,21 @@
1
- import { bundle, upgrade } from "@scalar/openapi-parser";
2
- import { fetchUrls } from "@scalar/openapi-parser/plugins-browser";
3
- import { reactive, toRaw } from "vue";
4
1
  import YAML from "yaml";
2
+ import { reactive } from "vue";
3
+ import { upgrade } from "@scalar/openapi-parser";
4
+ import { createMagicProxy, getRaw } from "@scalar/json-magic/magic-proxy";
5
5
  import { applySelectiveUpdates } from "./helpers/apply-selective-updates.js";
6
6
  import { deepClone, isObject, safeAssign } from "./helpers/general.js";
7
7
  import { getValueByPath } from "./helpers/json-path-utils.js";
8
8
  import { mergeObjects } from "./helpers/merge-object.js";
9
- import { createMagicProxy, getRaw } from "./helpers/proxy.js";
10
9
  import { createNavigation } from "./navigation/index.js";
11
10
  import { extensions } from "./schemas/extensions.js";
12
- import { InMemoryWorkspaceSchema } from "./schemas/inmemory-workspace.js";
13
- import { defaultReferenceConfig } from "./schemas/reference-config/index.js";
14
11
  import { coerceValue } from "./schemas/typebox-coerce.js";
15
12
  import { OpenAPIDocumentSchema } from "./schemas/v3.1/strict/openapi-document.js";
13
+ import { defaultReferenceConfig } from "./schemas/reference-config/index.js";
14
+ import { InMemoryWorkspaceSchema } from "./schemas/inmemory-workspace.js";
15
+ import { createOverridesProxy } from "./helpers/overrides-proxy.js";
16
+ import { bundle } from "@scalar/json-magic/bundle";
17
+ import { fetchUrls } from "@scalar/json-magic/bundle/plugins/browser";
18
+ import { apply, diff, merge } from "@scalar/json-magic/diff";
16
19
  const defaultConfig = {
17
20
  "x-scalar-reference-config": defaultReferenceConfig
18
21
  };
@@ -29,6 +32,7 @@ const createWorkspaceStore = (workspaceProps) => {
29
32
  const originalDocuments = {};
30
33
  const intermediateDocuments = {};
31
34
  const documentConfigs = {};
35
+ const overrides = {};
32
36
  const workspace = reactive({
33
37
  ...workspaceProps?.meta,
34
38
  documents: {},
@@ -47,18 +51,63 @@ const createWorkspaceStore = (workspaceProps) => {
47
51
  function getActiveDocumentName() {
48
52
  return workspace[extensions.workspace.activeDocument] ?? Object.keys(workspace.documents)[0] ?? "";
49
53
  }
50
- function addDocumentSync(input) {
54
+ function saveDocument(documentName) {
55
+ const intermediateDocument = intermediateDocuments[documentName];
56
+ const workspaceDocument = workspace.documents[documentName];
57
+ if (!workspaceDocument) {
58
+ return;
59
+ }
60
+ const updatedDocument = getRaw(workspaceDocument);
61
+ if (!intermediateDocument || !updatedDocument) {
62
+ return;
63
+ }
64
+ const excludedDiffs = applySelectiveUpdates(intermediateDocument, updatedDocument);
65
+ return excludedDiffs;
66
+ }
67
+ async function addInMemoryDocument(input) {
51
68
  const { name, meta } = input;
52
69
  const document = coerceValue(OpenAPIDocumentSchema, upgrade(input.document).specification);
53
70
  originalDocuments[name] = deepClone({ ...document, ...meta });
54
71
  intermediateDocuments[name] = deepClone({ ...document, ...meta });
55
72
  documentConfigs[name] = input.config ?? {};
73
+ overrides[name] = input.overrides ?? {};
56
74
  if (document[extensions.document.navigation] === void 0) {
57
75
  document[extensions.document.navigation] = createNavigation(document, input.config ?? {}).entries;
58
76
  }
59
- workspace.documents[name] = createMagicProxy({ ...document, ...meta });
77
+ if (document[extensions.document.navigation] === void 0) {
78
+ await bundle(input.document, { treeShake: false, plugins: [fetchUrls()] });
79
+ }
80
+ workspace.documents[name] = createOverridesProxy(createMagicProxy({ ...document, ...meta }), input.overrides);
81
+ }
82
+ async function addDocument(input) {
83
+ const { name, meta } = input;
84
+ const resolve = await loadDocument(input);
85
+ if (!resolve.ok) {
86
+ console.error(`Failed to fetch document '${name}': request was not successful`);
87
+ workspace.documents[name] = {
88
+ ...meta,
89
+ openapi: "3.1.0",
90
+ info: {
91
+ title: `Document '${name}' could not be loaded`,
92
+ version: "unknown"
93
+ }
94
+ };
95
+ return;
96
+ }
97
+ if (!isObject(resolve.data)) {
98
+ console.error(`Failed to load document '${name}': response data is not a valid object`);
99
+ workspace.documents[name] = {
100
+ ...meta,
101
+ openapi: "3.1.0",
102
+ info: {
103
+ title: `Document '${name}' could not be loaded`,
104
+ version: "unknown"
105
+ }
106
+ };
107
+ return;
108
+ }
109
+ await addInMemoryDocument({ ...input, document: resolve.data });
60
110
  }
61
- workspaceProps?.documents?.forEach(addDocumentSync);
62
111
  const visitedNodesCache = /* @__PURE__ */ new Set();
63
112
  return {
64
113
  /**
@@ -100,6 +149,30 @@ const createWorkspaceStore = (workspaceProps) => {
100
149
  }
101
150
  Object.assign(currentDocument, { [key]: value });
102
151
  },
152
+ /**
153
+ * Replaces the content of a specific document in the workspace with the provided input.
154
+ * This method computes the difference between the current document and the new input,
155
+ * then applies only the necessary changes in place. The updates are applied atomically,
156
+ * ensuring the document is updated in a single operation.
157
+ *
158
+ * @param documentName - The name of the document to update.
159
+ * @param input - The new content to apply to the document (as a plain object).
160
+ * @example
161
+ * // Replace the content of the 'api' document with new data
162
+ * store.replaceDocument('api', {
163
+ * openapi: '3.1.0',
164
+ * info: { title: 'Updated API', version: '1.0.1' },
165
+ * paths: {},
166
+ * })
167
+ */
168
+ replaceDocument(documentName, input) {
169
+ const currentDocument = workspace.documents[documentName];
170
+ if (!currentDocument) {
171
+ return console.error(`Document '${documentName}' does not exist in the workspace.`);
172
+ }
173
+ const newDocument = coerceValue(OpenAPIDocumentSchema, upgrade(input).specification);
174
+ applySelectiveUpdates(currentDocument, newDocument);
175
+ },
103
176
  /**
104
177
  * Resolves a reference in the active document by following the provided path and resolving any external $ref references.
105
178
  * This method traverses the document structure following the given path and resolves any $ref references it encounters.
@@ -154,54 +227,7 @@ const createWorkspaceStore = (workspaceProps) => {
154
227
  * }
155
228
  * })
156
229
  */
157
- addDocument: async (input) => {
158
- const { name, meta, config } = input;
159
- const resolve = await loadDocument(input);
160
- if (!resolve.ok) {
161
- console.error(`Failed to fetch document '${name}': request was not successful`);
162
- workspace.documents[name] = {
163
- ...meta,
164
- openapi: "3.1.0",
165
- info: {
166
- title: `Document '${name}' could not be loaded`,
167
- version: "unknown"
168
- }
169
- };
170
- return;
171
- }
172
- if (!isObject(resolve.data)) {
173
- console.error(`Failed to load document '${name}': response data is not a valid object`);
174
- workspace.documents[name] = {
175
- ...meta,
176
- openapi: "3.1.0",
177
- info: {
178
- title: `Document '${name}' could not be loaded`,
179
- version: "unknown"
180
- }
181
- };
182
- return;
183
- }
184
- addDocumentSync({ document: resolve.data, name, meta, config });
185
- },
186
- /**
187
- * Similar to addDocument but requires and in-mem object to be provided and loads the document synchronously
188
- * @param document - The document content to add. This should be a valid OpenAPI/Swagger document or other supported format
189
- * @param meta - Metadata for the document, including its name and other properties defined in WorkspaceDocumentMeta
190
- * @example
191
- * // Add a new OpenAPI document to the workspace
192
- * store.addDocument({
193
- * name: 'name',
194
- * document: {
195
- * openapi: '3.0.0',
196
- * info: { title: 'title' },
197
- * },
198
- * meta: {
199
- * 'x-scalar-active-auth': 'Bearer',
200
- * 'x-scalar-active-server': 'production'
201
- * }
202
- * })
203
- */
204
- addDocumentSync,
230
+ addDocument,
205
231
  /**
206
232
  * Returns the merged configuration for the active document.
207
233
  *
@@ -267,15 +293,7 @@ const createWorkspaceStore = (workspaceProps) => {
267
293
  * // Save the current state of the document named 'api'
268
294
  * const excludedDiffs = store.saveDocument('api')
269
295
  */
270
- saveDocument(documentName) {
271
- const intermediateDocument = intermediateDocuments[documentName];
272
- const updatedDocument = toRaw(getRaw(workspace.documents[documentName]));
273
- if (!intermediateDocument || !updatedDocument) {
274
- return;
275
- }
276
- const excludedDiffs = applySelectiveUpdates(intermediateDocument, updatedDocument);
277
- return excludedDiffs;
278
- },
296
+ saveDocument,
279
297
  /**
280
298
  * Restores the specified document to its last locally saved state.
281
299
  *
@@ -294,8 +312,12 @@ const createWorkspaceStore = (workspaceProps) => {
294
312
  * store.revertDocumentChanges('api')
295
313
  */
296
314
  revertDocumentChanges(documentName) {
315
+ const workspaceDocument = workspace.documents[documentName];
316
+ if (!workspaceDocument) {
317
+ return;
318
+ }
297
319
  const intermediateDocument = intermediateDocuments[documentName];
298
- const updatedDocument = getRaw(workspace.documents[documentName]);
320
+ const updatedDocument = getRaw(workspaceDocument);
299
321
  if (!intermediateDocument || !updatedDocument) {
300
322
  return;
301
323
  }
@@ -332,14 +354,15 @@ const createWorkspaceStore = (workspaceProps) => {
332
354
  name,
333
355
  // Extract the raw document data for export, removing any Vue reactivity wrappers.
334
356
  // When importing, the document can be wrapped again in a magic proxy.
335
- toRaw(getRaw(doc))
357
+ createOverridesProxy(getRaw(doc), overrides[name])
336
358
  ])
337
359
  )
338
360
  },
339
361
  meta: workspaceProps?.meta ?? {},
340
362
  documentConfigs,
341
363
  originalDocuments,
342
- intermediateDocuments
364
+ intermediateDocuments,
365
+ overrides
343
366
  });
344
367
  },
345
368
  /**
@@ -360,7 +383,90 @@ const createWorkspaceStore = (workspaceProps) => {
360
383
  safeAssign(originalDocuments, result.originalDocuments);
361
384
  safeAssign(intermediateDocuments, result.intermediateDocuments);
362
385
  safeAssign(documentConfigs, result.documentConfigs);
386
+ safeAssign(overrides, result.overrides);
363
387
  safeAssign(workspace, result.meta);
388
+ },
389
+ /**
390
+ * Imports a workspace from a WorkspaceSpecification object.
391
+ *
392
+ * This method assigns workspace metadata and adds all documents defined in the specification.
393
+ * Each document is added using its $ref and optional overrides.
394
+ *
395
+ * @example
396
+ * ```ts
397
+ * await store.importWorkspaceFromSpecification({
398
+ * documents: {
399
+ * api: { $ref: '/specs/api.yaml' },
400
+ * petstore: { $ref: '/specs/petstore.yaml' }
401
+ * },
402
+ * overrides: {
403
+ * api: { config: { features: { showModels: true } } }
404
+ * },
405
+ * info: { title: 'My Workspace' },
406
+ * workspace: 'v1',
407
+ * "x-scalar-dark-mode": true
408
+ * })
409
+ * ```
410
+ *
411
+ * @param specification - The workspace specification to import.
412
+ */
413
+ importWorkspaceFromSpecification: (specification) => {
414
+ const { documents, overrides: overrides2, info, workspace: workspaceVersion, ...meta } = specification;
415
+ safeAssign(workspace, meta);
416
+ return Promise.all(
417
+ Object.entries(documents ?? {}).map(
418
+ ([name, doc]) => addDocument({ url: doc.$ref, name, overrides: overrides2?.[name] })
419
+ )
420
+ );
421
+ },
422
+ /**
423
+ * Rebases a document in the workspace with a new origin, resolving conflicts if provided.
424
+ *
425
+ * This method is used to rebase a document (e.g., after pulling remote changes) by applying the changes
426
+ * from the new origin and merging them with local edits. If there are conflicts, they can be resolved
427
+ * by providing a list of resolved conflicts.
428
+ *
429
+ * @param documentName - The name of the document to rebase.
430
+ * @param newDocumentOrigin - The new origin document (as an object) to rebase onto.
431
+ * @param resolvedConflicts - (Optional) An array of resolved conflicts to apply.
432
+ * @returns If there are unresolved conflicts and no resolution is provided, returns the list of conflicts.
433
+ *
434
+ * @example
435
+ * // Example: Rebase a document with a new origin and resolve conflicts
436
+ * const conflicts = store.rebaseDocument('api', newOriginDoc)
437
+ * if (conflicts && conflicts.length > 0) {
438
+ * // User resolves conflicts here...
439
+ * store.rebaseDocument('api', newOriginDoc, userResolvedConflicts)
440
+ * }
441
+ */
442
+ rebaseDocument: (documentName, newDocumentOrigin, resolvedConflicts) => {
443
+ const newOrigin = coerceValue(OpenAPIDocumentSchema, upgrade(newDocumentOrigin).specification);
444
+ const originalDocument = originalDocuments[documentName];
445
+ const intermediateDocument = intermediateDocuments[documentName];
446
+ const activeDocument = workspace.documents[documentName] ? getRaw(workspace.documents[documentName]) : void 0;
447
+ if (!originalDocument || !intermediateDocument || !activeDocument) {
448
+ return console.error("[ERROR]: Specified document is missing or internal corrupted workspace state");
449
+ }
450
+ const changelogAA = diff(originalDocument, newOrigin);
451
+ const changelogAB = diff(originalDocument, intermediateDocument);
452
+ const changesA = merge(changelogAA, changelogAB);
453
+ if (resolvedConflicts === void 0) {
454
+ return changesA.conflicts;
455
+ }
456
+ const changesetA = changesA.diffs.concat(resolvedConflicts);
457
+ const newIntermediateDocument = apply(deepClone(originalDocument), changesetA);
458
+ intermediateDocuments[documentName] = newIntermediateDocument;
459
+ originalDocuments[documentName] = newOrigin;
460
+ const changelogBA = diff(intermediateDocument, newIntermediateDocument);
461
+ const changelogBB = diff(intermediateDocument, activeDocument);
462
+ const changesB = merge(changelogBA, changelogBB);
463
+ const changesetB = changesB.diffs.concat(changesB.conflicts.flatMap((it) => it[0]));
464
+ const newActiveDocument = apply(deepClone(newIntermediateDocument), changesetB);
465
+ workspace.documents[documentName] = createOverridesProxy(
466
+ createMagicProxy({ ...newActiveDocument }),
467
+ overrides[documentName]
468
+ );
469
+ return;
364
470
  }
365
471
  };
366
472
  };