@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.
- package/CHANGELOG.md +40 -0
- package/README.md +115 -3
- package/dist/client.d.ts +14 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +175 -69
- package/dist/client.js.map +3 -3
- package/dist/helpers/apply-selective-updates.d.ts +1 -1
- package/dist/helpers/apply-selective-updates.d.ts.map +1 -1
- package/dist/helpers/apply-selective-updates.js +1 -1
- package/dist/helpers/apply-selective-updates.js.map +1 -1
- package/dist/helpers/overrides-proxy.d.ts +5 -0
- package/dist/helpers/overrides-proxy.d.ts.map +1 -0
- package/dist/helpers/overrides-proxy.js +48 -0
- package/dist/helpers/overrides-proxy.js.map +7 -0
- package/dist/mutators/index.js +1 -1
- package/dist/mutators/index.js.map +2 -2
- package/dist/navigation/helpers/traverse-schemas.js +2 -2
- package/dist/navigation/helpers/traverse-schemas.js.map +2 -2
- package/dist/navigation/helpers/traverse-tags.js +2 -2
- package/dist/navigation/helpers/traverse-tags.js.map +2 -2
- package/dist/schemas/inmemory-workspace.d.ts +5633 -0
- package/dist/schemas/inmemory-workspace.d.ts.map +1 -1
- package/dist/schemas/inmemory-workspace.js +3 -1
- package/dist/schemas/inmemory-workspace.js.map +2 -2
- package/dist/schemas/reference-config/index.d.ts +2 -2
- package/dist/schemas/reference-config/index.d.ts.map +1 -1
- package/dist/schemas/reference-config/index.js.map +2 -2
- package/dist/schemas/typebox-types.d.ts +14 -0
- package/dist/schemas/typebox-types.d.ts.map +1 -0
- package/dist/schemas/typebox-types.js +19 -0
- package/dist/schemas/typebox-types.js.map +7 -0
- package/dist/schemas/v3.1/type-guard.d.ts +4 -2
- package/dist/schemas/v3.1/type-guard.d.ts.map +1 -1
- package/dist/schemas/v3.1/type-guard.js +3 -1
- package/dist/schemas/v3.1/type-guard.js.map +2 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +6 -4
- package/dist/server.js.map +2 -2
- package/dist/types.d.ts +6 -39
- package/dist/types.d.ts.map +1 -1
- package/package.json +18 -9
- package/dist/helpers/proxy.d.ts +0 -63
- package/dist/helpers/proxy.d.ts.map +0 -1
- package/dist/helpers/proxy.js +0 -103
- 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.
|
|
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.
|
|
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
|
|
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 {
|
|
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:
|
|
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.
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
};
|