@scalar/json-magic 0.4.3 → 0.5.1

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 (111) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +16 -0
  3. package/dist/bundle/bundle.d.ts +37 -10
  4. package/dist/bundle/bundle.d.ts.map +1 -1
  5. package/dist/bundle/bundle.js +40 -30
  6. package/dist/bundle/bundle.js.map +2 -2
  7. package/dist/bundle/index.d.ts +2 -2
  8. package/dist/bundle/index.d.ts.map +1 -1
  9. package/dist/bundle/index.js +3 -2
  10. package/dist/bundle/index.js.map +2 -2
  11. package/dist/bundle/plugins/fetch-urls/index.d.ts.map +1 -1
  12. package/dist/bundle/plugins/fetch-urls/index.js +2 -2
  13. package/dist/bundle/plugins/fetch-urls/index.js.map +2 -2
  14. package/dist/bundle/plugins/parse-json/index.d.ts.map +1 -1
  15. package/dist/bundle/plugins/parse-json/index.js +1 -1
  16. package/dist/bundle/plugins/parse-json/index.js.map +2 -2
  17. package/dist/bundle/plugins/parse-yaml/index.d.ts.map +1 -1
  18. package/dist/bundle/plugins/parse-yaml/index.js +1 -1
  19. package/dist/bundle/plugins/parse-yaml/index.js.map +2 -2
  20. package/dist/bundle/plugins/read-files/index.d.ts.map +1 -1
  21. package/dist/bundle/plugins/read-files/index.js +1 -1
  22. package/dist/bundle/plugins/read-files/index.js.map +2 -2
  23. package/dist/helpers/convert-to-local-ref.d.ts +10 -0
  24. package/dist/helpers/convert-to-local-ref.d.ts.map +1 -0
  25. package/dist/helpers/convert-to-local-ref.js +26 -0
  26. package/dist/helpers/convert-to-local-ref.js.map +7 -0
  27. package/dist/helpers/escape-json-pointer.d.ts.map +1 -0
  28. package/dist/{utils → helpers}/escape-json-pointer.js.map +1 -1
  29. package/dist/helpers/get-schemas.d.ts +21 -0
  30. package/dist/helpers/get-schemas.d.ts.map +1 -0
  31. package/dist/helpers/get-schemas.js +37 -0
  32. package/dist/helpers/get-schemas.js.map +7 -0
  33. package/dist/helpers/get-segments-from-path.d.ts.map +1 -0
  34. package/dist/{utils → helpers}/get-segments-from-path.js.map +1 -1
  35. package/dist/helpers/get-value-by-path.d.ts +24 -0
  36. package/dist/helpers/get-value-by-path.d.ts.map +1 -0
  37. package/dist/helpers/get-value-by-path.js +23 -0
  38. package/dist/helpers/get-value-by-path.js.map +7 -0
  39. package/dist/helpers/is-json-object.d.ts.map +1 -0
  40. package/dist/{utils → helpers}/is-json-object.js +1 -1
  41. package/dist/helpers/is-json-object.js.map +7 -0
  42. package/dist/helpers/is-object.d.ts.map +1 -0
  43. package/dist/{utils → helpers}/is-object.js.map +1 -1
  44. package/dist/helpers/is-yaml.d.ts.map +1 -0
  45. package/dist/{utils → helpers}/is-yaml.js.map +1 -1
  46. package/dist/{utils → helpers}/json-path-utils.d.ts +0 -11
  47. package/dist/helpers/json-path-utils.d.ts.map +1 -0
  48. package/dist/{utils → helpers}/json-path-utils.js +0 -9
  49. package/dist/helpers/json-path-utils.js.map +7 -0
  50. package/dist/helpers/normalize.d.ts.map +1 -0
  51. package/dist/{utils → helpers}/normalize.js.map +1 -1
  52. package/dist/helpers/unescape-json-pointer.d.ts.map +1 -0
  53. package/dist/{utils → helpers}/unescape-json-pointer.js.map +1 -1
  54. package/dist/magic-proxy/proxy.d.ts +35 -10
  55. package/dist/magic-proxy/proxy.d.ts.map +1 -1
  56. package/dist/magic-proxy/proxy.js +40 -20
  57. package/dist/magic-proxy/proxy.js.map +2 -2
  58. package/esbuild.ts +1 -0
  59. package/package.json +6 -1
  60. package/src/bundle/bundle.test.ts +533 -25
  61. package/src/bundle/bundle.ts +53 -37
  62. package/src/bundle/index.ts +3 -3
  63. package/src/bundle/plugins/fetch-urls/index.ts +2 -2
  64. package/src/bundle/plugins/parse-json/index.ts +1 -1
  65. package/src/bundle/plugins/parse-yaml/index.ts +3 -2
  66. package/src/bundle/plugins/read-files/index.ts +1 -1
  67. package/src/helpers/convert-to-local-ref.test.ts +211 -0
  68. package/src/helpers/convert-to-local-ref.ts +43 -0
  69. package/src/helpers/get-schemas.test.ts +356 -0
  70. package/src/helpers/get-schemas.ts +80 -0
  71. package/src/helpers/get-value-by-path.test.ts +338 -0
  72. package/src/helpers/get-value-by-path.ts +44 -0
  73. package/src/{utils → helpers}/is-json-object.ts +1 -1
  74. package/src/{utils → helpers}/json-path-utils.ts +0 -19
  75. package/src/{utils → helpers}/normalize.test.ts +2 -1
  76. package/src/magic-proxy/proxy.test.ts +548 -0
  77. package/src/magic-proxy/proxy.ts +80 -31
  78. package/dist/utils/escape-json-pointer.d.ts.map +0 -1
  79. package/dist/utils/get-segments-from-path.d.ts.map +0 -1
  80. package/dist/utils/is-json-object.d.ts.map +0 -1
  81. package/dist/utils/is-json-object.js.map +0 -7
  82. package/dist/utils/is-object.d.ts.map +0 -1
  83. package/dist/utils/is-yaml.d.ts.map +0 -1
  84. package/dist/utils/json-path-utils.d.ts.map +0 -1
  85. package/dist/utils/json-path-utils.js.map +0 -7
  86. package/dist/utils/normalize.d.ts.map +0 -1
  87. package/dist/utils/unescape-json-pointer.d.ts.map +0 -1
  88. /package/dist/{utils → helpers}/escape-json-pointer.d.ts +0 -0
  89. /package/dist/{utils → helpers}/escape-json-pointer.js +0 -0
  90. /package/dist/{utils → helpers}/get-segments-from-path.d.ts +0 -0
  91. /package/dist/{utils → helpers}/get-segments-from-path.js +0 -0
  92. /package/dist/{utils → helpers}/is-json-object.d.ts +0 -0
  93. /package/dist/{utils → helpers}/is-object.d.ts +0 -0
  94. /package/dist/{utils → helpers}/is-object.js +0 -0
  95. /package/dist/{utils → helpers}/is-yaml.d.ts +0 -0
  96. /package/dist/{utils → helpers}/is-yaml.js +0 -0
  97. /package/dist/{utils → helpers}/normalize.d.ts +0 -0
  98. /package/dist/{utils → helpers}/normalize.js +0 -0
  99. /package/dist/{utils → helpers}/unescape-json-pointer.d.ts +0 -0
  100. /package/dist/{utils → helpers}/unescape-json-pointer.js +0 -0
  101. /package/src/{utils → helpers}/escape-json-pointer.test.ts +0 -0
  102. /package/src/{utils → helpers}/escape-json-pointer.ts +0 -0
  103. /package/src/{utils → helpers}/get-segments-from-path.test.ts +0 -0
  104. /package/src/{utils → helpers}/get-segments-from-path.ts +0 -0
  105. /package/src/{utils → helpers}/is-object.test.ts +0 -0
  106. /package/src/{utils → helpers}/is-object.ts +0 -0
  107. /package/src/{utils → helpers}/is-yaml.ts +0 -0
  108. /package/src/{utils → helpers}/json-path-utils.test.ts +0 -0
  109. /package/src/{utils → helpers}/normalize.ts +0 -0
  110. /package/src/{utils → helpers}/unescape-json-pointer.test.ts +0 -0
  111. /package/src/{utils → helpers}/unescape-json-pointer.ts +0 -0
