@scalar/workspace-store 0.3.1 → 0.3.2

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 (51) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/client.js +1 -1
  3. package/dist/client.js.map +1 -1
  4. package/package.json +6 -2
  5. package/.turbo/turbo-build.log +0 -13
  6. package/esbuild.ts +0 -8
  7. package/src/client.test.ts +0 -409
  8. package/src/client.ts +0 -254
  9. package/src/helpers/general.ts +0 -30
  10. package/src/helpers/json-path-utils.test.ts +0 -13
  11. package/src/helpers/json-path-utils.ts +0 -38
  12. package/src/helpers/proxy.test.ts +0 -61
  13. package/src/helpers/proxy.ts +0 -213
  14. package/src/schemas/callback.ts +0 -13
  15. package/src/schemas/components.ts +0 -36
  16. package/src/schemas/contact.ts +0 -11
  17. package/src/schemas/discriminator.ts +0 -13
  18. package/src/schemas/encoding.ts +0 -17
  19. package/src/schemas/example.ts +0 -17
  20. package/src/schemas/external-documentation.ts +0 -9
  21. package/src/schemas/header.ts +0 -19
  22. package/src/schemas/info.ts +0 -23
  23. package/src/schemas/license.ts +0 -11
  24. package/src/schemas/link.ts +0 -24
  25. package/src/schemas/media-type.ts +0 -21
  26. package/src/schemas/oauth-flow.ts +0 -13
  27. package/src/schemas/oauthflows.ts +0 -16
  28. package/src/schemas/openapi-document.ts +0 -34
  29. package/src/schemas/operation-without-callback.ts +0 -37
  30. package/src/schemas/parameter.ts +0 -26
  31. package/src/schemas/path-item.ts +0 -35
  32. package/src/schemas/paths.ts +0 -11
  33. package/src/schemas/reference.ts +0 -20
  34. package/src/schemas/request-body.ts +0 -12
  35. package/src/schemas/response.ts +0 -16
  36. package/src/schemas/responses.ts +0 -14
  37. package/src/schemas/schema.ts +0 -26
  38. package/src/schemas/security-requirement.ts +0 -16
  39. package/src/schemas/security-scheme.ts +0 -58
  40. package/src/schemas/server-variable.ts +0 -11
  41. package/src/schemas/server-workspace.ts +0 -36
  42. package/src/schemas/server.ts +0 -12
  43. package/src/schemas/tag.ts +0 -12
  44. package/src/schemas/xml.ts +0 -19
  45. package/src/schemas.ts +0 -6
  46. package/src/server.test.ts +0 -470
  47. package/src/server.ts +0 -341
  48. package/test/helpers.ts +0 -16
  49. package/tsconfig.build.json +0 -12
  50. package/tsconfig.json +0 -8
  51. package/vite.config.ts +0 -9
