@scalar/json-magic 0.8.2 → 0.8.4
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 +20 -0
- package/dist/bundle/index.d.ts +1 -0
- package/dist/bundle/index.d.ts.map +1 -1
- package/dist/bundle/index.js.map +1 -1
- package/dist/bundle/plugins/browser.js.map +1 -1
- package/dist/bundle/plugins/node.d.ts +1 -1
- package/dist/bundle/plugins/node.js +1 -1
- package/dist/bundle/plugins/node.js.map +1 -1
- package/dist/dereference/index.d.ts.map +1 -1
- package/dist/dereference/index.js.map +2 -2
- package/dist/diff/index.d.ts +1 -1
- package/dist/diff/index.d.ts.map +1 -1
- package/dist/diff/index.js +1 -1
- package/dist/diff/index.js.map +2 -2
- package/dist/helpers/escape-json-pointer.d.ts +1 -1
- package/dist/helpers/escape-json-pointer.js.map +1 -1
- package/dist/magic-proxy/index.d.ts.map +1 -1
- package/dist/magic-proxy/index.js.map +2 -2
- package/dist/magic-proxy/proxy.d.ts +0 -1
- package/dist/magic-proxy/proxy.d.ts.map +1 -1
- package/dist/magic-proxy/proxy.js +1 -2
- package/dist/magic-proxy/proxy.js.map +2 -2
- package/package.json +12 -13
- package/.turbo/turbo-build.log +0 -10
- package/esbuild.ts +0 -15
- package/src/bundle/bundle.test.ts +0 -2917
- package/src/bundle/bundle.ts +0 -916
- package/src/bundle/create-limiter.test.ts +0 -28
- package/src/bundle/create-limiter.ts +0 -52
- package/src/bundle/index.ts +0 -3
- package/src/bundle/plugins/browser.ts +0 -4
- package/src/bundle/plugins/fetch-urls/index.test.ts +0 -141
- package/src/bundle/plugins/fetch-urls/index.ts +0 -105
- package/src/bundle/plugins/node.ts +0 -5
- package/src/bundle/plugins/parse-json/index.test.ts +0 -24
- package/src/bundle/plugins/parse-json/index.ts +0 -32
- package/src/bundle/plugins/parse-yaml/index.test.ts +0 -26
- package/src/bundle/plugins/parse-yaml/index.ts +0 -34
- package/src/bundle/plugins/read-files/index.test.ts +0 -36
- package/src/bundle/plugins/read-files/index.ts +0 -58
- package/src/bundle/value-generator.test.ts +0 -165
- package/src/bundle/value-generator.ts +0 -143
- package/src/dereference/dereference.test.ts +0 -142
- package/src/dereference/dereference.ts +0 -84
- package/src/dereference/index.ts +0 -2
- package/src/diff/apply.test.ts +0 -262
- package/src/diff/apply.ts +0 -83
- package/src/diff/diff.test.ts +0 -328
- package/src/diff/diff.ts +0 -93
- package/src/diff/index.test.ts +0 -150
- package/src/diff/index.ts +0 -5
- package/src/diff/merge.test.ts +0 -1109
- package/src/diff/merge.ts +0 -136
- package/src/diff/trie.test.ts +0 -30
- package/src/diff/trie.ts +0 -113
- package/src/diff/utils.test.ts +0 -169
- package/src/diff/utils.ts +0 -111
- package/src/helpers/convert-to-local-ref.test.ts +0 -211
- package/src/helpers/convert-to-local-ref.ts +0 -43
- package/src/helpers/escape-json-pointer.test.ts +0 -13
- package/src/helpers/escape-json-pointer.ts +0 -8
- package/src/helpers/get-schemas.test.ts +0 -356
- package/src/helpers/get-schemas.ts +0 -80
- package/src/helpers/get-segments-from-path.test.ts +0 -17
- package/src/helpers/get-segments-from-path.ts +0 -17
- package/src/helpers/get-value-by-path.test.ts +0 -338
- package/src/helpers/get-value-by-path.ts +0 -44
- package/src/helpers/is-json-object.ts +0 -31
- package/src/helpers/is-object.test.ts +0 -27
- package/src/helpers/is-object.ts +0 -4
- package/src/helpers/is-yaml.ts +0 -18
- package/src/helpers/json-path-utils.test.ts +0 -57
- package/src/helpers/json-path-utils.ts +0 -50
- package/src/helpers/normalize.test.ts +0 -92
- package/src/helpers/normalize.ts +0 -35
- package/src/helpers/unescape-json-pointer.test.ts +0 -23
- package/src/helpers/unescape-json-pointer.ts +0 -9
- package/src/magic-proxy/index.ts +0 -2
- package/src/magic-proxy/proxy.test.ts +0 -1987
- package/src/magic-proxy/proxy.ts +0 -323
- package/src/types.ts +0 -1
- package/tsconfig.build.json +0 -12
- package/tsconfig.json +0 -16
- package/vite.config.ts +0 -8
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { createLimiter } from './create-limiter'
|
|
3
|
-
|
|
4
|
-
describe('createLimiter', { timeout: 10000 }, () => {
|
|
5
|
-
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
6
|
-
|
|
7
|
-
it('run in order and no more than the specified number of concurrent requests', async () => {
|
|
8
|
-
const limiter = createLimiter(2)
|
|
9
|
-
|
|
10
|
-
let active = 0
|
|
11
|
-
const maxObserved: number[] = []
|
|
12
|
-
|
|
13
|
-
const makeTask = (id: number) =>
|
|
14
|
-
limiter(async () => {
|
|
15
|
-
active++
|
|
16
|
-
maxObserved.push(active)
|
|
17
|
-
await sleep(100)
|
|
18
|
-
active--
|
|
19
|
-
return id
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
const tasks = [1, 2, 3, 4, 5].map(makeTask)
|
|
23
|
-
const results = await Promise.all(tasks)
|
|
24
|
-
|
|
25
|
-
expect(results).toEqual([1, 2, 3, 4, 5])
|
|
26
|
-
expect(Math.max(...maxObserved)).toBeLessThanOrEqual(2)
|
|
27
|
-
})
|
|
28
|
-
})
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a function that limits the number of concurrent executions of async functions.
|
|
3
|
-
*
|
|
4
|
-
* @param maxConcurrent - Maximum number of concurrent executions allowed
|
|
5
|
-
* @returns A function that wraps async functions to limit their concurrent execution
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* const limiter = createLimiter(2) // Allow max 2 concurrent executions
|
|
10
|
-
*
|
|
11
|
-
* // These will run with max 2 at a time
|
|
12
|
-
* const results = await Promise.all([
|
|
13
|
-
* limiter(() => fetch('/api/1')),
|
|
14
|
-
* limiter(() => fetch('/api/2')),
|
|
15
|
-
* limiter(() => fetch('/api/3')),
|
|
16
|
-
* limiter(() => fetch('/api/4'))
|
|
17
|
-
* ])
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export function createLimiter(maxConcurrent: number) {
|
|
21
|
-
let activeCount = 0
|
|
22
|
-
const queue: (() => void)[] = []
|
|
23
|
-
|
|
24
|
-
const next = () => {
|
|
25
|
-
if (queue.length === 0 || activeCount >= maxConcurrent) {
|
|
26
|
-
return
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const resolve = queue.shift()
|
|
30
|
-
|
|
31
|
-
if (resolve) {
|
|
32
|
-
resolve()
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const run = async <T>(fn: () => Promise<T>): Promise<T> => {
|
|
37
|
-
if (activeCount >= maxConcurrent) {
|
|
38
|
-
await new Promise<void>((resolve) => queue.push(resolve))
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
activeCount++
|
|
42
|
-
try {
|
|
43
|
-
const result = await fn()
|
|
44
|
-
return result
|
|
45
|
-
} finally {
|
|
46
|
-
activeCount--
|
|
47
|
-
next()
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return run
|
|
52
|
-
}
|
package/src/bundle/index.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { type FastifyInstance, fastify } from 'fastify'
|
|
2
|
-
import { assert, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import { fetchUrl } from '.'
|
|
5
|
-
|
|
6
|
-
describe('fetchUrl', () => {
|
|
7
|
-
const noLimit = <T>(fn: () => Promise<T>) => fn()
|
|
8
|
-
|
|
9
|
-
let server: FastifyInstance
|
|
10
|
-
const PORT = 7291
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
server = fastify({ logger: false })
|
|
14
|
-
|
|
15
|
-
return async () => {
|
|
16
|
-
await server.close()
|
|
17
|
-
}
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('reads json response', async () => {
|
|
21
|
-
const url = `http://localhost:${PORT}`
|
|
22
|
-
|
|
23
|
-
const response = {
|
|
24
|
-
message: '200OK',
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
server.get('/', (_, reply) => {
|
|
28
|
-
reply.send(response)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
await server.listen({ port: PORT })
|
|
32
|
-
|
|
33
|
-
const result = await fetchUrl(url, noLimit)
|
|
34
|
-
|
|
35
|
-
expect(result.ok).toBe(true)
|
|
36
|
-
assert(result.ok === true)
|
|
37
|
-
expect(result.data).toEqual(response)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('reads yaml response', async () => {
|
|
41
|
-
const url = `http://localhost:${PORT}`
|
|
42
|
-
|
|
43
|
-
server.get('/', (_, reply) => {
|
|
44
|
-
reply.header('content-type', 'application/yml').send('a: a')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
await server.listen({ port: PORT })
|
|
48
|
-
|
|
49
|
-
const result = await fetchUrl(url, noLimit)
|
|
50
|
-
|
|
51
|
-
expect(result.ok).toBe(true)
|
|
52
|
-
assert(result.ok === true)
|
|
53
|
-
expect(result.data).toEqual({ a: 'a' })
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('returns error on non-200 response', async () => {
|
|
57
|
-
const url = `http://localhost:${PORT}`
|
|
58
|
-
|
|
59
|
-
server.get('/', (_, reply) => {
|
|
60
|
-
reply.status(404).send()
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
await server.listen({ port: PORT })
|
|
64
|
-
|
|
65
|
-
const result = await fetchUrl(url, noLimit)
|
|
66
|
-
|
|
67
|
-
expect(result.ok).toBe(false)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('send headers to the specified domain', async () => {
|
|
71
|
-
const url = `http://localhost:${PORT}`
|
|
72
|
-
const headersSpy = vi.fn()
|
|
73
|
-
|
|
74
|
-
const response = {
|
|
75
|
-
message: '200OK',
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
server.get('/', (request, reply) => {
|
|
79
|
-
headersSpy(request.headers)
|
|
80
|
-
reply.send(response)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
await server.listen({ port: PORT })
|
|
84
|
-
await fetchUrl(url, noLimit, {
|
|
85
|
-
headers: [{ headers: { 'Authorization': 'Bearer <TOKEN>' }, domains: [`localhost:${PORT}`] }],
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
expect(headersSpy).toHaveBeenCalledOnce()
|
|
89
|
-
expect(headersSpy).toHaveBeenCalledWith({
|
|
90
|
-
'accept': '*/*',
|
|
91
|
-
'accept-encoding': 'gzip, deflate',
|
|
92
|
-
'accept-language': '*',
|
|
93
|
-
'authorization': 'Bearer <TOKEN>',
|
|
94
|
-
'connection': 'keep-alive',
|
|
95
|
-
'host': `localhost:${PORT}`,
|
|
96
|
-
'sec-fetch-mode': 'cors',
|
|
97
|
-
'user-agent': 'node',
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('does not send headers to other domains', async () => {
|
|
102
|
-
const url = `http://localhost:${PORT}`
|
|
103
|
-
const headersSpy = vi.fn()
|
|
104
|
-
|
|
105
|
-
const response = {
|
|
106
|
-
message: '200OK',
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
server.get('/', (request, reply) => {
|
|
110
|
-
headersSpy(request.headers)
|
|
111
|
-
reply.send(response)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
await server.listen({ port: PORT })
|
|
115
|
-
await fetchUrl(url, noLimit, {
|
|
116
|
-
headers: [{ headers: { 'Authorization': 'Bearer <TOKEN>' }, domains: ['localhost:9932', 'localhost'] }],
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
expect(headersSpy).toHaveBeenCalledOnce()
|
|
120
|
-
expect(headersSpy).toHaveBeenNthCalledWith(1, {
|
|
121
|
-
'accept': '*/*',
|
|
122
|
-
'accept-encoding': 'gzip, deflate',
|
|
123
|
-
'accept-language': '*',
|
|
124
|
-
'connection': 'keep-alive',
|
|
125
|
-
'host': `localhost:${PORT}`,
|
|
126
|
-
'sec-fetch-mode': 'cors',
|
|
127
|
-
'user-agent': 'node',
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('runs custom fetcher', async () => {
|
|
132
|
-
const customFetch = vi.fn().mockResolvedValue(new Response('{}', { status: 200 }))
|
|
133
|
-
|
|
134
|
-
await fetchUrl('https://example.com', (fn) => fn(), {
|
|
135
|
-
fetch: customFetch,
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
expect(customFetch).toHaveBeenCalledOnce()
|
|
139
|
-
expect(customFetch).toHaveBeenCalledWith('https://example.com', { headers: undefined })
|
|
140
|
-
})
|
|
141
|
-
})
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import type { LoaderPlugin, ResolveResult } from '@/bundle'
|
|
2
|
-
import { isRemoteUrl } from '@/bundle/bundle'
|
|
3
|
-
import { createLimiter } from '@/bundle/create-limiter'
|
|
4
|
-
import { normalize } from '@/helpers/normalize'
|
|
5
|
-
|
|
6
|
-
type FetchConfig = Partial<{
|
|
7
|
-
headers: { headers: HeadersInit; domains: string[] }[]
|
|
8
|
-
fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response>
|
|
9
|
-
}>
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Safely checks for host from a URL
|
|
13
|
-
* Needed because we cannot create a URL from a relative remote URL ex: examples/openapi.json
|
|
14
|
-
*/
|
|
15
|
-
const getHost = (url: string): string | null => {
|
|
16
|
-
try {
|
|
17
|
-
return new URL(url).host
|
|
18
|
-
} catch {
|
|
19
|
-
return null
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Fetches and normalizes data from a remote URL
|
|
25
|
-
* @param url - The URL to fetch data from
|
|
26
|
-
* @returns A promise that resolves to either the normalized data or an error result
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* const result = await fetchUrl('https://api.example.com/data.json')
|
|
30
|
-
* if (result.ok) {
|
|
31
|
-
* console.log(result.data) // The normalized data
|
|
32
|
-
* } else {
|
|
33
|
-
* console.log('Failed to fetch data')
|
|
34
|
-
* }
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
export async function fetchUrl(
|
|
38
|
-
url: string,
|
|
39
|
-
limiter: <T>(fn: () => Promise<T>) => Promise<T>,
|
|
40
|
-
config?: FetchConfig,
|
|
41
|
-
): Promise<ResolveResult> {
|
|
42
|
-
try {
|
|
43
|
-
const host = getHost(url)
|
|
44
|
-
|
|
45
|
-
// Get the headers that match the domain
|
|
46
|
-
const headers = config?.headers?.find((a) => a.domains.find((d) => d === host) !== undefined)?.headers
|
|
47
|
-
|
|
48
|
-
const exec = config?.fetch ?? fetch
|
|
49
|
-
|
|
50
|
-
const result = await limiter(() =>
|
|
51
|
-
exec(url, {
|
|
52
|
-
headers,
|
|
53
|
-
}),
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
if (result.ok) {
|
|
57
|
-
const body = await result.text()
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
ok: true,
|
|
61
|
-
data: normalize(body),
|
|
62
|
-
raw: body,
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const contentType = result.headers.get('Content-Type') ?? ''
|
|
67
|
-
|
|
68
|
-
// Warn if the content type is HTML or XML as we only support JSON/YAML
|
|
69
|
-
if (['text/html', 'application/xml'].includes(contentType)) {
|
|
70
|
-
console.warn(`[WARN] We only support JSON/YAML formats, received ${contentType}`)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
console.warn(`[WARN] Fetch failed with status ${result.status} ${result.statusText} for URL: ${url}`)
|
|
74
|
-
return {
|
|
75
|
-
ok: false,
|
|
76
|
-
}
|
|
77
|
-
} catch {
|
|
78
|
-
console.warn(`[WARN] Failed to parse JSON/YAML from URL: ${url}`)
|
|
79
|
-
return {
|
|
80
|
-
ok: false,
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Creates a plugin for handling remote URL references.
|
|
87
|
-
* This plugin validates and fetches data from HTTP/HTTPS URLs.
|
|
88
|
-
*
|
|
89
|
-
* @returns A plugin object with validate and exec functions
|
|
90
|
-
* @example
|
|
91
|
-
* const urlPlugin = fetchUrls()
|
|
92
|
-
* if (urlPlugin.validate('https://example.com/schema.json')) {
|
|
93
|
-
* const result = await urlPlugin.exec('https://example.com/schema.json')
|
|
94
|
-
* }
|
|
95
|
-
*/
|
|
96
|
-
export function fetchUrls(config?: FetchConfig & Partial<{ limit: number | null }>): LoaderPlugin {
|
|
97
|
-
// If there is a limit specified we limit the number of concurrent calls
|
|
98
|
-
const limiter = config?.limit ? createLimiter(config.limit) : <T>(fn: () => Promise<T>) => fn()
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
type: 'loader',
|
|
102
|
-
validate: isRemoteUrl,
|
|
103
|
-
exec: (value) => fetchUrl(value, limiter, config),
|
|
104
|
-
}
|
|
105
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { parseJson } from '@/bundle/plugins/parse-json'
|
|
4
|
-
|
|
5
|
-
describe('parse-json', () => {
|
|
6
|
-
it.each([
|
|
7
|
-
['{}', true],
|
|
8
|
-
['{ "a": "b" }', true],
|
|
9
|
-
['{ "a": 2 }', true],
|
|
10
|
-
["{ 'a': 2 }", false],
|
|
11
|
-
['{ some string', false],
|
|
12
|
-
['{ ', false],
|
|
13
|
-
])('should validate if strings are json valid format', (a, b) => {
|
|
14
|
-
expect(parseJson().validate(a)).toBe(b)
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('should parse json string', async () => {
|
|
18
|
-
expect(await parseJson().exec('{ "message": "Hello World" }')).toEqual({
|
|
19
|
-
ok: true,
|
|
20
|
-
data: { message: 'Hello World' },
|
|
21
|
-
'raw': '{ "message": "Hello World" }',
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
})
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { LoaderPlugin } from '@/bundle'
|
|
2
|
-
import { isJsonObject } from '@/helpers/is-json-object'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Creates a plugin that parses JSON strings into JavaScript objects.
|
|
6
|
-
* @returns A plugin object with validate and exec functions
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* const jsonPlugin = parseJson()
|
|
10
|
-
* const result = jsonPlugin.exec('{"name": "John", "age": 30}')
|
|
11
|
-
* // result = { name: 'John', age: 30 }
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
|
-
export function parseJson(): LoaderPlugin {
|
|
15
|
-
return {
|
|
16
|
-
type: 'loader',
|
|
17
|
-
validate: isJsonObject,
|
|
18
|
-
exec: (value) => {
|
|
19
|
-
try {
|
|
20
|
-
return Promise.resolve({
|
|
21
|
-
ok: true,
|
|
22
|
-
data: JSON.parse(value),
|
|
23
|
-
raw: value,
|
|
24
|
-
})
|
|
25
|
-
} catch {
|
|
26
|
-
return Promise.resolve({
|
|
27
|
-
ok: false,
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { parseYaml } from '@/bundle/plugins/parse-yaml'
|
|
4
|
-
|
|
5
|
-
describe('parse-yaml', () => {
|
|
6
|
-
it.each([
|
|
7
|
-
['hi: hello\n', true],
|
|
8
|
-
['- some: 1\n', true],
|
|
9
|
-
['valid: value\nhey: hi\n', true],
|
|
10
|
-
["{ 'a': 2 }", false],
|
|
11
|
-
['{ some string', false],
|
|
12
|
-
['{ ', false],
|
|
13
|
-
['{}', false],
|
|
14
|
-
['{ "json": "" }', false],
|
|
15
|
-
])('should validate if strings are yaml valid format', (a, b) => {
|
|
16
|
-
expect(parseYaml().validate(a)).toBe(b)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('should parse yaml string', async () => {
|
|
20
|
-
expect(await parseYaml().exec('{ "message": "Hello World" }')).toEqual({
|
|
21
|
-
ok: true,
|
|
22
|
-
data: { message: 'Hello World' },
|
|
23
|
-
'raw': '{ "message": "Hello World" }',
|
|
24
|
-
})
|
|
25
|
-
})
|
|
26
|
-
})
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import YAML from 'yaml'
|
|
2
|
-
|
|
3
|
-
import type { LoaderPlugin } from '@/bundle'
|
|
4
|
-
import { isYaml } from '@/helpers/is-yaml'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Creates a plugin that parses YAML strings into JavaScript objects.
|
|
8
|
-
* @returns A plugin object with validate and exec functions
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* const yamlPlugin = parseYaml()
|
|
12
|
-
* const result = yamlPlugin.exec('name: John\nage: 30')
|
|
13
|
-
* // result = { name: 'John', age: 30 }
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export function parseYaml(): LoaderPlugin {
|
|
17
|
-
return {
|
|
18
|
-
type: 'loader',
|
|
19
|
-
validate: isYaml,
|
|
20
|
-
exec: (value) => {
|
|
21
|
-
try {
|
|
22
|
-
return Promise.resolve({
|
|
23
|
-
ok: true,
|
|
24
|
-
data: YAML.parse(value, { merge: true, maxAliasCount: 10000 }),
|
|
25
|
-
raw: value,
|
|
26
|
-
})
|
|
27
|
-
} catch {
|
|
28
|
-
return Promise.resolve({
|
|
29
|
-
ok: false,
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto'
|
|
2
|
-
import fs from 'node:fs/promises'
|
|
3
|
-
|
|
4
|
-
import { assert, describe, expect, it } from 'vitest'
|
|
5
|
-
|
|
6
|
-
import { readFile } from '.'
|
|
7
|
-
|
|
8
|
-
describe('readFile', () => {
|
|
9
|
-
it('reads json contents of a file', async () => {
|
|
10
|
-
const contents = { message: 'ok' }
|
|
11
|
-
const path = randomUUID()
|
|
12
|
-
await fs.writeFile(path, JSON.stringify(contents))
|
|
13
|
-
|
|
14
|
-
const result = await readFile(path)
|
|
15
|
-
await fs.rm(path)
|
|
16
|
-
|
|
17
|
-
expect(result.ok).toBe(true)
|
|
18
|
-
assert(result.ok === true)
|
|
19
|
-
|
|
20
|
-
expect(result.data).toEqual(contents)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('reads yml contents of a file', async () => {
|
|
24
|
-
const contents = 'a: a'
|
|
25
|
-
const path = randomUUID()
|
|
26
|
-
await fs.writeFile(path, contents)
|
|
27
|
-
|
|
28
|
-
const result = await readFile(path)
|
|
29
|
-
await fs.rm(path)
|
|
30
|
-
|
|
31
|
-
expect(result.ok).toBe(true)
|
|
32
|
-
assert(result.ok === true)
|
|
33
|
-
|
|
34
|
-
expect(result.data).toEqual({ a: 'a' })
|
|
35
|
-
})
|
|
36
|
-
})
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { LoaderPlugin, ResolveResult } from '@/bundle'
|
|
2
|
-
import { isFilePath } from '@/bundle/bundle'
|
|
3
|
-
import { normalize } from '@/helpers/normalize'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Reads and normalizes data from a local file
|
|
7
|
-
* @param path - The file path to read from
|
|
8
|
-
* @returns A promise that resolves to either the normalized data or an error result
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* const result = await readFile('./schemas/user.json')
|
|
12
|
-
* if (result.ok) {
|
|
13
|
-
* console.log(result.data) // The normalized data
|
|
14
|
-
* } else {
|
|
15
|
-
* console.log('Failed to read file')
|
|
16
|
-
* }
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
export async function readFile(path: string): Promise<ResolveResult> {
|
|
20
|
-
const fs = typeof window === 'undefined' ? await import('node:fs/promises') : undefined
|
|
21
|
-
|
|
22
|
-
if (fs === undefined) {
|
|
23
|
-
throw 'Can not use readFiles plugin outside of a node environment'
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
const fileContents = await fs.readFile(path, { encoding: 'utf-8' })
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
ok: true,
|
|
31
|
-
data: normalize(fileContents),
|
|
32
|
-
raw: fileContents,
|
|
33
|
-
}
|
|
34
|
-
} catch {
|
|
35
|
-
return {
|
|
36
|
-
ok: false,
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Creates a plugin for handling local file references.
|
|
43
|
-
* This plugin validates and reads data from local filesystem paths.
|
|
44
|
-
*
|
|
45
|
-
* @returns A plugin object with validate and exec functions
|
|
46
|
-
* @example
|
|
47
|
-
* const filePlugin = readFiles()
|
|
48
|
-
* if (filePlugin.validate('./local-schema.json')) {
|
|
49
|
-
* const result = await filePlugin.exec('./local-schema.json')
|
|
50
|
-
* }
|
|
51
|
-
*/
|
|
52
|
-
export function readFiles(): LoaderPlugin {
|
|
53
|
-
return {
|
|
54
|
-
type: 'loader',
|
|
55
|
-
validate: isFilePath,
|
|
56
|
-
exec: readFile,
|
|
57
|
-
}
|
|
58
|
-
}
|