@@ -1,25 +1,28 @@
1
1
  import { randomUUID } from 'node:crypto'
2
2
  import fs from 'node:fs/promises'
3
+ import { setTimeout } from 'node:timers/promises'
4
+
5
+ import { consoleWarnSpy, resetConsoleSpies } from '@scalar/helpers/testing/console-spies'
3
6
  import fastify, { type FastifyInstance } from 'fastify'
4
7
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
8
+ import YAML from 'yaml'
9
+
10
+ import { parseJson } from '@/bundle/plugins/parse-json'
11
+ import { parseYaml } from '@/bundle/plugins/parse-yaml'
12
+ import { getHash } from '@/bundle/value-generator'
13
+
5
14
  import {
15
+ type LoaderPlugin,
6
16
  bundle,
7
- getNestedValue,
8
17
  isLocalRef,
9
18
  isRemoteUrl,
10
19
  prefixInternalRef,
11
20
  prefixInternalRefRecursive,
21
+ resolveAndCopyReferences,
12
22
  setValueAtPath,
13
- type LoaderPlugin,
14
23
  } from './bundle'
15
24
  import { fetchUrls } from './plugins/fetch-urls'
16
25
  import { readFiles } from './plugins/read-files'
17
- import { setTimeout } from 'node:timers/promises'
18
- import { parseJson } from '@/bundle/plugins/parse-json'
19
- import { parseYaml } from '@/bundle/plugins/parse-yaml'
20
- import YAML from 'yaml'
21
- import { getHash } from '@/bundle/value-generator'
22
- import { consoleWarnSpy, resetConsoleSpies } from '@scalar/helpers/testing/console-spies'
23
26
 
