@scalar/json-magic 0.6.0 → 0.7.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.
Files changed (46) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/CHANGELOG.md +22 -0
  3. package/dist/bundle/bundle.d.ts +1 -0
  4. package/dist/bundle/bundle.d.ts.map +1 -1
  5. package/dist/bundle/bundle.js +3 -3
  6. package/dist/bundle/bundle.js.map +2 -2
  7. package/dist/bundle/plugins/fetch-urls/index.d.ts.map +1 -1
  8. package/dist/bundle/plugins/fetch-urls/index.js +8 -1
  9. package/dist/bundle/plugins/fetch-urls/index.js.map +2 -2
  10. package/dist/bundle/plugins/parse-json/index.d.ts.map +1 -1
  11. package/dist/bundle/plugins/parse-json/index.js +7 -6
  12. package/dist/bundle/plugins/parse-json/index.js.map +2 -2
  13. package/dist/bundle/plugins/parse-yaml/index.d.ts.map +1 -1
  14. package/dist/bundle/plugins/parse-yaml/index.js +7 -6
  15. package/dist/bundle/plugins/parse-yaml/index.js.map +2 -2
  16. package/dist/bundle/plugins/read-files/index.d.ts.map +1 -1
  17. package/dist/bundle/plugins/read-files/index.js +2 -1
  18. package/dist/bundle/plugins/read-files/index.js.map +2 -2
  19. package/dist/diff/diff.d.ts.map +1 -1
  20. package/dist/diff/diff.js.map +2 -2
  21. package/dist/diff/utils.d.ts.map +1 -1
  22. package/dist/diff/utils.js.map +2 -2
  23. package/dist/magic-proxy/proxy.d.ts +7 -6
  24. package/dist/magic-proxy/proxy.d.ts.map +1 -1
  25. package/dist/magic-proxy/proxy.js +21 -11
  26. package/dist/magic-proxy/proxy.js.map +2 -2
  27. package/package.json +4 -4
  28. package/src/bundle/bundle.test.ts +278 -259
  29. package/src/bundle/bundle.ts +4 -4
  30. package/src/bundle/plugins/fetch-urls/index.test.ts +21 -24
  31. package/src/bundle/plugins/fetch-urls/index.ts +10 -0
  32. package/src/bundle/plugins/parse-json/index.test.ts +3 -1
  33. package/src/bundle/plugins/parse-json/index.ts +7 -6
  34. package/src/bundle/plugins/parse-yaml/index.test.ts +3 -1
  35. package/src/bundle/plugins/parse-yaml/index.ts +7 -6
  36. package/src/bundle/plugins/read-files/index.test.ts +4 -3
  37. package/src/bundle/plugins/read-files/index.ts +1 -0
  38. package/src/bundle/value-generator.test.ts +7 -8
  39. package/src/dereference/dereference.test.ts +6 -6
  40. package/src/diff/diff.ts +0 -1
  41. package/src/diff/utils.test.ts +2 -2
  42. package/src/diff/utils.ts +0 -2
  43. package/src/helpers/escape-json-pointer.test.ts +1 -1
  44. package/src/helpers/unescape-json-pointer.test.ts +1 -1
  45. package/src/magic-proxy/proxy.test.ts +108 -76
  46. package/src/magic-proxy/proxy.ts +34 -20
@@ -1154,41 +1154,41 @@ describe('createMagicProxy', () => {
1154
1154
  })
1155
1155
 