package/src/server.ts DELETED
@@ -1,341 +0,0 @@
1
- import { escapeJsonPointer, upgrade } from '@scalar/openapi-parser'
2
- import type { OpenAPIV3_1 } from '@scalar/openapi-types'
3
- import { getValueByPath, parseJsonPointer } from './helpers/json-path-utils'
4
- import type { WorkspaceDocumentMeta, WorkspaceMeta } from './schemas/server-workspace'
5
- import fs from 'node:fs/promises'
6
- import { cwd } from 'node:process'
7
-
8
- const DEFAULT_ASSETS_FOLDER = 'assets'
9
- export const WORKSPACE_FILE_NAME = 'scalar-workspace.json'
10
-
11
- type CreateServerWorkspaceStore =
12
- | {
13
- directory?: string
14
- mode: 'static'
15
- documents: {
16
- name: string
17
- document: Record<string, unknown> | string
18
- meta?: WorkspaceDocumentMeta
19
- }[]
20
- meta?: WorkspaceMeta
21
- }
22
- | {
23
- baseUrl: string
24
- mode: 'ssr'
25
- documents: {
26
- name: string
27
- document: Record<string, unknown> | string
28
- meta?: WorkspaceDocumentMeta
29
- }[]
30
- meta?: WorkspaceMeta
31
- }
32
-
33
- const httpMethods = new Set(['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'])
34
-
35
- /**
36
- * Filters an OpenAPI PathsObject to only include standard HTTP methods.
37
- * Removes any vendor extensions or other non-HTTP properties.
38
- *
39
- * @param paths - The OpenAPI PathsObject to filter
40
- * @returns A new PathsObject containing only standard HTTP methods
41
- *
42
- * @example
43
- * Input: {
44
- * "/users": {
45
- * "get": {...},
46
- * "x-custom": {...},
47
- * "post": {...}
48
- * }
49
- * }
50
- * Output: {
51
- * "/users": {
52
- * "get": {...},
53
- * "post": {...}
54
- * }
55
- * }
56
- */
57
- export function filterHttpMethodsOnly(paths: OpenAPIV3_1.PathsObject) {
58
- const result: OpenAPIV3_1.PathsObject = {}
59
-
60
- for (const [path, methods] of Object.entries(paths)) {
61
- if (!methods) {
62
- continue
63
- }
64
-
65
- const filteredMethods: Record<string, any> = {}
66
-
67
- for (const [method, operation] of Object.entries(methods)) {
68
- if (httpMethods.has(method.toLowerCase())) {
69
- filteredMethods[method] = operation
70
- }
71
- }
72
-
73
- if (Object.keys(filteredMethods).length > 0) {
74
- result[path] = filteredMethods
75
- }
76
- }
77
-
78
- return result
79
- }
80
-
81
- /**
82
- * Escapes path keys in an OpenAPI PathsObject to be JSON Pointer compatible.
83
- * This is necessary because OpenAPI paths can contain characters that need to be escaped
84
- * when used as JSON Pointer references (like '/' and '~').
85
- *
86
- * @example
87
- * Input: { "/users/{id}": { ... } }
88
- * Output: { "/users~1{id}": { ... } }
89
- */
90
- export function escapePaths(paths: OpenAPIV3_1.PathsObject) {
91
- const result: OpenAPIV3_1.PathsObject = {}
92
- Object.keys(paths).forEach((path) => {
93
- result[escapeJsonPointer(path)] = paths[path]
94
- })
95
-
96
- return result
97
- }
98
-
99
- /**
100
- * Externalizes components by turning them into refs.
101
- */
102
- export function externalizeComponentReferences(
103
- document: OpenAPIV3_1.Document,
104
- meta: { mode: 'ssr'; name: string; baseUrl: string } | { mode: 'static'; name: string; directory: string },
105
- ) {
106
- const result: Record<string, any> = {}
107
-
108
- if (!document.components) {
109
- return result
110
- }
111
-
112
- Object.entries(document.components).forEach(([type, component]) => {
113
- result[type] = {}
114
- Object.keys(component).forEach((name) => {
115
- const ref =
116
- meta.mode === 'ssr'
117
- ? `${meta.baseUrl}/${meta.name}/components/${type}/${name}#`
118
- : `${meta.directory}/chunks/${meta.name}/components/${type}/${name}.json#`
119
-
120
- result[type][name] = { '$ref': ref, $global: true }
121
- })
122
- })
123
-
124
- return result
125
- }
126
-
127
- /**
128
- * Externalizes paths operations by turning them into refs.
129
- */
130
- export function externalizePathReferences(
131
- document: OpenAPIV3_1.Document,
132
- meta: { mode: 'ssr'; name: string; baseUrl: string } | { mode: 'static'; name: string; directory: string },
133
- ) {
134
- const result: Record<string, any> = {}
135
-
136
- if (!document.paths) {
137
- return result
138
- }
139
-
140
- Object.entries(document.paths).forEach(([path, pathItem]) => {
141
- if (!pathItem) {
142
- return result
143
- }
144
-
145
- result[path] = {}
146
-
147
- const escapedPath = escapeJsonPointer(path)
148
-
149
- Object.keys(pathItem).forEach((type) => {
150
- if (httpMethods.has(type)) {
151
- const ref =
152
- meta.mode === 'ssr'
153
- ? `${meta.baseUrl}/${meta.name}/operations/${escapedPath}/${type}#`
154
- : `${meta.directory}/chunks/${meta.name}/operations/${escapedPath}/${type}.json#`
155
-
156
- result[path][type] = { '$ref': ref, $global: true }
157
- } else {
158
- result[path][type] = pathItem[type]
159
- }
160
- })
161
- })
162
-
163
- return result
164
- }
165
-
166
- /**
167
- * Create server state workspace store
168
- */
169
- export function createServerWorkspaceStore(workspaceProps: CreateServerWorkspaceStore) {
170
- const documents = workspaceProps.documents.map((el) => {
171
- const document = upgrade(el.document).specification
172
-
173
- return { ...el, document }
174
- })
175
-
176
- /**
177
- * A map of document chunks that can be loaded asynchronously by the client.
178
- * Each document is split into components and operations to enable lazy loading.
179
- * The keys are document names and values contain the components and operations
180
- * for that document.
181
- */
182
- const assets = documents.reduce<
183
- Record<string, { components?: OpenAPIV3_1.ComponentsObject; operations?: Record<string, unknown> }>
184
- >((acc, { name, document }) => {
185
- acc[name] = {
186
- components: document.components,
187
- operations: document.paths && escapePaths(filterHttpMethodsOnly(document.paths)),
188
- }
189
- return acc
190
- }, {})
191
-
192
- /**
193
- * Base workspace document containing essential metadata and document references.
194
- *
195
- * This workspace document provides the minimal information needed for initial rendering.
196
- * All components and path operations are replaced with references to enable lazy loading.
197
- *
198
- * In SSR mode, references point to API endpoints.
199
- * In static mode, references point to filesystem chunks.
200
- */
201
- const workspace = {
202
- ...workspaceProps.meta,
203
- documents: documents.reduce<Record<string, Record<string, unknown>>>((acc, { name, document, meta }) => {
204
- const options =
205
- workspaceProps.mode === 'ssr'
206
- ? { mode: workspaceProps.mode, name, baseUrl: workspaceProps.baseUrl }
207
- : { mode: workspaceProps.mode, name, directory: workspaceProps.directory ?? DEFAULT_ASSETS_FOLDER }
208
-
209
- // Transform the original document by setting all the components and paths operations on refs
210
- const components = externalizeComponentReferences(document, options)
211
- const paths = externalizePathReferences(document, options)
212
-
213
- acc[name] = { ...meta, ...document, components, paths }
214
- return acc
215
- }, {}),
216
- }
217
-
218
- return {
219
- /**
220
- * Generates workspace chunks by writing components and operations to the filesystem.
221
- *
222
- * This method is only available in static mode. It creates a directory structure containing:
223
- * - A workspace file with metadata and document references
224
- * - Component chunks split by type (schemas, parameters, etc)
225
- * - Operation chunks split by path and HTTP method
226
- *
227
- * The generated workspace references will be relative file paths pointing to these chunks.
228
- *
229
- * @throws {Error} If called when mode is not 'static'
230
- */
231
- generateWorkspaceChunks: async () => {
232
- if (workspaceProps.mode !== 'static') {
233
- throw 'Mode has to be set to `static` to generate filesystem workspace chunks'
234
- }
235
-
236
- // Write the workspace document
237
- const basePath = `${cwd()}/${workspaceProps.directory ?? DEFAULT_ASSETS_FOLDER}`
238
- await fs.mkdir(basePath, { recursive: true })
239
-
240
- // Write the workspace contents on the file system
241
- await fs.writeFile(`${basePath}/${WORKSPACE_FILE_NAME}`, JSON.stringify(workspace))
242
-
243
- // Write the chunks
244
- for (const [name, { components, operations }] of Object.entries(assets)) {
245
- // Write the components chunks
246
- if (components) {
247
- for (const [type, component] of Object.entries(components as Record<string, Record<string, unknown>>)) {
248
- const componentPath = `${basePath}/chunks/${name}/components/${type}`
249
- await fs.mkdir(componentPath, { recursive: true })
250
-
251
- for (const [key, value] of Object.entries(component)) {
252
- await fs.writeFile(`${componentPath}/${key}.json`, JSON.stringify(value))
253
- }
254
- }
255
- }
256
-
257
- // Write the operations chunks
258
- if (operations) {
259
- for (const [path, methods] of Object.entries(operations as Record<string, Record<string, unknown>>)) {
260
- const operationPath = `${basePath}/chunks/${name}/operations/${path}`
261
- await fs.mkdir(operationPath, { recursive: true })
262
-
263
- for (const [method, operation] of Object.entries(methods)) {
264
- await fs.writeFile(`${operationPath}/${method}.json`, JSON.stringify(operation))
265
- }
266
- }
267
- }
268
- }
269
- },
270
- /**
271
- * Returns the workspace document containing metadata and all sparse documents.
272
- *
273
- * The workspace document includes:
274
- * - Global workspace metadata (theme, active document, etc)
275
- * - Document metadata and sparse document
276
- * - In SSR mode: References point to in-memory chunks
277
- * - In static mode: References point to filesystem chunks
278
- *
279
- * @returns The complete workspace document
280
- */
281
- getWorkspace: () => {
282
- return workspace
283
- },
284
- /**
285
- * Retrieves a chunk of data from the workspace using a JSON Pointer
286
- *
287
- * A JSON Pointer is a string that references a specific location in a JSON document.
288
- * Only components and operations chunks can be retrieved.
289
- *
290
- * @example
291
- * ```ts
292
- * // Get a component
293
- * get('#/document-name/components/schemas/User')
294
- *
295
- * // Get an operation
296
- * get('#/document-name/operations/pets/get')
297
- * ```
298
- *
299
- * @param pointer - The JSON Pointer string to locate the chunk
300
- * @returns The chunk data if found, undefined otherwise
301
- */
302
- get: (pointer: string) => {
303
- return getValueByPath(assets, parseJsonPointer(pointer))
304
- },
305
- /**
306
- * Adds a new document to the workspace.
307
- *
308
- * The document will be:
309
- * - Upgraded to OpenAPI 3.1 if needed
310
- * - Split into components and operations chunks
311
- * - Have its references externalized based on the workspace mode
312
- * - Added to the workspace with its metadata
313
- *
314
- * @param document - The OpenAPI document to add
315
- * @param meta - Document metadata including required name and optional settings
316
- */
317
- addDocument: (document: Record<string, unknown>, meta: { name: string } & WorkspaceDocumentMeta) => {
318
- const { name, ...documentMeta } = meta
319
-
320
- const documentV3 = upgrade(document).specification
321
-
322
- // add the assets
323
- assets[meta.name] = {
324
- components: documentV3.components,
325
- operations: documentV3.paths && escapePaths(filterHttpMethodsOnly(documentV3.paths)),
326
- }
327
-
328
- const options =
329
- workspaceProps.mode === 'ssr'
330
- ? { mode: workspaceProps.mode, name, baseUrl: workspaceProps.baseUrl }
331
- : { mode: workspaceProps.mode, name, directory: workspaceProps.directory ?? DEFAULT_ASSETS_FOLDER }
332
-
333
- const components = externalizeComponentReferences(documentV3, options)
334
- const paths = externalizePathReferences(documentV3, options)
335
-
336
- // The document is now a minimal version with externalized references to components and operations.
337
- // These references will be resolved asynchronously when needed through the workspace's get() method.
338
- workspace.documents[meta.name] = { ...documentMeta, ...documentV3, components, paths }
339
- },
340
- }
341
- }
package/test/helpers.ts DELETED
@@ -1,16 +0,0 @@
1
- import fs from 'node:fs/promises'
2
-
3
- export async function allFilesMatch(fileList: { path: string; content: string }[]): Promise<boolean> {
4
- for (const { content, path } of fileList) {
5
- try {
6
- const actualContent = await fs.readFile(path, 'utf8')
7
- if (actualContent !== content) {
8
- return false
9
- }
10
- } catch {
11
- // If file doesn't exist or any other read error
12
- return false
13
- }
14
- }
15
- return true
16
- }
@@ -1,12 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "include": ["src"],
4
- "exclude": ["**/*.test.ts", "**/*.test-d.ts"],
5
- "compilerOptions": {
6
- "skipLibCheck": true,
7
- "declaration": true,
8
- "declarationMap": true,
9
- "emitDeclarationOnly": true,
10
- "outDir": "dist/"
11
- }
12
- }
package/tsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "paths": {
5
- "@/*": ["./src/*"]
6
- }
7
- }
8
- }
package/vite.config.ts DELETED
@@ -1,9 +0,0 @@
1
- import { alias } from '@scalar/build-tooling'
2
- import { defineConfig } from 'vite'
3
-
4
- export default defineConfig({
5
- plugins: [],
6
- resolve: {
7
- alias: alias(import.meta.url),
8
- },
9
- })