24
27
  describe('bundle', () => {
25
28
  describe('external urls', () => {
@@ -1102,18 +1105,18 @@ describe('bundle', () => {
1102
1105
  $ref: `${url}/external/document.json`,
1103
1106
  },
1104
1107
  b: {
1105
- $ref: `${url}/chunk2#`,
1108
+ $ref: `${url}/chunk2`,
1106
1109
  $global: true,
1107
1110
  },
1108
1111
  a: {
1109
- $ref: `${url}/chunk1#`,
1112
+ $ref: `${url}/chunk1`,
1110
1113
  $global: true,
1111
1114
  },
1112
1115
  entry: {
1113
1116
  $ref: '#/a',
1114
1117
  },
1115
1118
  nonBundle: {
1116
- $ref: `${url}/chunk1#`,
1119
+ $ref: `${url}/chunk1`,
1117
1120
  },
1118
1121
  }
1119
1122
 
@@ -1145,7 +1148,7 @@ describe('bundle', () => {
1145
1148
  $ref: '#/a',
1146
1149
  },
1147
1150
  nonBundle: {
1148
- $ref: `http://localhost:${port}/chunk1#`,
1151
+ $ref: `http://localhost:${port}/chunk1`,
1149
1152
  },
1150
1153
  'x-ext': {
1151
1154
  [await getHash(`${url}/external/document.json`)]: {
@@ -1325,6 +1328,448 @@ describe('bundle', () => {
1325
1328
  'x-ext': { e53b62c: { message: 'some resolved external reference' } },
1326
1329
  })
1327
1330
  })
1331
+
1332
+ it('does not modify external URLs when already defined by $id property', async () => {
1333
+ const url = `http://localhost:${port}`
1334
+
1335
+ const input = {
1336
+ $id: 'https://example.com/root',
1337
+ components: {
1338
+ schemas: {
1339
+ User: {
1340
+ $id: `${url}/schema`,
1341
+ type: 'object',
1342
+ properties: {
1343
+ name: { type: 'string' },
1344
+ },
1345
+ },
1346
+ },
1347
+ },
1348
+ paths: {
1349
+ '/users': {
1350
+ get: {
1351
+ responses: {
1352
+ '200': {
1353
+ description: 'Success',
1354
+ content: {
1355
+ 'application/json': {
1356
+ schema: {
1357
+ $ref: `${url}/schema`,
1358
+ },
1359
+ },
1360
+ },
1361
+ },
1362
+ },
1363
+ },
1364
+ },
1365
+ },
1366
+ }
1367
+
1368
+ await bundle(input, {
1369
+ plugins: [fetchUrls(), readFiles()],
1370
+ treeShake: false,
1371
+ })
1372
+
1373
+ // The $ref should remain unchanged because the schema is already defined locally with $id
1374
+ expect(input.paths['/users'].get.responses['200'].content['application/json'].schema.$ref).toBe(`${url}/schema`)
1375
+
1376
+ // The external schema should not be bundled into x-ext
1377
+ expect(input['x-ext']).toBeUndefined()
1378
+
1379
+ // The local schema with $id should remain unchanged
1380
+ expect(input.components.schemas.User).toEqual({
1381
+ $id: `${url}/schema`,
1382
+ type: 'object',
1383
+ properties: {
1384
+ name: { type: 'string' },
1385
+ },
1386
+ })
1387
+ })
1388
+
1389
+ it('does not modify external URLs when already defined by $anchor property', async () => {
1390
+ const input = {
1391
+ $id: 'https://example.com/root',
1392
+ components: {
1393
+ schemas: {
1394
+ User: {
1395
+ $anchor: 'user-schema',
1396
+ type: 'object',
1397
+ properties: {
1398
+ name: { type: 'string' },
1399
+ },
1400
+ },
1401
+ },
1402
+ },
1403
+ paths: {
1404
+ '/users': {
1405
+ get: {
1406
+ responses: {
1407
+ '200': {
1408
+ description: 'Success',
1409
+ content: {
1410
+ 'application/json': {
1411
+ schema: {
1412
+ $ref: 'https://example.com/root/schema#user-schema',
1413
+ },
1414
+ },
1415
+ },
1416
+ },
1417
+ },
1418
+ },
1419
+ },
1420
+ },
1421
+ }
1422
+
1423
+ await bundle(input, {
1424
+ plugins: [fetchUrls(), readFiles()],
1425
+ treeShake: false,
1426
+ })
1427
+
1428
+ // The $ref should remain unchanged because the schema is already defined locally with $anchor
1429
+ expect(input.paths['/users'].get.responses['200'].content['application/json'].schema.$ref).toBe(
1430
+ 'https://example.com/root/schema#user-schema',
1431
+ )
1432
+
1433
+ // The external schema should not be bundled into x-ext
1434
+ expect(input['x-ext']).toBeUndefined()
1435
+
1436
+ // The local schema with $anchor should remain unchanged
1437
+ expect(input.components.schemas.User).toEqual({
1438
+ $anchor: 'user-schema',
1439
+ type: 'object',
1440
+ properties: {
1441
+ name: { type: 'string' },
1442
+ },
1443
+ })
1444
+ })
1445
+
1446
+ it('does not modify external URLs when prefix is already defined by $id', async () => {
1447
+ const url = `http://localhost:${port}`
1448
+
1449
+ const input = {
1450
+ $id: `${url}/schema`,
1451
+ components: {
1452
+ schemas: {
1453
+ User: {
1454
+ type: 'object',
1455
+ properties: {
1456
+ name: { type: 'string' },
1457
+ },
1458
+ },
1459
+ },
1460
+ },
1461
+ paths: {
1462
+ '/users': {
1463
+ get: {
1464
+ responses: {
1465
+ '200': {
1466
+ description: 'Success',
1467
+ content: {
1468
+ 'application/json': {
1469
+ schema: {
1470
+ $ref: `${url}/schema#/components/schemas/User`,
1471
+ },
1472
+ },
1473
+ },
1474
+ },
1475
+ },
1476
+ },
1477
+ },
1478
+ },
1479
+ }
1480
+
1481
+ await bundle(input, {
1482
+ plugins: [fetchUrls(), readFiles()],
1483
+ treeShake: false,
1484
+ })
1485
+
1486
+ // The $ref should remain unchanged because the prefix is already defined locally with $id
1487
+ expect(input.paths['/users'].get.responses['200'].content['application/json'].schema.$ref).toBe(
1488
+ `${url}/schema#/components/schemas/User`,
1489
+ )
1490
+
1491
+ // The external schema should not be bundled into x-ext
1492
+ expect(input['x-ext']).toBeUndefined()
1493
+
1494
+ // The local schema should remain unchanged
1495
+ expect(input.components.schemas.User).toEqual({
1496
+ type: 'object',
1497
+ properties: {
1498
+ name: { type: 'string' },
1499
+ },
1500
+ })
1501
+ })
1502
+
1503
+ it('prioritizes $id when resolving refs', async () => {
1504
+ const input = {
1505
+ $id: 'https://example.com/root',
1506
+ a: {
1507
+ b: {
1508
+ c: {
1509
+ $ref: '/b',
1510
+ },
1511
+ },
1512
+ },
1513
+ }
1514
+
1515
+ const fn = vi.fn()
1516
+
1517
+ await bundle(input, {
1518
+ treeShake: false,
1519
+ plugins: [
1520
+ {
1521
+ type: 'loader',
1522
+ validate: () => true,
1523
+ exec: async (value) => {
1524
+ fn(value)
1525
+ return {
1526
+ ok: true,
1527
+ data: {
1528
+ message: 'resolved value',
1529
+ },
1530
+ }
1531
+ },
1532
+ },
1533
+ ],
1534
+ })
1535
+
1536
+ expect(input).toEqual({
1537
+ '$id': 'https://example.com/root',
1538
+ 'a': {
1539
+ 'b': {
1540
+ 'c': {
1541
+ '$ref': '#/x-ext/69a42cc',
1542
+ },
1543
+ },
1544
+ },
1545
+ 'x-ext': {
1546
+ '69a42cc': {
1547
+ 'message': 'resolved value',
1548
+ },
1549
+ },
1550
+ })
1551
+
1552
+ expect(fn).toHaveBeenCalled()
1553
+ expect(fn).toHaveBeenCalledWith('https://example.com/b')
1554
+ })
1555
+
1556
+ it('prioritizes $id when resolving refs with origin #1', async () => {
1557
+ const url = `http://localhost:${port}`
1558
+
1559
+ const input = {
1560
+ $id: '/root',
1561
+ a: {
1562
+ b: {
1563
+ c: {
1564
+ $ref: '/b',
1565
+ },
1566
+ },
1567
+ },
1568
+ }
1569
+
1570
+ const fn = vi.fn()
1571
+
1572
+ await bundle(input, {
1573
+ treeShake: false,
1574
+ origin: url,
1575
+ plugins: [
1576
+ {
1577
+ type: 'loader',
1578
+ validate: () => true,
1579
+ exec: async (value) => {
1580
+ fn(value)
1581
+ return {
1582
+ ok: true,
1583
+ data: {
1584
+ message: 'resolved value',
1585
+ },
1586
+ }
1587
+ },
1588
+ },
1589
+ ],
1590
+ })
1591
+
1592
+ expect(input).toEqual({
1593
+ '$id': '/root',
1594
+ 'a': {
1595
+ 'b': {
1596
+ 'c': {
1597
+ '$ref': '#/x-ext/25c8e1f',
1598
+ },
1599
+ },
1600
+ },
1601
+ 'x-ext': {
1602
+ '25c8e1f': {
1603
+ 'message': 'resolved value',
1604
+ },
1605
+ },
1606
+ })
1607
+
1608
+ expect(fn).toHaveBeenCalled()
1609
+ expect(fn).toHaveBeenCalledWith('/b')
1610
+ })
1611
+
1612
+ it('prioritizes $id when resolving refs with origin #2', async () => {
1613
+ const url = `http://localhost:${port}`
1614
+
1615
+ const input = {
1616
+ $id: 'http://example.com/root',
1617
+ a: {
1618
+ b: {
1619
+ c: {
1620
+ $ref: '/b',
1621
+ },
1622
+ },
1623
+ },
1624
+ }
1625
+
1626
+ const fn = vi.fn()
1627
+
1628
+ await bundle(url, {
1629
+ treeShake: false,
1630
+ origin: url,
1631
+ plugins: [
1632
+ {
1633
+ type: 'loader',
1634
+ validate: () => true,
1635
+ exec: async (value) => {
1636
+ fn(value)
1637
+
1638
+ if (value === url) {
1639
+ return {
1640
+ ok: true,
1641
+ data: input,
1642
+ }
1643
+ }
1644
+
1645
+ return {
1646
+ ok: true,
1647
+ data: {
1648
+ message: 'resolved value',
1649
+ },
1650
+ }
1651
+ },
1652
+ },
1653
+ ],
1654
+ })
1655
+
1656
+ expect(input).toEqual({
1657
+ '$id': 'http://example.com/root',
1658
+ 'a': {
1659
+ 'b': {
1660
+ 'c': {
1661
+ '$ref': '#/x-ext/943da6f',
1662
+ },
1663
+ },
1664
+ },
1665
+ 'x-ext': {
1666
+ '943da6f': {
1667
+ 'message': 'resolved value',
1668
+ },
1669
+ },
1670
+ })
1671
+
1672
+ expect(fn).toHaveBeenCalledTimes(2)
1673
+ expect(fn.mock.calls[0][0]).toBe(url)
1674
+ expect(fn.mock.calls[1][0]).toBe('http://example.com/b')
1675
+ })
1676
+
1677
+ it('correctly bundles when doing a partial bundle with $anchor on a different context', async () => {
1678
+ const input = {
1679
+ a: {
1680
+ b: {
1681
+ c: {
1682
+ $ref: '#/e/f',
1683
+ },
1684
+ },
1685
+ },
1686
+ e: {
1687
+ $id: 'https://example.com/e',
1688
+ $anchor: 'my-anchor',
1689
+ f: {
1690
+ $ref: '#my-anchor',
1691
+ },
1692
+ g: {
1693
+ $ref: 'http://example.com',
1694
+ },
1695
+ },
1696
+ }
1697
+
1698
+ const fn = vi.fn()
1699
+
1700
+ await bundle(input.a, {
1701
+ treeShake: false,
1702
+ plugins: [
1703
+ {
1704
+ type: 'loader',
1705
+ validate: () => true,
1706
+ exec: async (value) => {
1707
+ fn(value)
1708
+ if (value === 'http://example.com') {
1709
+ return {
1710
+ ok: true,
1711
+ data: {
1712
+ message: 'resolved value',
1713
+ },
1714
+ }
1715
+ }
1716
+ return { ok: false }
1717
+ },
1718
+ },
1719
+ ],
1720
+ root: input,
1721
+ urlMap: true,
1722
+ cache: new Map(),
1723
+ })
1724
+
1725
+ expect(input).toEqual({
1726
+ 'a': {
1727
+ 'b': {
1728
+ 'c': {
1729
+ '$ref': '#/e/f',
1730
+ },
1731
+ },
1732
+ },
1733
+ 'e': {
1734
+ '$anchor': 'my-anchor',
1735
+ '$id': 'https://example.com/e',
1736
+ 'f': {
1737
+ '$ref': '#my-anchor',
1738
+ },
1739
+ 'g': {
1740
+ '$ref': '#/x-ext/89dce6a',
1741
+ },
1742
+ },
1743
+ 'x-ext': {
1744
+ '89dce6a': {
1745
+ 'message': 'resolved value',
1746
+ },
1747
+ },
1748
+ 'x-ext-urls': {
1749
+ '89dce6a': 'http://example.com',
1750
+ },
1751
+ })
1752
+ })
1753
+
1754
+ it('treats internal root pointers as internal references', async () => {
1755
+ resetConsoleSpies()
1756
+
1757
+ const input = {
1758
+ a: {
1759
+ $ref: '#/',
1760
+ },
1761
+ }
1762
+
1763
+ await bundle(input, { plugins: [fetchUrls()], treeShake: false })
1764
+
1765
+ expect(input).toEqual({
1766
+ a: {
1767
+ $ref: '#/',
1768
+ },
1769
+ })
1770
+
1771
+ expect(consoleWarnSpy).toHaveBeenCalledTimes(0)
1772
+ })
1328
1773
  })
1329
1774
 
1330
1775
  describe('local files', () => {
@@ -1405,7 +1850,9 @@ describe('bundle', () => {
1405
1850
  }
1406
1851
  const bName = randomUUID()
1407
1852
 
1408
- await fs.mkdir('./nested')
1853
+ await fs.mkdir('./nested').catch(() => {
1854
+ return
1855
+ })
1409
1856
  await fs.writeFile(`./nested/${bName}`, JSON.stringify(b))
1410
1857
  await fs.writeFile(`./nested/${cName}`, JSON.stringify(c))
1411
1858
 
@@ -1449,7 +1896,9 @@ describe('bundle', () => {
1449
1896
  }
1450
1897
  const bName = randomUUID()
1451
1898
 
1452
- await fs.mkdir('./nested')
1899
+ await fs.mkdir('./nested').catch(() => {
1900
+ return
1901
+ })
1453
1902
  await fs.writeFile(`./nested/${bName}`, JSON.stringify(b))
1454
1903
  await fs.writeFile(`./nested/${cName}`, JSON.stringify(c))
1455
1904
 
@@ -2333,17 +2782,6 @@ describe('isLocalRef', () => {
2333
2782
  })
2334
2783
  })
2335
2784
 
2336
- describe('getNestedValue', () => {
2337
- it.each([
2338
- [{ a: { b: { c: 'hello' } } }, ['a', 'b', 'c'], 'hello'],
2339
- [{ a: { b: { c: 'hello' } } }, [], { a: { b: { c: 'hello' } } }],
2340
- [{ foo: { bar: { baz: 42 } } }, ['foo', 'bar', 'baz'], 42],
2341
- [{ foo: { bar: { baz: 42 } } }, ['foo', 'non-existing', 'baz'], undefined],
2342
- ])('gets nested value', (a, b, c) => {
2343
- expect(getNestedValue(a, b)).toEqual(c)
2344
- })
2345
- })
2346
-
2347
2785
  describe('prefixInternalRef', () => {
2348
2786
  it.each([
2349
2787
  ['#/hello', ['prefix'], '#/prefix/hello'],
@@ -2385,3 +2823,73 @@ describe('setValueAtPath', () => {
2385
2823
  expect(a).toEqual(d)
2386
2824
  })
2387
2825
  })
2826
+
2827
+ describe('resolveAndCopyReferences', () => {
2828
+ const source = {
2829
+ openapi: '3.1.1',
2830
+ info: {
2831
+ title: 'Example API',
2832
+ version: '1.0.0',
2833
+ },
2834
+ paths: {
2835
+ '/': {
2836
+ get: {
2837
+ responses: {
2838
+ '200': {
2839
+ content: {
2840
+ 'application/json': {
2841
+ schema: {
2842
+ $ref: '#/components/schemas/User',
2843
+ },
2844
+ },
2845
+ },
2846
+ },
2847
+ },
2848
+ },
2849
+ },
2850
+ },
2851
+ components: {
2852
+ schemas: {
2853
+ User: {
2854
+ type: 'object',
2855
+ properties: {
2856
+ name: { type: 'string' },
2857
+ },
2858
+ },
2859
+ Person: {
2860
+ type: 'object',
2861
+ properties: {
2862
+ name: { type: 'string', default: 'John Doe' },
2863
+ },
2864
+ },
2865
+ },
2866
+ },
2867
+ }
2868
+
2869
+ it('correctly resolves and copies local references, and leaves out the rest', async () => {
2870
+ const target = {}
2871
+
2872
+ resolveAndCopyReferences(target, source, '/paths/~1', '', '', true)
2873
+
2874
+ expect(target).toEqual({
2875
+ paths: {
2876
+ '/': {
2877
+ get: {
2878
+ responses: {
2879
+ '200': {
2880
+ content: {
2881
+ 'application/json': { schema: { '$ref': '#/components/schemas/User' } },
2882
+ },
2883
+ },
2884
+ },
2885
+ },
2886
+ },
2887
+ },
2888
+ components: {
2889
+ schemas: {
2890
+ User: { type: 'object', properties: { name: { type: 'string' } } },
2891
+ },
2892
+ },
2893
+ })
2894
+ })
2895
+ })