@scalar/json-magic 0.8.1 → 0.8.3
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 +22 -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/bundle/value-generator.d.ts +2 -2
- package/dist/bundle/value-generator.d.ts.map +1 -1
- package/dist/bundle/value-generator.js +3 -3
- package/dist/bundle/value-generator.js.map +2 -2
- 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 -14
- package/.turbo/turbo-build.log +0 -10
- package/dist/helpers/generate-hash.d.ts +0 -11
- package/dist/helpers/generate-hash.d.ts.map +0 -1
- package/dist/helpers/generate-hash.js +0 -16
- package/dist/helpers/generate-hash.js.map +0 -7
- package/esbuild.ts +0 -15
- package/src/bundle/bundle.test.ts +0 -2921
- 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 -144
- 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 -145
- 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/generate-hash.test.ts +0 -74
- package/src/helpers/generate-hash.ts +0 -29
- 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,338 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { getValueByPath } from './get-value-by-path'
|
|
4
|
-
|
|
5
|
-
describe('getValueByPath', () => {
|
|
6
|
-
it('should return the value at a simple path', () => {
|
|
7
|
-
const target = {
|
|
8
|
-
foo: 'bar',
|
|
9
|
-
}
|
|
10
|
-
const result = getValueByPath(target, ['foo'])
|
|
11
|
-
|
|
12
|
-
expect(result).toEqual({
|
|
13
|
-
context: '',
|
|
14
|
-
value: 'bar',
|
|
15
|
-
})
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('should return the value at a nested path', () => {
|
|
19
|
-
const target = {
|
|
20
|
-
foo: {
|
|
21
|
-
bar: {
|
|
22
|
-
baz: 42,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
}
|
|
26
|
-
const result = getValueByPath(target, ['foo', 'bar', 'baz'])
|
|
27
|
-
|
|
28
|
-
expect(result).toEqual({
|
|
29
|
-
context: '',
|
|
30
|
-
value: 42,
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('should return context with $id when found', () => {
|
|
35
|
-
const target = {
|
|
36
|
-
foo: {
|
|
37
|
-
$id: 'https://example.com/schema',
|
|
38
|
-
bar: {
|
|
39
|
-
baz: 'value',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
const result = getValueByPath(target, ['foo', 'bar', 'baz'])
|
|
44
|
-
|
|
45
|
-
expect(result).toEqual({
|
|
46
|
-
context: 'https://example.com/schema',
|
|
47
|
-
value: 'value',
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should preserve context from parent when child has no $id', () => {
|
|
52
|
-
const target = {
|
|
53
|
-
foo: {
|
|
54
|
-
$id: 'https://example.com/parent',
|
|
55
|
-
bar: {
|
|
56
|
-
baz: 'value',
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
}
|
|
60
|
-
const result = getValueByPath(target, ['foo', 'bar', 'baz'])
|
|
61
|
-
|
|
62
|
-
expect(result).toEqual({
|
|
63
|
-
context: 'https://example.com/parent',
|
|
64
|
-
value: 'value',
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('should update context when child has $id', () => {
|
|
69
|
-
const target = {
|
|
70
|
-
foo: {
|
|
71
|
-
$id: 'https://example.com/parent',
|
|
72
|
-
bar: {
|
|
73
|
-
$id: 'https://example.com/child',
|
|
74
|
-
baz: 'value',
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
}
|
|
78
|
-
const result = getValueByPath(target, ['foo', 'bar', 'baz'])
|
|
79
|
-
|
|
80
|
-
expect(result).toEqual({
|
|
81
|
-
context: 'https://example.com/child',
|
|
82
|
-
value: 'value',
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('should return undefined value when path does not exist', () => {
|
|
87
|
-
const target = {
|
|
88
|
-
foo: 'bar',
|
|
89
|
-
}
|
|
90
|
-
const result = getValueByPath(target, ['nonexistent'])
|
|
91
|
-
|
|
92
|
-
expect(result).toEqual({
|
|
93
|
-
context: '',
|
|
94
|
-
value: undefined,
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('should return undefined value when path partially exists', () => {
|
|
99
|
-
const target = {
|
|
100
|
-
foo: {
|
|
101
|
-
bar: 'baz',
|
|
102
|
-
},
|
|
103
|
-
}
|
|
104
|
-
const result = getValueByPath(target, ['foo', 'nonexistent'])
|
|
105
|
-
|
|
106
|
-
expect(result).toEqual({
|
|
107
|
-
context: '',
|
|
108
|
-
value: undefined,
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
it('should return undefined value when traversing through null', () => {
|
|
113
|
-
const target = {
|
|
114
|
-
foo: null,
|
|
115
|
-
}
|
|
116
|
-
const result = getValueByPath(target, ['foo', 'bar'])
|
|
117
|
-
|
|
118
|
-
expect(result).toEqual({
|
|
119
|
-
context: '',
|
|
120
|
-
value: undefined,
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('should return undefined value when traversing through primitive', () => {
|
|
125
|
-
const target = {
|
|
126
|
-
foo: 'string',
|
|
127
|
-
}
|
|
128
|
-
const result = getValueByPath(target, ['foo', 'bar'])
|
|
129
|
-
|
|
130
|
-
expect(result).toEqual({
|
|
131
|
-
context: '',
|
|
132
|
-
value: undefined,
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('should handle empty segments array', () => {
|
|
137
|
-
const target = {
|
|
138
|
-
foo: 'bar',
|
|
139
|
-
}
|
|
140
|
-
const result = getValueByPath(target, [])
|
|
141
|
-
|
|
142
|
-
expect(result).toEqual({
|
|
143
|
-
context: '',
|
|
144
|
-
value: target,
|
|
145
|
-
})
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
it('should handle array indices in path', () => {
|
|
149
|
-
const target = {
|
|
150
|
-
items: [{ name: 'first' }, { name: 'second' }],
|
|
151
|
-
}
|
|
152
|
-
const result = getValueByPath(target, ['items', '0', 'name'])
|
|
153
|
-
|
|
154
|
-
expect(result).toEqual({
|
|
155
|
-
context: '',
|
|
156
|
-
value: 'first',
|
|
157
|
-
})
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
it('should handle objects with numeric keys', () => {
|
|
161
|
-
const target = {
|
|
162
|
-
'0': 'zero',
|
|
163
|
-
'1': 'one',
|
|
164
|
-
}
|
|
165
|
-
const result = getValueByPath(target, ['0'])
|
|
166
|
-
|
|
167
|
-
expect(result).toEqual({
|
|
168
|
-
context: '',
|
|
169
|
-
value: 'zero',
|
|
170
|
-
})
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
it('should handle deeply nested objects', () => {
|
|
174
|
-
const target = {
|
|
175
|
-
level1: {
|
|
176
|
-
level2: {
|
|
177
|
-
level3: {
|
|
178
|
-
level4: {
|
|
179
|
-
level5: {
|
|
180
|
-
value: 'deep',
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
}
|
|
187
|
-
const result = getValueByPath(target, ['level1', 'level2', 'level3', 'level4', 'level5', 'value'])
|
|
188
|
-
|
|
189
|
-
expect(result).toEqual({
|
|
190
|
-
context: '',
|
|
191
|
-
value: 'deep',
|
|
192
|
-
})
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
it('should handle mixed data types in path', () => {
|
|
196
|
-
const target = {
|
|
197
|
-
string: 'text',
|
|
198
|
-
number: 42,
|
|
199
|
-
boolean: true,
|
|
200
|
-
null: null,
|
|
201
|
-
array: [1, 2, 3],
|
|
202
|
-
object: { nested: 'value' },
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
expect(getValueByPath(target, ['string'])).toEqual({ context: '', value: 'text' })
|
|
206
|
-
expect(getValueByPath(target, ['number'])).toEqual({ context: '', value: 42 })
|
|
207
|
-
expect(getValueByPath(target, ['boolean'])).toEqual({ context: '', value: true })
|
|
208
|
-
expect(getValueByPath(target, ['null'])).toEqual({ context: '', value: null })
|
|
209
|
-
expect(getValueByPath(target, ['array'])).toEqual({ context: '', value: [1, 2, 3] })
|
|
210
|
-
expect(getValueByPath(target, ['object', 'nested'])).toEqual({ context: '', value: 'value' })
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
it('should handle $id with different formats', () => {
|
|
214
|
-
const target = {
|
|
215
|
-
schema1: {
|
|
216
|
-
$id: 'https://example.com/schema1',
|
|
217
|
-
value: 'test1',
|
|
218
|
-
},
|
|
219
|
-
schema2: {
|
|
220
|
-
$id: 'urn:uuid:123e4567-e89b-12d3-a456-426614174000',
|
|
221
|
-
value: 'test2',
|
|
222
|
-
},
|
|
223
|
-
schema3: {
|
|
224
|
-
$id: 'relative/path',
|
|
225
|
-
value: 'test3',
|
|
226
|
-
},
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
expect(getValueByPath(target, ['schema1', 'value'])).toEqual({
|
|
230
|
-
context: 'https://example.com/schema1',
|
|
231
|
-
value: 'test1',
|
|
232
|
-
})
|
|
233
|
-
expect(getValueByPath(target, ['schema2', 'value'])).toEqual({
|
|
234
|
-
context: 'urn:uuid:123e4567-e89b-12d3-a456-426614174000',
|
|
235
|
-
value: 'test2',
|
|
236
|
-
})
|
|
237
|
-
expect(getValueByPath(target, ['schema3', 'value'])).toEqual({
|
|
238
|
-
context: 'relative/path',
|
|
239
|
-
value: 'test3',
|
|
240
|
-
})
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
it('should ignore non-string $id values', () => {
|
|
244
|
-
const target = {
|
|
245
|
-
schema1: {
|
|
246
|
-
$id: 123, // number
|
|
247
|
-
value: 'test1',
|
|
248
|
-
},
|
|
249
|
-
schema2: {
|
|
250
|
-
$id: { nested: 'object' }, // object
|
|
251
|
-
value: 'test2',
|
|
252
|
-
},
|
|
253
|
-
schema3: {
|
|
254
|
-
$id: null, // null
|
|
255
|
-
value: 'test3',
|
|
256
|
-
},
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
expect(getValueByPath(target, ['schema1', 'value'])).toEqual({
|
|
260
|
-
context: '',
|
|
261
|
-
value: 'test1',
|
|
262
|
-
})
|
|
263
|
-
expect(getValueByPath(target, ['schema2', 'value'])).toEqual({
|
|
264
|
-
context: '',
|
|
265
|
-
value: 'test2',
|
|
266
|
-
})
|
|
267
|
-
expect(getValueByPath(target, ['schema3', 'value'])).toEqual({
|
|
268
|
-
context: '',
|
|
269
|
-
value: 'test3',
|
|
270
|
-
})
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
it('should handle context inheritance through multiple levels', () => {
|
|
274
|
-
const target = {
|
|
275
|
-
root: {
|
|
276
|
-
$id: 'https://example.com/root',
|
|
277
|
-
level1: {
|
|
278
|
-
level2: {
|
|
279
|
-
$id: 'https://example.com/level2',
|
|
280
|
-
level3: {
|
|
281
|
-
level4: {
|
|
282
|
-
value: 'nested',
|
|
283
|
-
},
|
|
284
|
-
},
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
},
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Should use root context when no intermediate $id
|
|
291
|
-
expect(getValueByPath(target, ['root', 'level1', 'level2', 'level3', 'level4', 'value'])).toEqual({
|
|
292
|
-
context: 'https://example.com/level2',
|
|
293
|
-
value: 'nested',
|
|
294
|
-
})
|
|
295
|
-
})
|
|
296
|
-
|
|
297
|
-
it('should handle edge case with empty string keys', () => {
|
|
298
|
-
const target = {
|
|
299
|
-
'': 'empty key',
|
|
300
|
-
normal: 'normal key',
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
expect(getValueByPath(target, [''])).toEqual({
|
|
304
|
-
context: '',
|
|
305
|
-
value: 'empty key',
|
|
306
|
-
})
|
|
307
|
-
expect(getValueByPath(target, ['normal'])).toEqual({
|
|
308
|
-
context: '',
|
|
309
|
-
value: 'normal key',
|
|
310
|
-
})
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
it('should handle special characters in keys', () => {
|
|
314
|
-
const target = {
|
|
315
|
-
'key-with-dash': 'dash',
|
|
316
|
-
'key_with_underscore': 'underscore',
|
|
317
|
-
'key.with.dots': 'dots',
|
|
318
|
-
'key with spaces': 'spaces',
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
expect(getValueByPath(target, ['key-with-dash'])).toEqual({
|
|
322
|
-
context: '',
|
|
323
|
-
value: 'dash',
|
|
324
|
-
})
|
|
325
|
-
expect(getValueByPath(target, ['key_with_underscore'])).toEqual({
|
|
326
|
-
context: '',
|
|
327
|
-
value: 'underscore',
|
|
328
|
-
})
|
|
329
|
-
expect(getValueByPath(target, ['key.with.dots'])).toEqual({
|
|
330
|
-
context: '',
|
|
331
|
-
value: 'dots',
|
|
332
|
-
})
|
|
333
|
-
expect(getValueByPath(target, ['key with spaces'])).toEqual({
|
|
334
|
-
context: '',
|
|
335
|
-
value: 'spaces',
|
|
336
|
-
})
|
|
337
|
-
})
|
|
338
|
-
})
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { getId } from '@/helpers/get-schemas'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Traverses an object using an array of string segments (path keys) and returns
|
|
5
|
-
* the value at the specified path along with its context (id if available).
|
|
6
|
-
*
|
|
7
|
-
* @param target - The root object to traverse.
|
|
8
|
-
* @param segments - An array of string keys representing the path to traverse.
|
|
9
|
-
* @returns An object containing the final context (id or previous context) and the value at the path.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* const obj = {
|
|
13
|
-
* foo: {
|
|
14
|
-
* bar: {
|
|
15
|
-
* baz: 42
|
|
16
|
-
* }
|
|
17
|
-
* }
|
|
18
|
-
* };
|
|
19
|
-
* // Returns: { context: '', value: 42 }
|
|
20
|
-
* getValueByPath(obj, ['foo', 'bar', 'baz']);
|
|
21
|
-
*/
|
|
22
|
-
export function getValueByPath(target: unknown, segments: string[]): { context: string; value: any } {
|
|
23
|
-
return segments.reduce<{ context: string; value: unknown }>(
|
|
24
|
-
(acc, key) => {
|
|
25
|
-
// If the accumulator is undefined, the path does not exist
|
|
26
|
-
if (acc.value === undefined) {
|
|
27
|
-
return { context: '', value: undefined }
|
|
28
|
-
}
|
|
29
|
-
// If the accumulator is not an object or is null, stop traversal
|
|
30
|
-
if (typeof acc.value !== 'object' || acc.value === null) {
|
|
31
|
-
return { context: '', value: undefined }
|
|
32
|
-
}
|
|
33
|
-
// Attempt to get the id from the current value for context tracking
|
|
34
|
-
const id = getId(acc.value)
|
|
35
|
-
|
|
36
|
-
// Return the next context and value for the next iteration
|
|
37
|
-
return { context: id ?? acc.context, value: acc.value?.[key] }
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
context: '',
|
|
41
|
-
value: target,
|
|
42
|
-
},
|
|
43
|
-
)
|
|
44
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { isObject } from '@/helpers/is-object'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Determines if a string represents a valid JSON object (i.e., a plain object, not an array, primitive, or null).
|
|
5
|
-
* The function first checks if the string appears to start with an opening curly brace (ignoring leading whitespace),
|
|
6
|
-
* which is a quick heuristic to rule out arrays, primitives, and most invalid JSON. If this check passes,
|
|
7
|
-
* it attempts to parse the string with JSON.parse. The result is then checked to ensure it is a plain object
|
|
8
|
-
* (not an array, null, or primitive) using the isObject utility.
|
|
9
|
-
*
|
|
10
|
-
* @param value - The string to evaluate
|
|
11
|
-
* @returns true if the string is valid JSON and parses to a plain object, false otherwise
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* isJsonObject('{"foo": "bar"}') // true
|
|
15
|
-
* isJsonObject('[1,2,3]') // false
|
|
16
|
-
* isJsonObject('not json') // false
|
|
17
|
-
* isJsonObject('42') // false
|
|
18
|
-
*/
|
|
19
|
-
export function isJsonObject(value: string) {
|
|
20
|
-
// Quickly rule out anything that doesn't start with an object brace
|
|
21
|
-
if (!/^\s*(\{)/.test(value.slice(0, 500))) {
|
|
22
|
-
return false
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const val = JSON.parse(value)
|
|
27
|
-
return isObject(val)
|
|
28
|
-
} catch {
|
|
29
|
-
return false
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { isObject } from './is-object'
|
|
4
|
-
|
|
5
|
-
describe('isObject', () => {
|
|
6
|
-
it('returns true for an object', () => {
|
|
7
|
-
const result = isObject({
|
|
8
|
-
foo: 'bar',
|
|
9
|
-
})
|
|
10
|
-
expect(result).toBe(true)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('returns true for an empty object', () => {
|
|
14
|
-
const result = isObject({})
|
|
15
|
-
expect(result).toBe(true)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('returns false for a string', () => {
|
|
19
|
-
const result = isObject('foo')
|
|
20
|
-
expect(result).toBe(false)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('returns false for an array', () => {
|
|
24
|
-
const result = isObject([])
|
|
25
|
-
expect(result).toBe(false)
|
|
26
|
-
})
|
|
27
|
-
})
|
package/src/helpers/is-object.ts
DELETED
package/src/helpers/is-yaml.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checks if a string appears to be YAML content.
|
|
3
|
-
* This function uses a simple heuristic: it looks for a line that starts with an optional dash,
|
|
4
|
-
* followed by a key (alphanumeric or dash), a colon, and a value, and then at least one more line.
|
|
5
|
-
* This is not a full YAML parser, but works for basic detection.
|
|
6
|
-
*
|
|
7
|
-
* @param value - The string to check
|
|
8
|
-
* @returns true if the string looks like YAML, false otherwise
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* isYaml('openapi: 3.0.0\ninfo:\n title: Example') // true
|
|
12
|
-
* isYaml('{"openapi": "3.0.0", "info": {"title": "Example"}}') // false
|
|
13
|
-
* isYaml('- name: value\n- name: value2') // true
|
|
14
|
-
* isYaml('type: object') // false (only one line)
|
|
15
|
-
*/
|
|
16
|
-
export function isYaml(value: string): boolean {
|
|
17
|
-
return /^\s*(?:-\s*)?[\w\-]+\s*:\s*.+\n.*/.test(value)
|
|
18
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { createPathFromSegments, parseJsonPointer } from './json-path-utils'
|
|
4
|
-
|
|
5
|
-
describe('parseJsonPointer', () => {
|
|
6
|
-
test.each([
|
|
7
|
-
['#/users/name', ['users', 'name']],
|
|
8
|
-
['#/', []],
|
|
9
|
-
['', []],
|
|
10
|
-
['users/name', ['users', 'name']],
|
|
11
|
-
])('should correctly parse json pointers', (a, b) => {
|
|
12
|
-
expect(parseJsonPointer(a)).toEqual(b)
|
|
13
|
-
})
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
describe('createPathFromSegments', () => {
|
|
17
|
-
test('creates nested objects for non-numeric segments', () => {
|
|
18
|
-
const obj: any = {}
|
|
19
|
-
const leaf = createPathFromSegments(obj, ['components', 'schemas', 'User'])
|
|
20
|
-
|
|
21
|
-
expect(obj).toEqual({ components: { schemas: { User: {} } } })
|
|
22
|
-
expect(leaf).toBe(obj.components.schemas.User)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test('creates arrays for numeric segments', () => {
|
|
26
|
-
const obj: any = {}
|
|
27
|
-
const arr = createPathFromSegments(obj, ['items', '0'])
|
|
28
|
-
|
|
29
|
-
expect(Array.isArray(obj.items['0'])).toBe(true)
|
|
30
|
-
expect(arr).toBe(obj.items['0'])
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
test('does not overwrite existing values along the path', () => {
|
|
34
|
-
const obj: any = { a: { b: { c: { existing: true } } } }
|
|
35
|
-
const leaf = createPathFromSegments(obj, ['a', 'b', 'c'])
|
|
36
|
-
|
|
37
|
-
expect(leaf).toEqual({ existing: true })
|
|
38
|
-
expect(obj.a.b.c).toEqual({ existing: true })
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
test('returns root object when segments array is empty', () => {
|
|
42
|
-
const obj: any = { pre: true }
|
|
43
|
-
const result = createPathFromSegments(obj, [])
|
|
44
|
-
|
|
45
|
-
expect(result).toBe(obj)
|
|
46
|
-
expect(obj).toEqual({ pre: true })
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
test('creates nested arrays for consecutive numeric segments', () => {
|
|
50
|
-
const obj: any = {}
|
|
51
|
-
const leaf = createPathFromSegments(obj, ['arr', '0', '1'])
|
|
52
|
-
|
|
53
|
-
expect(Array.isArray(obj.arr['0'])).toBe(true)
|
|
54
|
-
expect(Array.isArray(obj.arr['0']['1'])).toBe(true)
|
|
55
|
-
expect(leaf).toBe(obj.arr['0']['1'])
|
|
56
|
-
})
|
|
57
|
-
})
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parses a JSON Pointer string into an array of path segments
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* ```ts
|
|
6
|
-
* parseJsonPointer('#/components/schemas/User')
|
|
7
|
-
*
|
|
8
|
-
* ['components', 'schemas', 'User']
|
|
9
|
-
* ```
|
|
10
|
-
*/
|
|
11
|
-
export function parseJsonPointer(pointer: string): string[] {
|
|
12
|
-
return (
|
|
13
|
-
pointer
|
|
14
|
-
// Split on '/'
|
|
15
|
-
.split('/')
|
|
16
|
-
// Remove the leading '#' if present
|
|
17
|
-
.filter((segment, index) => (index !== 0 || segment !== '#') && segment)
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Creates a nested path in an object from an array of path segments.
|
|
23
|
-
* Only creates intermediate objects/arrays if they don't already exist.
|
|
24
|
-
*
|
|
25
|
-
* @param obj - The target object to create the path in
|
|
26
|
-
* @param segments - Array of path segments to create
|
|
27
|
-
* @returns The final nested object/array at the end of the path
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```ts
|
|
31
|
-
* const obj = {}
|
|
32
|
-
* createPathFromSegments(obj, ['components', 'schemas', 'User'])
|
|
33
|
-
* // Creates: { components: { schemas: { User: {} } } }
|
|
34
|
-
*
|
|
35
|
-
* createPathFromSegments(obj, ['items', '0', 'name'])
|
|
36
|
-
* // Creates: { items: [{ name: {} }] }
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
export function createPathFromSegments(obj: any, segments: string[]) {
|
|
40
|
-
return segments.reduce((acc, part) => {
|
|
41
|
-
if (acc[part] === undefined) {
|
|
42
|
-
if (isNaN(Number(part))) {
|
|
43
|
-
acc[part] = {}
|
|
44
|
-
} else {
|
|
45
|
-
acc[part] = []
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return acc[part]
|
|
49
|
-
}, obj)
|
|
50
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { normalize } from '@/helpers/normalize'
|
|
4
|
-
|
|
5
|
-
describe('normalize', () => {
|
|
6
|
-
it('returns undefined if the document is null', () => {
|
|
7
|
-
expect(normalize(null)).toEqual(undefined)
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
it('should parse JSON string specifications', () => {
|
|
11
|
-
const jsonString = '{"foo": "bar"}'
|
|
12
|
-
expect(normalize(jsonString)).toEqual({ foo: 'bar' })
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('should parse YAML string specifications', () => {
|
|
16
|
-
const yamlString = 'foo: bar\nbar: foo'
|
|
17
|
-
expect(normalize(yamlString)).toEqual({ foo: 'bar', bar: 'foo' })
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('should handle invalid YAML with custom maxAliasCount', () => {
|
|
21
|
-
const yamlString = `
|
|
22
|
-
aliases: &ref
|
|
23
|
-
- item1
|
|
24
|
-
- item2
|
|
25
|
-
items: *ref
|
|
26
|
-
`
|
|
27
|
-
expect(() => normalize(yamlString)).not.toThrow()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('should return the same object if specification is already an object', () => {
|
|
31
|
-
const obj = { foo: 'bar' }
|
|
32
|
-
expect(normalize(obj)).toBe(obj)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it("doesn't freak out on invalid JSON strings ", () => {
|
|
36
|
-
// Missing quotes around property name
|
|
37
|
-
const malformedJson = '{ foo: "bar" }'
|
|
38
|
-
expect(normalize(malformedJson)).toEqual(undefined)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should handle empty string input', () => {
|
|
42
|
-
expect(normalize('')).toEqual(undefined)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('should handle whitespace-only string input', () => {
|
|
46
|
-
expect(normalize(' ')).toEqual(undefined)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('should handle complex nested structures', () => {
|
|
50
|
-
const complex = {
|
|
51
|
-
nested: {
|
|
52
|
-
array: [1, 2, 3],
|
|
53
|
-
object: {
|
|
54
|
-
foo: 'bar',
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
}
|
|
58
|
-
expect(normalize(complex)).toEqual(complex)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('should handle invalid JSON and YAML strings gracefully', () => {
|
|
62
|
-
const invalidString = 'not a valid json or yaml'
|
|
63
|
-
expect(() => normalize(invalidString)).not.toThrow()
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('should handle non-string, non-object inputs', () => {
|
|
67
|
-
expect(normalize(42)).toEqual(42)
|
|
68
|
-
expect(normalize(true)).toEqual(true)
|
|
69
|
-
expect(normalize([1, 2, 3])).toEqual([1, 2, 3])
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('should handle deeply nested structures', () => {
|
|
73
|
-
const nested = { a: { b: { c: { d: { e: 'deep' } } } } }
|
|
74
|
-
expect(normalize(nested)).toEqual(nested)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
it('should handle large input strings', () => {
|
|
78
|
-
const largeJson = JSON.stringify({ foo: 'bar'.repeat(10000) })
|
|
79
|
-
expect(normalize(largeJson)).toEqual({ foo: 'bar'.repeat(10000) })
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('should handle circular references', () => {
|
|
83
|
-
const circularObj: any = {}
|
|
84
|
-
circularObj.self = circularObj
|
|
85
|
-
expect(() => normalize(circularObj)).not.toThrow()
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('should handle special characters in strings', () => {
|
|
89
|
-
const specialCharJson = '{"foo": "bar\\n"}'
|
|
90
|
-
expect(normalize(specialCharJson)).toEqual({ foo: 'bar\n' })
|
|
91
|
-
})
|
|
92
|
-
})
|
package/src/helpers/normalize.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { parse } from 'yaml'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Normalize a string (YAML, JSON, object) to a JavaScript datatype.
|
|
5
|
-
*/
|
|
6
|
-
export function normalize(content: any) {
|
|
7
|
-
if (content === null) {
|
|
8
|
-
return undefined
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
if (typeof content === 'string') {
|
|
12
|
-
if (content.trim() === '') {
|
|
13
|
-
return undefined
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
return JSON.parse(content)
|
|
18
|
-
} catch (_error) {
|
|
19
|
-
// Does it look like YAML?
|
|
20
|
-
const hasColon = /^[^:]+:/.test(content)
|
|
21
|
-
const isJson = content.slice(0, 50).trimStart().startsWith('{')
|
|
22
|
-
|
|
23
|
-
if (!hasColon || isJson) {
|
|
24
|
-
return undefined
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return parse(content, {
|
|
28
|
-
maxAliasCount: 10000,
|
|
29
|
-
merge: true,
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return content
|
|
35
|
-
}
|