1156
1156
  describe('show underscore properties when specified', () => {
1157
- it('should not hide properties starting with underscore from direct access', () => {
1157
+ it('should not hide properties starting with __scalar_ from direct access', () => {
1158
1158
  const input = {
1159
1159
  public: 'visible',
1160
- _private: 'hidden',
1161
- __internal: 'also hidden',
1160
+ __scalar_private: 'hidden',
1161
+ __scalar_internal: 'also hidden',
1162
1162
  normal_underscore: 'visible with underscore in middle',
1163
1163
  }
1164
1164
 
1165
1165
  const result = createMagicProxy(input, { showInternal: true })
1166
1166
 
1167
1167
  expect(result.public).toBe('visible')
1168
- expect(result._private).toBe('hidden')
1169
- expect(result.__internal).toBe('also hidden')
1168
+ expect(result.__scalar_private).toBe('hidden')
1169
+ expect(result.__scalar_internal).toBe('also hidden')
1170
1170
  expect(result.normal_underscore).toBe('visible with underscore in middle')
1171
1171
  })
1172
1172
 
1173
- it('should not hide underscore properties from "in" operator', () => {
1173
+ it('should not hide __scalar_ properties from "in" operator', () => {
1174
1174
  const input = {
1175
1175
  public: 'visible',
1176
- _private: 'hidden',
1177
- __internal: 'also hidden',
1176
+ __scalar_private: 'hidden',
1177
+ __scalar_internal: 'also hidden',
1178
1178
  }
1179
1179
 
1180
1180
  const result = createMagicProxy(input, { showInternal: true })
1181
1181
 
1182
1182
  expect('public' in result).toBe(true)
1183
- expect('_private' in result).toBe(true)
1184
- expect('__internal' in result).toBe(true)
1183
+ expect('__scalar_private' in result).toBe(true)
1184
+ expect('__scalar_internal' in result).toBe(true)
1185
1185
  })
1186
1186
 
1187
- it('should not exclude underscore properties from Object.keys enumeration', () => {
1187
+ it('should not exclude __scalar_ properties from Object.keys enumeration', () => {
1188
1188
  const input = {
1189
1189
  public: 'visible',
1190
- _private: 'hidden',
1191
- __internal: 'also hidden',
1190
+ __scalar_private: 'hidden',
1191
+ __scalar_internal: 'also hidden',
1192
1192
  another: 'visible',
1193
1193
  }
1194
1194
 
@@ -1197,79 +1197,79 @@ describe('createMagicProxy', () => {
1197
1197
 
1198
1198
  expect(keys).toContain('public')
1199
1199
  expect(keys).toContain('another')
1200
- expect(keys).toContain('_private')
1201
- expect(keys).toContain('__internal')
1200
+ expect(keys).toContain('__scalar_private')
1201
+ expect(keys).toContain('__scalar_internal')
1202
1202
  })
1203
1203
 
1204
- it('should not hide underscore properties from getOwnPropertyDescriptor', () => {
1204
+ it('should not hide __scalar_ properties from getOwnPropertyDescriptor', () => {
1205
1205
  const input = {
1206
1206
  public: 'visible',
1207
- _private: 'hidden',
1207
+ __scalar_private: 'hidden',
1208
1208
  }
1209
1209
 
1210
1210
  const result = createMagicProxy(input, { showInternal: true })
1211
1211
 
1212
1212
  expect(Object.getOwnPropertyDescriptor(result, 'public')).toBeDefined()
1213
- expect(Object.getOwnPropertyDescriptor(result, '_private')).toBeDefined()
1213
+ expect(Object.getOwnPropertyDescriptor(result, '__scalar_private')).toBeDefined()
1214
1214
  })
1215
1215
 
1216
- it('should not hide underscore properties in nested objects', () => {
1216
+ it('should not hide __scalar_ properties in nested objects', () => {
1217
1217
  const input = {
1218
1218
  nested: {
1219
1219
  public: 'visible',
1220
- _private: 'hidden',
1220
+ __scalar_private: 'hidden',
1221
1221
  deeper: {
1222
- _alsoHidden: 'secret',
1222
+ __scalar_alsoHidden: 'secret',
1223
1223
  visible: 'shown',
1224
1224
  },
1225
1225
  },
1226
- _topLevel: 'hidden',
1226
+ __scalar_topLevel: 'hidden',
1227
1227
  }
1228
1228
 
1229
1229
  const result = createMagicProxy(input, { showInternal: true })
1230
1230
 
1231
- expect(result._topLevel).toBe('hidden')
1231
+ expect(result.__scalar_topLevel).toBe('hidden')
1232
1232
  expect(result.nested.public).toBe('visible')
1233
- expect(result.nested._private).toBe('hidden')
1234
- expect(result.nested.deeper._alsoHidden).toBe('secret')
1233
+ expect(result.nested.__scalar_private).toBe('hidden')
1234
+ expect(result.nested.deeper.__scalar_alsoHidden).toBe('secret')
1235
1235
  expect(result.nested.deeper.visible).toBe('shown')
1236
1236
  })
1237
1237
 
1238
- it('should show underscore properties with arrays containing objects with underscore properties', () => {
1238
+ it('should show __scalar_ properties with arrays containing objects with __scalar_ properties', () => {
1239
1239
  const input = {
1240
1240
  items: [
1241
- { public: 'item1', _private: 'hidden1' },
1242
- { public: 'item2', _private: 'hidden2' },
1241
+ { public: 'item1', __scalar_private: 'hidden1' },
1242
+ { public: 'item2', __scalar_private: 'hidden2' },
1243
1243
  ],
1244
1244
  }
1245
1245
 
1246
1246
  const result = createMagicProxy(input, { showInternal: true })
1247
1247
 
1248
1248
  expect(result.items[0].public).toBe('item1')
1249
- expect(result.items[0]._private).toBe('hidden1')
1249
+ expect(result.items[0].__scalar_private).toBe('hidden1')
1250
1250
  expect(result.items[1].public).toBe('item2')
1251
- expect(result.items[1]._private).toBe('hidden2')
1251
+ expect(result.items[1].__scalar_private).toBe('hidden2')
1252
1252
  })
1253
1253
 
1254
- it('should show underscore ref properties', () => {
1254
+ it('should show __scalar_ ref properties', () => {
1255
1255
  const input = {
1256
1256
  definitions: {
1257
1257
  example: {
1258
1258
  value: 'hello',
1259
- _internal: 'hidden',
1259
+ __scalar_internal: 'hidden',
1260
1260
  },
1261
1261
  },
1262
- _hiddenRef: { $ref: '#/definitions/example' },
1262
+ __scalar_hiddenRef: { $ref: '#/definitions/example' },
1263
1263
  publicRef: { $ref: '#/definitions/example' },
1264
1264
  }
1265
1265
 
1266
1266
  const result = createMagicProxy(input, { showInternal: true })
1267
1267
 
1268
- // Underscore property should be hidden
1269
- expect(result._hiddenRef).toEqual({
1268
+ // __scalar_ property should be hidden
1269
+ expect(result.__scalar_hiddenRef).toEqual({
1270
1270
  '$ref': '#/definitions/example',
1271
1271
  '$ref-value': {
1272
- '_internal': 'hidden',
1272
+ '__scalar_internal': 'hidden',
1273
1273
  'value': 'hello',
1274
1274
  },
1275
1275
  })
@@ -1277,8 +1277,8 @@ describe('createMagicProxy', () => {
1277
1277
  // Public ref should work normally
1278
1278
  expect(result.publicRef['$ref-value'].value).toBe('hello')
1279
1279
 
1280
- // Underscore properties in referenced objects should be hidden
1281
- expect(result.publicRef['$ref-value']._internal).toBe('hidden')
1280
+ // __scalar_ properties in referenced objects should be hidden
1281
+ expect(result.publicRef['$ref-value'].__scalar_internal).toBe('hidden')
1282
1282
  })
1283
1283
  })
1284
1284
 
@@ -1830,42 +1830,46 @@ describe('createMagicProxy', () => {
1830
1830
  })
1831
1831
  })
1832
1832
 
1833
- describe('hide underscore properties', () => {
1834
- it('should hide properties starting with underscore from direct access', () => {
1833
+ describe('hide __scalar_ properties', () => {
1834
+ it('should hide properties starting with __scalar_ from direct access', () => {
1835
1835
  const input = {
1836
1836
  public: 'visible',
1837
- _private: 'hidden',
1838
- __internal: 'also hidden',
1837
+ __scalar_private: 'hidden',
1838
+ __scalar_internal: 'also hidden',
1839
1839
  normal_underscore: 'visible with underscore in middle',
1840
+ _id: 'legitimate user property', // This should NOT be hidden
1841
+ _type: 'another legitimate property', // This should NOT be hidden
1840
1842
  }
1841
1843
 
1842
1844
  const result = createMagicProxy(input)
1843
1845
 
1844
1846
  expect(result.public).toBe('visible')
1845
- expect(result._private).toBe(undefined)
1846
- expect(result.__internal).toBe(undefined)
1847
+ expect(result.__scalar_private).toBe(undefined)
1848
+ expect(result.__scalar_internal).toBe(undefined)
1847
1849
  expect(result.normal_underscore).toBe('visible with underscore in middle')
1850
+ expect(result._id).toBe('legitimate user property') // Should be visible
1851
+ expect(result._type).toBe('another legitimate property') // Should be visible
1848
1852
  })
1849
1853
 
1850
- it('should hide underscore properties from "in" operator', () => {
1854
+ it('should hide __scalar_ properties from "in" operator', () => {
1851
1855
  const input = {
1852
1856
  public: 'visible',
1853
- _private: 'hidden',
1854
- __internal: 'also hidden',
1857
+ __scalar_private: 'hidden',
1858
+ __scalar_internal: 'also hidden',
1855
1859
  }
1856
1860
 
1857
1861
  const result = createMagicProxy(input)
1858
1862
 
1859
1863
  expect('public' in result).toBe(true)
1860
- expect('_private' in result).toBe(false)
1861
- expect('__internal' in result).toBe(false)
1864
+ expect('__scalar_private' in result).toBe(false)
1865
+ expect('__scalar_internal' in result).toBe(false)
1862
1866
  })
1863
1867
 
1864
- it('should exclude underscore properties from Object.keys enumeration', () => {
1868
+ it('should exclude __scalar_ properties from Object.keys enumeration', () => {
1865
1869
  const input = {
1866
1870
  public: 'visible',
1867
- _private: 'hidden',
1868
- __internal: 'also hidden',
1871
+ __scalar_private: 'hidden',
1872
+ __scalar_internal: 'also hidden',
1869
1873
  another: 'visible',
1870
1874
  }
1871
1875
 
@@ -1874,82 +1878,110 @@ describe('createMagicProxy', () => {
1874
1878
 
1875
1879
  expect(keys).toContain('public')
1876
1880
  expect(keys).toContain('another')
1877
- expect(keys).not.toContain('_private')
1878
- expect(keys).not.toContain('__internal')
1881
+ expect(keys).not.toContain('__scalar_private')
1882
+ expect(keys).not.toContain('__scalar_internal')
1879
1883
  })
1880
1884
 
1881
- it('should hide underscore properties from getOwnPropertyDescriptor', () => {
1885
+ it('should hide __scalar_ properties from getOwnPropertyDescriptor', () => {
1882
1886
  const input = {
1883
1887
  public: 'visible',
1884
- _private: 'hidden',
1888
+ __scalar_private: 'hidden',
1885
1889
  }
1886
1890
 
1887
1891
  const result = createMagicProxy(input)
1888
1892
 
1889
1893
  expect(Object.getOwnPropertyDescriptor(result, 'public')).toBeDefined()
1890
- expect(Object.getOwnPropertyDescriptor(result, '_private')).toBe(undefined)
1894
+ expect(Object.getOwnPropertyDescriptor(result, '__scalar_private')).toBe(undefined)
1891
1895
  })
1892
1896
 
1893
- it('should hide underscore properties in nested objects', () => {
1897
+ it('should hide __scalar_ properties in nested objects', () => {
1894
1898
  const input = {
1895
1899
  nested: {
1896
1900
  public: 'visible',
1897
- _private: 'hidden',
1901
+ __scalar_private: 'hidden',
1898
1902
  deeper: {
1899
- _alsoHidden: 'secret',
1903
+ __scalar_alsoHidden: 'secret',
1900
1904
  visible: 'shown',
1901
1905
  },
1902
1906
  },
1903
- _topLevel: 'hidden',
1907
+ __scalar_topLevel: 'hidden',
1904
1908
  }
1905
1909
 
1906
1910
  const result = createMagicProxy(input)
1907
1911
 
1908
- expect(result._topLevel).toBe(undefined)
1912
+ expect(result.__scalar_topLevel).toBe(undefined)
1909
1913
  expect(result.nested.public).toBe('visible')
1910
- expect(result.nested._private).toBe(undefined)
1911
- expect(result.nested.deeper._alsoHidden).toBe(undefined)
1914
+ expect(result.nested.__scalar_private).toBe(undefined)
1915
+ expect(result.nested.deeper.__scalar_alsoHidden).toBe(undefined)
1912
1916
  expect(result.nested.deeper.visible).toBe('shown')
1913
1917
  })
1914
1918
 
1915
- it('should work with arrays containing objects with underscore properties', () => {
1919
+ it('should work with arrays containing objects with __scalar_ properties', () => {
1916
1920
  const input = {
1917
1921
  items: [
1918
- { public: 'item1', _private: 'hidden1' },
1919
- { public: 'item2', _private: 'hidden2' },
1922
+ { public: 'item1', __scalar_private: 'hidden1' },
1923
+ { public: 'item2', __scalar_private: 'hidden2' },
1920
1924
  ],
1921
1925
  }
1922
1926
 
1923
1927
  const result = createMagicProxy(input)
1924
1928
 
1925
1929
  expect(result.items[0].public).toBe('item1')
1926
- expect(result.items[0]._private).toBe(undefined)
1930
+ expect(result.items[0].__scalar_private).toBe(undefined)
1927
1931
  expect(result.items[1].public).toBe('item2')
1928
- expect(result.items[1]._private).toBe(undefined)
1932
+ expect(result.items[1].__scalar_private).toBe(undefined)
1929
1933
  })
1930
1934
 
1931
- it('should still allow refs to work with underscore hiding', () => {
1935
+ it('should still allow refs to work with __scalar_ hiding', () => {
1932
1936
  const input = {
1933
1937
  definitions: {
1934
1938
  example: {
1935
1939
  value: 'hello',
1936
- _internal: 'hidden',
1940
+ __scalar_internal: 'hidden',
1937
1941
  },
1938
1942
  },
1939
- _hiddenRef: { $ref: '#/definitions/example' },
1943
+ __scalar_hiddenRef: { $ref: '#/definitions/example' },
1940
1944
  publicRef: { $ref: '#/definitions/example' },
1941
1945
  }
1942
1946
 
1943
1947
  const result = createMagicProxy(input)
1944
1948
 
1945
- // Underscore property should be hidden
1946
- expect(result._hiddenRef).toBe(undefined)
1949
+ // __scalar_ property should be hidden
1950
+ expect(result.__scalar_hiddenRef).toBe(undefined)
1947
1951
 
1948
1952
  // Public ref should work normally
1949
1953
  expect(result.publicRef['$ref-value'].value).toBe('hello')
1950
1954
 
1951
- // Underscore properties in referenced objects should be hidden
1952
- expect(result.publicRef['$ref-value']._internal).toBe(undefined)
1955
+ // __scalar_ properties in referenced objects should be hidden
1956
+ expect(result.publicRef['$ref-value'].__scalar_internal).toBe(undefined)
1957
+ })
1958
+
1959
+ it('preserves regular underscore properties', () => {
1960
+ const input = {
1961
+ _id: 'user123',
1962
+ user_name: 'john',
1963
+ __version: '1.0',
1964
+ normal_property: 'visible',
1965
+ }
1966
+
1967
+ const result = createMagicProxy(input)
1968
+
1969
+ // Regular underscore properties should be visible
1970
+ expect(result._id).toBe('user123')
1971
+ expect(result.user_name).toBe('john')
1972
+ expect(result.__version).toBe('1.0')
1973
+ expect(result.normal_property).toBe('visible')
1974
+
1975
+ // Should be included in enumeration and "in" checks
1976
+ expect('_id' in result).toBe(true)
1977
+ expect('user_name' in result).toBe(true)
1978
+ expect('__version' in result).toBe(true)
1979
+
1980
+ const keys = Object.keys(result)
1981
+ expect(keys).toContain('_id')
1982
+ expect(keys).toContain('user_name')
1983
+ expect(keys).toContain('__version')
1984
+ expect(keys).toContain('normal_property')
1953
1985
  })
1954
1986
  })
1955
1987
  })
@@ -18,7 +18,7 @@ const REF_KEY = '$ref'
18
18
  * Features:
19
19
  * - If an object contains a `$ref` property, accessing the special `$ref-value` property will resolve and return the referenced value from the root object.
20
20
  * - All nested objects and arrays are recursively wrapped in proxies, so reference resolution works at any depth.
21
- * - Properties starting with an underscore (_) are considered internal and are hidden by default: they return undefined on access, are excluded from enumeration, and `'in'` checks return false. This can be overridden with the `showInternal` option.
21
+ * - Properties starting with `__scalar_` are considered internal and are hidden by default: they return undefined on access, are excluded from enumeration, and `'in'` checks return false. This can be overridden with the `showInternal` option.
22
22
  * - Setting, deleting, and enumerating properties works as expected, including for proxied references.
23
23
  * - Ensures referential stability by caching proxies for the same target object.
24
24
  *
@@ -33,17 +33,17 @@ const REF_KEY = '$ref'
33
33
  * foo: { bar: 123 }
34
34
  * },
35
35
  * refObj: { $ref: '#/definitions/foo' },
36
- * _internal: 'hidden property'
36
+ * __scalar_internal: 'hidden property'
37
37
  * }
38
38
  * const proxy = createMagicProxy(input)
39
39
  *
40
40
  * // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }
41
41
  * console.log(proxy.refObj['$ref-value']) // { bar: 123 }
42
42
  *
43
- * // Properties starting with underscore are hidden
44
- * console.log(proxy._internal) // undefined
45
- * console.log('_internal' in proxy) // false
46
- * console.log(Object.keys(proxy)) // ['definitions', 'refObj'] (no '_internal')
43
+ * // Properties starting with __scalar_ are hidden
44
+ * console.log(proxy.__scalar_internal) // undefined
45
+ * console.log('__scalar_internal' in proxy) // false
46
+ * console.log(Object.keys(proxy)) // ['definitions', 'refObj'] (no '__scalar_internal')
47
47
  *
48
48
  * // Setting and deleting properties works as expected
49
49
  * proxy.refObj.extra = 'hello'
@@ -99,7 +99,7 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
99
99
  * Proxy "get" trap for magic proxy.
100
100
  * - If accessing the special isMagicProxy symbol, return true to identify proxy.
101
101
  * - If accessing the magicProxyTarget symbol, return the original target object.
102
- * - Hide properties starting with underscore by returning undefined.
102
+ * - Hide properties starting with __scalar_ by returning undefined.
103
103
  * - If accessing "$ref-value" and the object has a local $ref, resolve and return the referenced value as a new magic proxy.
104
104
  * - For all other properties, recursively wrap the returned value in a magic proxy (if applicable).
105
105
  */
@@ -114,9 +114,9 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
114
114
  return target
115
115
  }
116
116
 
117
- // Hide properties starting with underscore - these are considered internal/private properties
117
+ // Hide properties starting with __scalar_ - these are considered internal/private properties
118
118
  // and should not be accessible through the magic proxy interface
119
- if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
119
+ if (typeof prop === 'string' && prop.startsWith('__scalar_') && !options?.showInternal) {
120
120
  return undefined
121
121
  }
122
122
 
@@ -140,6 +140,10 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
140
140
 
141
141
  // Resolve the reference and create a new magic proxy
142
142
  const resolvedValue = getValueByPath(args.root, parseJsonPointer(`#/${path}`))
143
+ // Return early if the value is already a magic proxy
144
+ if (isMagicProxyObject(resolvedValue.value)) {
145
+ return resolvedValue.value
146
+ }
143
147
  const proxiedValue = createMagicProxy(resolvedValue.value, options, {
144
148
  ...args,
145
149
  currentContext: resolvedValue.context,
@@ -152,6 +156,12 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
152
156
 
153
157
  // For all other properties, recursively wrap the value in a magic proxy
154
158
  const value = Reflect.get(target, prop, receiver)
159
+
160
+ // Return early if the value is already a magic proxy
161
+ if (isMagicProxyObject(value)) {
162
+ return value
163
+ }
164
+
155
165
  return createMagicProxy(value as T, options, { ...args, currentContext: id ?? args.currentContext })
156
166
  },
157
167
  /**
@@ -159,13 +169,13 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
159
169
  * Allows setting properties on the proxied object.
160
170
  * This will update the underlying target object.
161
171
  *
162
- * Note: it will not update if the property starts with an underscore (_)
172
+ * Note: it will not update if the property starts with __scalar_
163
173
  * Those will be considered private properties by the proxy
164
174
  */
165
175
  set(target, prop, newValue, receiver) {
166
176
  const ref = Reflect.get(target, REF_KEY, receiver)
167
177
 
168
- if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
178
+ if (typeof prop === 'string' && prop.startsWith('__scalar_') && !options?.showInternal) {
169
179
  return true
170
180
  }
171
181
 
@@ -215,12 +225,12 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
215
225
  * - Pretend that "$ref-value" exists if "$ref" exists on the target.
216
226
  * This allows expressions like `"$ref-value" in obj` to return true for objects with a $ref,
217
227
  * even though "$ref-value" is a virtual property provided by the proxy.
218
- * - Hide properties starting with underscore by returning false.
228
+ * - Hide properties starting with __scalar_ by returning false.
219
229
  * - For all other properties, defer to the default Reflect.has behavior.
220
230
  */
221
231
  has(target, prop) {
222
- // Hide properties starting with underscore
223
- if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
232
+ // Hide properties starting with __scalar_
233
+ if (typeof prop === 'string' && prop.startsWith('__scalar_') && !options?.showInternal) {
224
234
  return false
225
235
  }
226
236
 
@@ -236,14 +246,14 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
236
246
  * - If the object has a "$ref" property, ensures that "$ref-value" is also included in the keys,
237
247
  * even though "$ref-value" is a virtual property provided by the proxy.
238
248
  * This allows Object.keys, Reflect.ownKeys, etc. to include "$ref-value" for objects with $ref.
239
- * - Filters out properties starting with underscore.
249
+ * - Filters out properties starting with __scalar_.
240
250
  */
241
251
  ownKeys(target) {
242
252
  const keys = Reflect.ownKeys(target)
243
253
 
244
- // Filter out properties starting with underscore
254
+ // Filter out properties starting with __scalar_
245
255
  const filteredKeys = keys.filter(
246
- (key) => typeof key !== 'string' || !(key.startsWith('_') && !options?.showInternal),
256
+ (key) => typeof key !== 'string' || !(key.startsWith('__scalar_') && !options?.showInternal),
247
257
  )
248
258
 
249
259
  if (REF_KEY in target && !filteredKeys.includes(REF_VALUE)) {
@@ -255,13 +265,13 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
255
265
  /**
256
266
  * Proxy "getOwnPropertyDescriptor" trap for magic proxy.
257
267
  * - For the virtual "$ref-value" property, returns a descriptor that makes it appear as a regular property.
258
- * - Hide properties starting with underscore by returning undefined.
268
+ * - Hide properties starting with __scalar_ by returning undefined.
259
269
  * - For all other properties, delegates to the default Reflect.getOwnPropertyDescriptor behavior.
260
270
  * - This ensures that Object.getOwnPropertyDescriptor and similar methods work correctly with the virtual property.
261
271
  */
262
272
  getOwnPropertyDescriptor(target, prop) {
263
- // Hide properties starting with underscore
264
- if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
273
+ // Hide properties starting with __scalar_
274
+ if (typeof prop === 'string' && prop.startsWith('__scalar_') && !options?.showInternal) {
265
275
  return undefined
266
276
  }
267
277
 
@@ -286,6 +296,10 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
286
296
  return proxied
287
297
  }
288
298
 
299
+ export const isMagicProxyObject = (obj: unknown): boolean => {
300
+ return typeof obj === 'object' && obj !== null && (obj as { [isMagicProxy]: boolean })[isMagicProxy] === true
301
+ }
302
+
289
303
  /**
290
304
  * Gets the raw (non-proxied) version of an object created by createMagicProxy.
291
305
  * This is useful when you need to access the original object without the magic proxy wrapper.