@scalar/json-magic 0.3.1 → 0.4.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.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +21 -0
- package/dist/bundle/value-generator.d.ts.map +1 -1
- package/dist/dereference/dereference.d.ts.map +1 -1
- package/dist/diff/apply.d.ts.map +1 -1
- package/dist/diff/diff.d.ts.map +1 -1
- package/dist/diff/merge.d.ts.map +1 -1
- package/dist/diff/utils.d.ts.map +1 -1
- package/dist/magic-proxy/proxy.d.ts +12 -2
- package/dist/magic-proxy/proxy.d.ts.map +1 -1
- package/dist/magic-proxy/proxy.js +34 -7
- package/dist/magic-proxy/proxy.js.map +2 -2
- package/dist/utils/is-object.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/magic-proxy/proxy.test.ts +254 -1
- package/src/magic-proxy/proxy.ts +57 -7
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
|
-
> @scalar/json-magic@0.
|
|
2
|
+
> @scalar/json-magic@0.4.1 build /home/runner/work/scalar/scalar/packages/json-magic
|
|
3
3
|
> scalar-build-esbuild
|
|
4
4
|
|
|
5
|
-
[34m@scalar/json-magic: Build completed in
|
|
5
|
+
[34m@scalar/json-magic: Build completed in 33.98ms[39m
|
|
6
6
|
|
|
7
|
-
> @scalar/json-magic@0.
|
|
7
|
+
> @scalar/json-magic@0.4.1 types:build /home/runner/work/scalar/scalar/packages/json-magic
|
|
8
8
|
> scalar-types-build
|
|
9
9
|
|
|
10
|
-
[32mTypes build completed in 1.
|
|
10
|
+
[32mTypes build completed in 1.54s[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @scalar/json-magic
|
|
2
2
|
|
|
3
|
+
## 0.4.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [821717b]
|
|
8
|
+
- @scalar/helpers@0.0.10
|
|
9
|
+
|
|
10
|
+
## 0.4.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- 99894bc: feat: correctly validate the schemas
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 06a46f0: fix: add proxy cache to fix reactivity issues
|
|
19
|
+
- 63283aa: fix: use hidden properties during validation
|
|
20
|
+
- Updated dependencies [98c55d0]
|
|
21
|
+
- Updated dependencies [0e747c7]
|
|
22
|
+
- @scalar/helpers@0.0.9
|
|
23
|
+
|
|
3
24
|
## 0.3.1
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"value-generator.d.ts","sourceRoot":"","sources":["../../src/bundle/value-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,mBAe1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACrD,KAAK,EAAE,MAAM,EACb,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,mBAAmB,CAAC,EAAE,MAAM,EAC5B,KAAK,SAAI,mBAoBV;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,2BAA2B,
|
|
1
|
+
{"version":3,"file":"value-generator.d.ts","sourceRoot":"","sources":["../../src/bundle/value-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,mBAe1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACrD,KAAK,EAAE,MAAM,EACb,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,mBAAmB,CAAC,EAAE,MAAM,EAC5B,KAAK,SAAI,mBAoBV;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,2BAA2B,GACtC,UAAU,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EACrD,mBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAMvC;;;;;;;;;;;;OAYG;sBACqB,MAAM;CAqBjC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dereference.d.ts","sourceRoot":"","sources":["../../src/dereference/dereference.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,KAAK,iBAAiB,GAClB;IACE,OAAO,EAAE,IAAI,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;CACpB,GACD;IACE,OAAO,EAAE,KAAK,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAEL,KAAK,uBAAuB,CAAC,GAAG,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,GACnF,iBAAiB,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"dereference.d.ts","sourceRoot":"","sources":["../../src/dereference/dereference.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,KAAK,iBAAiB,GAClB;IACE,OAAO,EAAE,IAAI,CAAA;IACb,IAAI,EAAE,aAAa,CAAA;CACpB,GACD;IACE,OAAO,EAAE,KAAK,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAEL,KAAK,uBAAuB,CAAC,GAAG,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,GACnF,iBAAiB,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,WAAW,GAAI,IAAI,SAAS;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EACzD,OAAO,aAAa,EACpB,UAAU,IAAI,KACb,uBAAuB,CAAC,IAAI,CAgC9B,CAAA"}
|
package/dist/diff/apply.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/diff/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/diff/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrD,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,MAAM,UAAU,CAAC,CAAC,CAAC,EAAE,KACpB,CAyCF,CAAA"}
|
package/dist/diff/diff.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,KAAK,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,EAAE,IAAI;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/diff/diff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,KAAK,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,EAAE,IAAI;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,oBA0C7F,CAAA"}
|
package/dist/diff/merge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/diff/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/diff/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE;;;CA8FtE,CAAA"}
|
package/dist/diff/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/diff/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/diff/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,GAAI,GAAG,OAAO,EAAE,GAAG,OAAO,YAsBrD,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAe3G,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,YAY7C,CAAA"}
|
|
@@ -7,6 +7,8 @@ import type { UnknownObject } from '../types.js';
|
|
|
7
7
|
* will resolve and return the referenced value from the root object.
|
|
8
8
|
* - All nested objects and arrays are recursively wrapped in proxies, so reference resolution
|
|
9
9
|
* works at any depth.
|
|
10
|
+
* - Properties starting with an underscore (_) are hidden and will not be accessible through
|
|
11
|
+
* the proxy (returns undefined on access, false on 'in' checks, excluded from enumeration).
|
|
10
12
|
* - Setting, deleting, and enumerating properties works as expected, including for proxied references.
|
|
11
13
|
*
|
|
12
14
|
* @param target - The object or array to wrap in a magic proxy
|
|
@@ -18,18 +20,26 @@ import type { UnknownObject } from '../types.js';
|
|
|
18
20
|
* definitions: {
|
|
19
21
|
* foo: { bar: 123 }
|
|
20
22
|
* },
|
|
21
|
-
* refObj: { $ref: '#/definitions/foo' }
|
|
23
|
+
* refObj: { $ref: '#/definitions/foo' },
|
|
24
|
+
* _internal: 'hidden property'
|
|
22
25
|
* }
|
|
23
26
|
* const proxy = createMagicProxy(input)
|
|
24
27
|
*
|
|
25
28
|
* // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }
|
|
26
29
|
* console.log(proxy.refObj['$ref-value']) // { bar: 123 }
|
|
27
30
|
*
|
|
31
|
+
* // Properties starting with underscore are hidden
|
|
32
|
+
* console.log(proxy._internal) // undefined
|
|
33
|
+
* console.log('_internal' in proxy) // false
|
|
34
|
+
* console.log(Object.keys(proxy)) // ['definitions', 'refObj'] (no '_internal')
|
|
35
|
+
*
|
|
28
36
|
* // Setting and deleting properties works as expected
|
|
29
37
|
* proxy.refObj.extra = 'hello'
|
|
30
38
|
* delete proxy.refObj.extra
|
|
31
39
|
*/
|
|
32
|
-
export declare const createMagicProxy: <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(target: T,
|
|
40
|
+
export declare const createMagicProxy: <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(target: T, options?: {
|
|
41
|
+
showInternal?: boolean;
|
|
42
|
+
}, root?: S | T, cache?: Map<string, unknown>, proxyCache?: WeakMap<object, T>) => T;
|
|
33
43
|
/**
|
|
34
44
|
* Gets the raw (non-proxied) version of an object created by createMagicProxy.
|
|
35
45
|
* This is useful when you need to access the original object without the magic proxy wrapper.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/magic-proxy/proxy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAW5C
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/magic-proxy/proxy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAW5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,aAAa,EACnG,QAAQ,CAAC,EACT,UAAU;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,EACpC,OAAM,CAAC,GAAG,CAAU,EACpB,4BAAkC,EAClC,+BAAqC,MAgLtC,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAUnC"}
|
|
@@ -6,15 +6,19 @@ const isMagicProxy = Symbol("isMagicProxy");
|
|
|
6
6
|
const magicProxyTarget = Symbol("magicProxyTarget");
|
|
7
7
|
const REF_VALUE = "$ref-value";
|
|
8
8
|
const REF_KEY = "$ref";
|
|
9
|
-
const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map()) => {
|
|
9
|
+
const createMagicProxy = (target, options, root = target, cache = /* @__PURE__ */ new Map(), proxyCache = /* @__PURE__ */ new WeakMap()) => {
|
|
10
10
|
if (!isObject(target) && !Array.isArray(target)) {
|
|
11
11
|
return target;
|
|
12
12
|
}
|
|
13
|
+
if (proxyCache.has(target)) {
|
|
14
|
+
return proxyCache.get(target);
|
|
15
|
+
}
|
|
13
16
|
const handler = {
|
|
14
17
|
/**
|
|
15
18
|
* Proxy "get" trap for magic proxy.
|
|
16
19
|
* - If accessing the special isMagicProxy symbol, return true to identify proxy.
|
|
17
20
|
* - If accessing the magicProxyTarget symbol, return the original target object.
|
|
21
|
+
* - Hide properties starting with underscore by returning undefined.
|
|
18
22
|
* - If accessing "$ref-value" and the object has a local $ref, resolve and return the referenced value as a new magic proxy.
|
|
19
23
|
* - For all other properties, recursively wrap the returned value in a magic proxy (if applicable).
|
|
20
24
|
*/
|
|
@@ -26,25 +30,34 @@ const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map
|
|
|
26
30
|
return target2;
|
|
27
31
|
}
|
|
28
32
|
const ref = Reflect.get(target2, REF_KEY, receiver);
|
|
33
|
+
if (typeof prop === "string" && prop.startsWith("_") && !options?.showInternal) {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
29
36
|
if (prop === REF_VALUE && typeof ref === "string" && isLocalRef(ref)) {
|
|
30
37
|
if (cache.has(ref)) {
|
|
31
38
|
return cache.get(ref);
|
|
32
39
|
}
|
|
33
40
|
const resolvedValue = getValueByPath(root, parseJsonPointer(ref));
|
|
34
|
-
const proxiedValue = createMagicProxy(resolvedValue, root, cache);
|
|
41
|
+
const proxiedValue = createMagicProxy(resolvedValue, options, root, cache);
|
|
35
42
|
cache.set(ref, proxiedValue);
|
|
36
43
|
return proxiedValue;
|
|
37
44
|
}
|
|
38
45
|
const value = Reflect.get(target2, prop, receiver);
|
|
39
|
-
return createMagicProxy(value, root, cache);
|
|
46
|
+
return createMagicProxy(value, options, root, cache, proxyCache);
|
|
40
47
|
},
|
|
41
48
|
/**
|
|
42
49
|
* Proxy "set" trap for magic proxy.
|
|
43
50
|
* Allows setting properties on the proxied object.
|
|
44
51
|
* This will update the underlying target object.
|
|
52
|
+
*
|
|
53
|
+
* Note: it will not update if the property starts with an underscore (_)
|
|
54
|
+
* Those will be considered private properties by the proxy
|
|
45
55
|
*/
|
|
46
56
|
set(target2, prop, newValue, receiver) {
|
|
47
57
|
const ref = Reflect.get(target2, REF_KEY, receiver);
|
|
58
|
+
if (typeof prop === "string" && prop.startsWith("_") && !options?.showInternal) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
48
61
|
if (prop === REF_VALUE && typeof ref === "string" && isLocalRef(ref)) {
|
|
49
62
|
const segments = getSegmentsFromPath(ref);
|
|
50
63
|
if (segments.length === 0) {
|
|
@@ -72,9 +85,13 @@ const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map
|
|
|
72
85
|
* - Pretend that "$ref-value" exists if "$ref" exists on the target.
|
|
73
86
|
* This allows expressions like `"$ref-value" in obj` to return true for objects with a $ref,
|
|
74
87
|
* even though "$ref-value" is a virtual property provided by the proxy.
|
|
88
|
+
* - Hide properties starting with underscore by returning false.
|
|
75
89
|
* - For all other properties, defer to the default Reflect.has behavior.
|
|
76
90
|
*/
|
|
77
91
|
has(target2, prop) {
|
|
92
|
+
if (typeof prop === "string" && prop.startsWith("_") && !options?.showInternal) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
78
95
|
if (prop === REF_VALUE && REF_KEY in target2) {
|
|
79
96
|
return true;
|
|
80
97
|
}
|
|
@@ -86,21 +103,29 @@ const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map
|
|
|
86
103
|
* - If the object has a "$ref" property, ensures that "$ref-value" is also included in the keys,
|
|
87
104
|
* even though "$ref-value" is a virtual property provided by the proxy.
|
|
88
105
|
* This allows Object.keys, Reflect.ownKeys, etc. to include "$ref-value" for objects with $ref.
|
|
106
|
+
* - Filters out properties starting with underscore.
|
|
89
107
|
*/
|
|
90
108
|
ownKeys(target2) {
|
|
91
109
|
const keys = Reflect.ownKeys(target2);
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
const filteredKeys = keys.filter(
|
|
111
|
+
(key) => typeof key !== "string" || !(key.startsWith("_") && !options?.showInternal)
|
|
112
|
+
);
|
|
113
|
+
if (REF_KEY in target2 && !filteredKeys.includes(REF_VALUE)) {
|
|
114
|
+
filteredKeys.push(REF_VALUE);
|
|
94
115
|
}
|
|
95
|
-
return
|
|
116
|
+
return filteredKeys;
|
|
96
117
|
},
|
|
97
118
|
/**
|
|
98
119
|
* Proxy "getOwnPropertyDescriptor" trap for magic proxy.
|
|
99
120
|
* - For the virtual "$ref-value" property, returns a descriptor that makes it appear as a regular property.
|
|
121
|
+
* - Hide properties starting with underscore by returning undefined.
|
|
100
122
|
* - For all other properties, delegates to the default Reflect.getOwnPropertyDescriptor behavior.
|
|
101
123
|
* - This ensures that Object.getOwnPropertyDescriptor and similar methods work correctly with the virtual property.
|
|
102
124
|
*/
|
|
103
125
|
getOwnPropertyDescriptor(target2, prop) {
|
|
126
|
+
if (typeof prop === "string" && prop.startsWith("_") && !options?.showInternal) {
|
|
127
|
+
return void 0;
|
|
128
|
+
}
|
|
104
129
|
const ref = Reflect.get(target2, REF_KEY);
|
|
105
130
|
if (prop === REF_VALUE && typeof ref === "string") {
|
|
106
131
|
return {
|
|
@@ -113,7 +138,9 @@ const createMagicProxy = (target, root = target, cache = /* @__PURE__ */ new Map
|
|
|
113
138
|
return Reflect.getOwnPropertyDescriptor(target2, prop);
|
|
114
139
|
}
|
|
115
140
|
};
|
|
116
|
-
|
|
141
|
+
const proxied = new Proxy(target, handler);
|
|
142
|
+
proxyCache.set(target, proxied);
|
|
143
|
+
return proxied;
|
|
117
144
|
};
|
|
118
145
|
function getRaw(obj) {
|
|
119
146
|
if (typeof obj !== "object" || obj === null) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/magic-proxy/proxy.ts"],
|
|
4
|
-
"sourcesContent": ["import { isLocalRef } from '@/bundle/bundle'\nimport type { UnknownObject } from '@/types'\nimport { getSegmentsFromPath } from '@/utils/get-segments-from-path'\nimport { isObject } from '@/utils/is-object'\nimport { getValueByPath, parseJsonPointer } from '@/utils/json-path-utils'\n\nconst isMagicProxy = Symbol('isMagicProxy')\nconst magicProxyTarget = Symbol('magicProxyTarget')\n\nconst REF_VALUE = '$ref-value'\nconst REF_KEY = '$ref'\n\n/**\n * Creates a \"magic\" proxy for a given object or array, enabling transparent access to\n * JSON Reference ($ref) values as if they were directly present on the object.\n *\n * - If an object contains a `$ref` property, accessing the special `$ref-value` property\n * will resolve and return the referenced value from the root object.\n * - All nested objects and arrays are recursively wrapped in proxies, so reference resolution\n * works at any depth.\n * - Setting, deleting, and enumerating properties works as expected, including for proxied references.\n *\n * @param target - The object or array to wrap in a magic proxy\n * @param root - The root object for resolving local JSON references (defaults to target)\n * @returns A proxied version of the input object/array with magic $ref-value support\n *\n * @example\n * const input = {\n * definitions: {\n * foo: { bar: 123 }\n * },\n * refObj: { $ref: '#/definitions/foo' }\n * }\n * const proxy = createMagicProxy(input)\n *\n * // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }\n * console.log(proxy.refObj['$ref-value']) // { bar: 123 }\n *\n * // Setting and deleting properties works as expected\n * proxy.refObj.extra = 'hello'\n * delete proxy.refObj.extra\n */\nexport const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(\n target: T,\n root: S | T = target,\n cache = new Map<string, unknown>(),\n) => {\n if (!isObject(target) && !Array.isArray(target)) {\n return target\n }\n\n const handler: ProxyHandler<T> = {\n /**\n * Proxy \"get\" trap for magic proxy.\n * - If accessing the special isMagicProxy symbol, return true to identify proxy.\n * - If accessing the magicProxyTarget symbol, return the original target object.\n * - If accessing \"$ref-value\" and the object has a local $ref, resolve and return the referenced value as a new magic proxy.\n * - For all other properties, recursively wrap the returned value in a magic proxy (if applicable).\n */\n get(target, prop, receiver) {\n if (prop === isMagicProxy) {\n // Used to identify if an object is a magic proxy\n return true\n }\n\n if (prop === magicProxyTarget) {\n // Used to retrieve the original target object from the proxy\n return target\n }\n\n const ref = Reflect.get(target, REF_KEY, receiver)\n\n // If accessing \"$ref-value\" and $ref is a local reference, resolve and return the referenced value\n if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {\n // Check cache first for performance optimization\n if (cache.has(ref)) {\n return cache.get(ref)\n }\n\n // Resolve the reference and create a new magic proxy\n const resolvedValue = getValueByPath(root, parseJsonPointer(ref))\n const proxiedValue = createMagicProxy(resolvedValue, root, cache)\n\n // Store in cache for future lookups\n cache.set(ref, proxiedValue)\n return proxiedValue\n }\n\n // For all other properties, recursively wrap the value in a magic proxy\n const value = Reflect.get(target, prop, receiver)\n return createMagicProxy(value, root, cache)\n },\n /**\n * Proxy \"set\" trap for magic proxy.\n * Allows setting properties on the proxied object.\n * This will update the underlying target object.\n */\n set(target, prop, newValue, receiver) {\n const ref = Reflect.get(target, REF_KEY, receiver)\n\n if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {\n const segments = getSegmentsFromPath(ref)\n\n if (segments.length === 0) {\n return false // Can not set top level $ref-value\n }\n\n const parentNode = getValueByPath(root, segments.slice(0, -1))\n\n // TODO: Maybe we create the path if it does not exist?\n // TODO: This can allow for invalid references to not throw errors\n if (!parentNode || (!isObject(parentNode) && !Array.isArray(parentNode))) {\n return false // Parent node does not exist, cannot set $ref-value\n }\n parentNode[segments.at(-1)] = newValue\n return true\n }\n\n return Reflect.set(target, prop, newValue, receiver)\n },\n /**\n * Proxy \"deleteProperty\" trap for magic proxy.\n * Allows deleting properties from the proxied object.\n * This will update the underlying target object.\n */\n deleteProperty(target, prop) {\n return Reflect.deleteProperty(target, prop)\n },\n /**\n * Proxy \"has\" trap for magic proxy.\n * - Pretend that \"$ref-value\" exists if \"$ref\" exists on the target.\n * This allows expressions like `\"$ref-value\" in obj` to return true for objects with a $ref,\n * even though \"$ref-value\" is a virtual property provided by the proxy.\n * - For all other properties, defer to the default Reflect.has behavior.\n */\n has(target, prop) {\n // Pretend that \"$ref-value\" exists if \"$ref\" exists\n if (prop === REF_VALUE && REF_KEY in target) {\n return true\n }\n return Reflect.has(target, prop)\n },\n /**\n * Proxy \"ownKeys\" trap for magic proxy.\n * - Returns the list of own property keys for the proxied object.\n * - If the object has a \"$ref\" property, ensures that \"$ref-value\" is also included in the keys,\n * even though \"$ref-value\" is a virtual property provided by the proxy.\n * This allows Object.keys, Reflect.ownKeys, etc. to include \"$ref-value\" for objects with $ref.\n */\n ownKeys(target) {\n const keys = Reflect.ownKeys(target)\n if (REF_KEY in target && !keys.includes(REF_VALUE)) {\n keys.push(REF_VALUE)\n }\n return keys\n },\n\n /**\n * Proxy \"getOwnPropertyDescriptor\" trap for magic proxy.\n * - For the virtual \"$ref-value\" property, returns a descriptor that makes it appear as a regular property.\n * - For all other properties, delegates to the default Reflect.getOwnPropertyDescriptor behavior.\n * - This ensures that Object.getOwnPropertyDescriptor and similar methods work correctly with the virtual property.\n */\n getOwnPropertyDescriptor(target, prop) {\n const ref = Reflect.get(target, REF_KEY)\n\n if (prop === REF_VALUE && typeof ref === 'string') {\n return {\n configurable: true,\n enumerable: true,\n value: undefined,\n writable: false,\n }\n }\n\n // Otherwise, delegate to the default behavior\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n }\n\n return new Proxy<T>(target, handler)\n}\n\n/**\n * Gets the raw (non-proxied) version of an object created by createMagicProxy.\n * This is useful when you need to access the original object without the magic proxy wrapper.\n *\n * @param obj - The magic proxy object to get the raw version of\n * @returns The raw version of the object\n * @example\n * const proxy = createMagicProxy({ foo: { $ref: '#/bar' } })\n * const raw = getRaw(proxy) // { foo: { $ref: '#/bar' } }\n */\nexport function getRaw<T>(obj: T): T {\n if (typeof obj !== 'object' || obj === null) {\n return obj\n }\n\n if ((obj as T & { [isMagicProxy]: boolean | undefined })[isMagicProxy]) {\n return (obj as T & { [magicProxyTarget]: T })[magicProxyTarget]\n }\n\n return obj\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,kBAAkB;AAE3B,SAAS,2BAA2B;AACpC,SAAS,gBAAgB;AACzB,SAAS,gBAAgB,wBAAwB;AAEjD,MAAM,eAAe,OAAO,cAAc;AAC1C,MAAM,mBAAmB,OAAO,kBAAkB;AAElD,MAAM,YAAY;AAClB,MAAM,UAAU;
|
|
4
|
+
"sourcesContent": ["import { isLocalRef } from '@/bundle/bundle'\nimport type { UnknownObject } from '@/types'\nimport { getSegmentsFromPath } from '@/utils/get-segments-from-path'\nimport { isObject } from '@/utils/is-object'\nimport { getValueByPath, parseJsonPointer } from '@/utils/json-path-utils'\n\nconst isMagicProxy = Symbol('isMagicProxy')\nconst magicProxyTarget = Symbol('magicProxyTarget')\n\nconst REF_VALUE = '$ref-value'\nconst REF_KEY = '$ref'\n\n/**\n * Creates a \"magic\" proxy for a given object or array, enabling transparent access to\n * JSON Reference ($ref) values as if they were directly present on the object.\n *\n * - If an object contains a `$ref` property, accessing the special `$ref-value` property\n * will resolve and return the referenced value from the root object.\n * - All nested objects and arrays are recursively wrapped in proxies, so reference resolution\n * works at any depth.\n * - Properties starting with an underscore (_) are hidden and will not be accessible through\n * the proxy (returns undefined on access, false on 'in' checks, excluded from enumeration).\n * - Setting, deleting, and enumerating properties works as expected, including for proxied references.\n *\n * @param target - The object or array to wrap in a magic proxy\n * @param root - The root object for resolving local JSON references (defaults to target)\n * @returns A proxied version of the input object/array with magic $ref-value support\n *\n * @example\n * const input = {\n * definitions: {\n * foo: { bar: 123 }\n * },\n * refObj: { $ref: '#/definitions/foo' },\n * _internal: 'hidden property'\n * }\n * const proxy = createMagicProxy(input)\n *\n * // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }\n * console.log(proxy.refObj['$ref-value']) // { bar: 123 }\n *\n * // Properties starting with underscore are hidden\n * console.log(proxy._internal) // undefined\n * console.log('_internal' in proxy) // false\n * console.log(Object.keys(proxy)) // ['definitions', 'refObj'] (no '_internal')\n *\n * // Setting and deleting properties works as expected\n * proxy.refObj.extra = 'hello'\n * delete proxy.refObj.extra\n */\nexport const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(\n target: T,\n options?: { showInternal?: boolean },\n root: S | T = target,\n cache = new Map<string, unknown>(),\n proxyCache = new WeakMap<object, T>(),\n) => {\n if (!isObject(target) && !Array.isArray(target)) {\n return target\n }\n\n // Return existing proxy for the same target to ensure referential stability\n if (proxyCache.has(target)) {\n return proxyCache.get(target)\n }\n\n const handler: ProxyHandler<T> = {\n /**\n * Proxy \"get\" trap for magic proxy.\n * - If accessing the special isMagicProxy symbol, return true to identify proxy.\n * - If accessing the magicProxyTarget symbol, return the original target object.\n * - Hide properties starting with underscore by returning undefined.\n * - If accessing \"$ref-value\" and the object has a local $ref, resolve and return the referenced value as a new magic proxy.\n * - For all other properties, recursively wrap the returned value in a magic proxy (if applicable).\n */\n get(target, prop, receiver) {\n if (prop === isMagicProxy) {\n // Used to identify if an object is a magic proxy\n return true\n }\n\n if (prop === magicProxyTarget) {\n // Used to retrieve the original target object from the proxy\n return target\n }\n\n const ref = Reflect.get(target, REF_KEY, receiver)\n\n // Hide properties starting with underscore - these are considered internal/private properties\n // and should not be accessible through the magic proxy interface\n if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {\n return undefined\n }\n\n // If accessing \"$ref-value\" and $ref is a local reference, resolve and return the referenced value\n if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {\n // Check cache first for performance optimization\n if (cache.has(ref)) {\n return cache.get(ref)\n }\n\n // Resolve the reference and create a new magic proxy\n const resolvedValue = getValueByPath(root, parseJsonPointer(ref))\n const proxiedValue = createMagicProxy(resolvedValue, options, root, cache)\n\n // Store in cache for future lookups\n cache.set(ref, proxiedValue)\n return proxiedValue\n }\n\n // For all other properties, recursively wrap the value in a magic proxy\n const value = Reflect.get(target, prop, receiver)\n return createMagicProxy(value as T, options, root, cache, proxyCache)\n },\n /**\n * Proxy \"set\" trap for magic proxy.\n * Allows setting properties on the proxied object.\n * This will update the underlying target object.\n *\n * Note: it will not update if the property starts with an underscore (_)\n * Those will be considered private properties by the proxy\n */\n set(target, prop, newValue, receiver) {\n const ref = Reflect.get(target, REF_KEY, receiver)\n\n if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {\n return true\n }\n\n if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {\n const segments = getSegmentsFromPath(ref)\n\n if (segments.length === 0) {\n return false // Can not set top level $ref-value\n }\n\n const parentNode = getValueByPath(root, segments.slice(0, -1))\n\n // TODO: Maybe we create the path if it does not exist?\n // TODO: This can allow for invalid references to not throw errors\n if (!parentNode || (!isObject(parentNode) && !Array.isArray(parentNode))) {\n return false // Parent node does not exist, cannot set $ref-value\n }\n parentNode[segments.at(-1)] = newValue\n return true\n }\n\n return Reflect.set(target, prop, newValue, receiver)\n },\n /**\n * Proxy \"deleteProperty\" trap for magic proxy.\n * Allows deleting properties from the proxied object.\n * This will update the underlying target object.\n */\n deleteProperty(target, prop) {\n return Reflect.deleteProperty(target, prop)\n },\n /**\n * Proxy \"has\" trap for magic proxy.\n * - Pretend that \"$ref-value\" exists if \"$ref\" exists on the target.\n * This allows expressions like `\"$ref-value\" in obj` to return true for objects with a $ref,\n * even though \"$ref-value\" is a virtual property provided by the proxy.\n * - Hide properties starting with underscore by returning false.\n * - For all other properties, defer to the default Reflect.has behavior.\n */\n has(target, prop) {\n // Hide properties starting with underscore\n if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {\n return false\n }\n\n // Pretend that \"$ref-value\" exists if \"$ref\" exists\n if (prop === REF_VALUE && REF_KEY in target) {\n return true\n }\n return Reflect.has(target, prop)\n },\n /**\n * Proxy \"ownKeys\" trap for magic proxy.\n * - Returns the list of own property keys for the proxied object.\n * - If the object has a \"$ref\" property, ensures that \"$ref-value\" is also included in the keys,\n * even though \"$ref-value\" is a virtual property provided by the proxy.\n * This allows Object.keys, Reflect.ownKeys, etc. to include \"$ref-value\" for objects with $ref.\n * - Filters out properties starting with underscore.\n */\n ownKeys(target) {\n const keys = Reflect.ownKeys(target)\n\n // Filter out properties starting with underscore\n const filteredKeys = keys.filter(\n (key) => typeof key !== 'string' || !(key.startsWith('_') && !options?.showInternal),\n )\n\n if (REF_KEY in target && !filteredKeys.includes(REF_VALUE)) {\n filteredKeys.push(REF_VALUE)\n }\n return filteredKeys\n },\n\n /**\n * Proxy \"getOwnPropertyDescriptor\" trap for magic proxy.\n * - For the virtual \"$ref-value\" property, returns a descriptor that makes it appear as a regular property.\n * - Hide properties starting with underscore by returning undefined.\n * - For all other properties, delegates to the default Reflect.getOwnPropertyDescriptor behavior.\n * - This ensures that Object.getOwnPropertyDescriptor and similar methods work correctly with the virtual property.\n */\n getOwnPropertyDescriptor(target, prop) {\n // Hide properties starting with underscore\n if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {\n return undefined\n }\n\n const ref = Reflect.get(target, REF_KEY)\n\n if (prop === REF_VALUE && typeof ref === 'string') {\n return {\n configurable: true,\n enumerable: true,\n value: undefined,\n writable: false,\n }\n }\n\n // Otherwise, delegate to the default behavior\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n }\n\n const proxied = new Proxy<T>(target, handler)\n proxyCache.set(target, proxied)\n return proxied\n}\n\n/**\n * Gets the raw (non-proxied) version of an object created by createMagicProxy.\n * This is useful when you need to access the original object without the magic proxy wrapper.\n *\n * @param obj - The magic proxy object to get the raw version of\n * @returns The raw version of the object\n * @example\n * const proxy = createMagicProxy({ foo: { $ref: '#/bar' } })\n * const raw = getRaw(proxy) // { foo: { $ref: '#/bar' } }\n */\nexport function getRaw<T>(obj: T): T {\n if (typeof obj !== 'object' || obj === null) {\n return obj\n }\n\n if ((obj as T & { [isMagicProxy]: boolean | undefined })[isMagicProxy]) {\n return (obj as T & { [magicProxyTarget]: T })[magicProxyTarget]\n }\n\n return obj\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kBAAkB;AAE3B,SAAS,2BAA2B;AACpC,SAAS,gBAAgB;AACzB,SAAS,gBAAgB,wBAAwB;AAEjD,MAAM,eAAe,OAAO,cAAc;AAC1C,MAAM,mBAAmB,OAAO,kBAAkB;AAElD,MAAM,YAAY;AAClB,MAAM,UAAU;AAwCT,MAAM,mBAAmB,CAC9B,QACA,SACA,OAAc,QACd,QAAQ,oBAAI,IAAqB,GACjC,aAAa,oBAAI,QAAmB,MACjC;AACH,MAAI,CAAC,SAAS,MAAM,KAAK,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC/C,WAAO;AAAA,EACT;AAGA,MAAI,WAAW,IAAI,MAAM,GAAG;AAC1B,WAAO,WAAW,IAAI,MAAM;AAAA,EAC9B;AAEA,QAAM,UAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAS/B,IAAIA,SAAQ,MAAM,UAAU;AAC1B,UAAI,SAAS,cAAc;AAEzB,eAAO;AAAA,MACT;AAEA,UAAI,SAAS,kBAAkB;AAE7B,eAAOA;AAAA,MACT;AAEA,YAAM,MAAM,QAAQ,IAAIA,SAAQ,SAAS,QAAQ;AAIjD,UAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG,KAAK,CAAC,SAAS,cAAc;AAC9E,eAAO;AAAA,MACT;AAGA,UAAI,SAAS,aAAa,OAAO,QAAQ,YAAY,WAAW,GAAG,GAAG;AAEpE,YAAI,MAAM,IAAI,GAAG,GAAG;AAClB,iBAAO,MAAM,IAAI,GAAG;AAAA,QACtB;AAGA,cAAM,gBAAgB,eAAe,MAAM,iBAAiB,GAAG,CAAC;AAChE,cAAM,eAAe,iBAAiB,eAAe,SAAS,MAAM,KAAK;AAGzE,cAAM,IAAI,KAAK,YAAY;AAC3B,eAAO;AAAA,MACT;AAGA,YAAM,QAAQ,QAAQ,IAAIA,SAAQ,MAAM,QAAQ;AAChD,aAAO,iBAAiB,OAAY,SAAS,MAAM,OAAO,UAAU;AAAA,IACtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,IAAIA,SAAQ,MAAM,UAAU,UAAU;AACpC,YAAM,MAAM,QAAQ,IAAIA,SAAQ,SAAS,QAAQ;AAEjD,UAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG,KAAK,CAAC,SAAS,cAAc;AAC9E,eAAO;AAAA,MACT;AAEA,UAAI,SAAS,aAAa,OAAO,QAAQ,YAAY,WAAW,GAAG,GAAG;AACpE,cAAM,WAAW,oBAAoB,GAAG;AAExC,YAAI,SAAS,WAAW,GAAG;AACzB,iBAAO;AAAA,QACT;AAEA,cAAM,aAAa,eAAe,MAAM,SAAS,MAAM,GAAG,EAAE,CAAC;AAI7D,YAAI,CAAC,cAAe,CAAC,SAAS,UAAU,KAAK,CAAC,MAAM,QAAQ,UAAU,GAAI;AACxE,iBAAO;AAAA,QACT;AACA,mBAAW,SAAS,GAAG,EAAE,CAAC,IAAI;AAC9B,eAAO;AAAA,MACT;AAEA,aAAO,QAAQ,IAAIA,SAAQ,MAAM,UAAU,QAAQ;AAAA,IACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,eAAeA,SAAQ,MAAM;AAC3B,aAAO,QAAQ,eAAeA,SAAQ,IAAI;AAAA,IAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,IAAIA,SAAQ,MAAM;AAEhB,UAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG,KAAK,CAAC,SAAS,cAAc;AAC9E,eAAO;AAAA,MACT;AAGA,UAAI,SAAS,aAAa,WAAWA,SAAQ;AAC3C,eAAO;AAAA,MACT;AACA,aAAO,QAAQ,IAAIA,SAAQ,IAAI;AAAA,IACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,QAAQA,SAAQ;AACd,YAAM,OAAO,QAAQ,QAAQA,OAAM;AAGnC,YAAM,eAAe,KAAK;AAAA,QACxB,CAAC,QAAQ,OAAO,QAAQ,YAAY,EAAE,IAAI,WAAW,GAAG,KAAK,CAAC,SAAS;AAAA,MACzE;AAEA,UAAI,WAAWA,WAAU,CAAC,aAAa,SAAS,SAAS,GAAG;AAC1D,qBAAa,KAAK,SAAS;AAAA,MAC7B;AACA,aAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,yBAAyBA,SAAQ,MAAM;AAErC,UAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG,KAAK,CAAC,SAAS,cAAc;AAC9E,eAAO;AAAA,MACT;AAEA,YAAM,MAAM,QAAQ,IAAIA,SAAQ,OAAO;AAEvC,UAAI,SAAS,aAAa,OAAO,QAAQ,UAAU;AACjD,eAAO;AAAA,UACL,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AAGA,aAAO,QAAQ,yBAAyBA,SAAQ,IAAI;AAAA,IACtD;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,MAAS,QAAQ,OAAO;AAC5C,aAAW,IAAI,QAAQ,OAAO;AAC9B,SAAO;AACT;AAYO,SAAS,OAAU,KAAW;AACnC,MAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,WAAO;AAAA,EACT;AAEA,MAAK,IAAoD,YAAY,GAAG;AACtE,WAAQ,IAAsC,gBAAgB;AAAA,EAChE;AAEA,SAAO;AACT;",
|
|
6
6
|
"names": ["target"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-object.d.ts","sourceRoot":"","sources":["../../src/utils/is-object.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"is-object.d.ts","sourceRoot":"","sources":["../../src/utils/is-object.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,GAAG,KAAG,GAAG,IAAI,MAAwE,CAAA"}
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"url": "git+https://github.com/scalar/scalar.git",
|
|
11
11
|
"directory": "packages/json-magic"
|
|
12
12
|
},
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.4.1",
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=20"
|
|
16
16
|
},
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"vue": "^3.5.17",
|
|
52
52
|
"yaml": "2.8.0",
|
|
53
|
-
"@scalar/helpers": "0.0.
|
|
53
|
+
"@scalar/helpers": "0.0.10"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"fastify": "^5.3.3",
|
|
57
57
|
"vite": "6.1.6",
|
|
58
|
-
"@scalar/build-tooling": "0.2.
|
|
58
|
+
"@scalar/build-tooling": "0.2.7"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "scalar-build-esbuild",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { createMagicProxy, getRaw } from './proxy'
|
|
2
1
|
import { describe, expect, it } from 'vitest'
|
|
3
2
|
|
|
3
|
+
import { createMagicProxy, getRaw } from './proxy'
|
|
4
|
+
|
|
4
5
|
describe('createMagicProxy', () => {
|
|
5
6
|
describe('get', () => {
|
|
6
7
|
it('should correctly proxy internal refs', () => {
|
|
@@ -1137,4 +1138,256 @@ describe('createMagicProxy', () => {
|
|
|
1137
1138
|
expect(getRaw(proxied)).toEqual(input)
|
|
1138
1139
|
})
|
|
1139
1140
|
})
|
|
1141
|
+
|
|
1142
|
+
describe('show underscore properties when specified', () => {
|
|
1143
|
+
it('should not hide properties starting with underscore from direct access', () => {
|
|
1144
|
+
const input = {
|
|
1145
|
+
public: 'visible',
|
|
1146
|
+
_private: 'hidden',
|
|
1147
|
+
__internal: 'also hidden',
|
|
1148
|
+
normal_underscore: 'visible with underscore in middle',
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
const result = createMagicProxy(input, { showInternal: true })
|
|
1152
|
+
|
|
1153
|
+
expect(result.public).toBe('visible')
|
|
1154
|
+
expect(result._private).toBe('hidden')
|
|
1155
|
+
expect(result.__internal).toBe('also hidden')
|
|
1156
|
+
expect(result.normal_underscore).toBe('visible with underscore in middle')
|
|
1157
|
+
})
|
|
1158
|
+
|
|
1159
|
+
it('should not hide underscore properties from "in" operator', () => {
|
|
1160
|
+
const input = {
|
|
1161
|
+
public: 'visible',
|
|
1162
|
+
_private: 'hidden',
|
|
1163
|
+
__internal: 'also hidden',
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
const result = createMagicProxy(input, { showInternal: true })
|
|
1167
|
+
|
|
1168
|
+
expect('public' in result).toBe(true)
|
|
1169
|
+
expect('_private' in result).toBe(true)
|
|
1170
|
+
expect('__internal' in result).toBe(true)
|
|
1171
|
+
})
|
|
1172
|
+
|
|
1173
|
+
it('should not exclude underscore properties from Object.keys enumeration', () => {
|
|
1174
|
+
const input = {
|
|
1175
|
+
public: 'visible',
|
|
1176
|
+
_private: 'hidden',
|
|
1177
|
+
__internal: 'also hidden',
|
|
1178
|
+
another: 'visible',
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const result = createMagicProxy(input, { showInternal: true })
|
|
1182
|
+
const keys = Object.keys(result)
|
|
1183
|
+
|
|
1184
|
+
expect(keys).toContain('public')
|
|
1185
|
+
expect(keys).toContain('another')
|
|
1186
|
+
expect(keys).toContain('_private')
|
|
1187
|
+
expect(keys).toContain('__internal')
|
|
1188
|
+
})
|
|
1189
|
+
|
|
1190
|
+
it('should not hide underscore properties from getOwnPropertyDescriptor', () => {
|
|
1191
|
+
const input = {
|
|
1192
|
+
public: 'visible',
|
|
1193
|
+
_private: 'hidden',
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
const result = createMagicProxy(input, { showInternal: true })
|
|
1197
|
+
|
|
1198
|
+
expect(Object.getOwnPropertyDescriptor(result, 'public')).toBeDefined()
|
|
1199
|
+
expect(Object.getOwnPropertyDescriptor(result, '_private')).toBeDefined()
|
|
1200
|
+
})
|
|
1201
|
+
|
|
1202
|
+
it('should not hide underscore properties in nested objects', () => {
|
|
1203
|
+
const input = {
|
|
1204
|
+
nested: {
|
|
1205
|
+
public: 'visible',
|
|
1206
|
+
_private: 'hidden',
|
|
1207
|
+
deeper: {
|
|
1208
|
+
_alsoHidden: 'secret',
|
|
1209
|
+
visible: 'shown',
|
|
1210
|
+
},
|
|
1211
|
+
},
|
|
1212
|
+
_topLevel: 'hidden',
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
const result = createMagicProxy(input, { showInternal: true })
|
|
1216
|
+
|
|
1217
|
+
expect(result._topLevel).toBe('hidden')
|
|
1218
|
+
expect(result.nested.public).toBe('visible')
|
|
1219
|
+
expect(result.nested._private).toBe('hidden')
|
|
1220
|
+
expect(result.nested.deeper._alsoHidden).toBe('secret')
|
|
1221
|
+
expect(result.nested.deeper.visible).toBe('shown')
|
|
1222
|
+
})
|
|
1223
|
+
|
|
1224
|
+
it('should show underscore properties with arrays containing objects with underscore properties', () => {
|
|
1225
|
+
const input = {
|
|
1226
|
+
items: [
|
|
1227
|
+
{ public: 'item1', _private: 'hidden1' },
|
|
1228
|
+
{ public: 'item2', _private: 'hidden2' },
|
|
1229
|
+
],
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
const result = createMagicProxy(input, { showInternal: true })
|
|
1233
|
+
|
|
1234
|
+
expect(result.items[0].public).toBe('item1')
|
|
1235
|
+
expect(result.items[0]._private).toBe('hidden1')
|
|
1236
|
+
expect(result.items[1].public).toBe('item2')
|
|
1237
|
+
expect(result.items[1]._private).toBe('hidden2')
|
|
1238
|
+
})
|
|
1239
|
+
|
|
1240
|
+
it('should show underscore ref properties', () => {
|
|
1241
|
+
const input = {
|
|
1242
|
+
definitions: {
|
|
1243
|
+
example: {
|
|
1244
|
+
value: 'hello',
|
|
1245
|
+
_internal: 'hidden',
|
|
1246
|
+
},
|
|
1247
|
+
},
|
|
1248
|
+
_hiddenRef: { $ref: '#/definitions/example' },
|
|
1249
|
+
publicRef: { $ref: '#/definitions/example' },
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
const result = createMagicProxy(input, { showInternal: true })
|
|
1253
|
+
|
|
1254
|
+
// Underscore property should be hidden
|
|
1255
|
+
expect(result._hiddenRef).toEqual({
|
|
1256
|
+
'$ref': '#/definitions/example',
|
|
1257
|
+
'$ref-value': {
|
|
1258
|
+
'_internal': 'hidden',
|
|
1259
|
+
'value': 'hello',
|
|
1260
|
+
},
|
|
1261
|
+
})
|
|
1262
|
+
|
|
1263
|
+
// Public ref should work normally
|
|
1264
|
+
expect(result.publicRef['$ref-value'].value).toBe('hello')
|
|
1265
|
+
|
|
1266
|
+
// Underscore properties in referenced objects should be hidden
|
|
1267
|
+
expect(result.publicRef['$ref-value']._internal).toBe('hidden')
|
|
1268
|
+
})
|
|
1269
|
+
})
|
|
1270
|
+
|
|
1271
|
+
describe('hide underscore properties', () => {
|
|
1272
|
+
it('should hide properties starting with underscore from direct access', () => {
|
|
1273
|
+
const input = {
|
|
1274
|
+
public: 'visible',
|
|
1275
|
+
_private: 'hidden',
|
|
1276
|
+
__internal: 'also hidden',
|
|
1277
|
+
normal_underscore: 'visible with underscore in middle',
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
const result = createMagicProxy(input)
|
|
1281
|
+
|
|
1282
|
+
expect(result.public).toBe('visible')
|
|
1283
|
+
expect(result._private).toBe(undefined)
|
|
1284
|
+
expect(result.__internal).toBe(undefined)
|
|
1285
|
+
expect(result.normal_underscore).toBe('visible with underscore in middle')
|
|
1286
|
+
})
|
|
1287
|
+
|
|
1288
|
+
it('should hide underscore properties from "in" operator', () => {
|
|
1289
|
+
const input = {
|
|
1290
|
+
public: 'visible',
|
|
1291
|
+
_private: 'hidden',
|
|
1292
|
+
__internal: 'also hidden',
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
const result = createMagicProxy(input)
|
|
1296
|
+
|
|
1297
|
+
expect('public' in result).toBe(true)
|
|
1298
|
+
expect('_private' in result).toBe(false)
|
|
1299
|
+
expect('__internal' in result).toBe(false)
|
|
1300
|
+
})
|
|
1301
|
+
|
|
1302
|
+
it('should exclude underscore properties from Object.keys enumeration', () => {
|
|
1303
|
+
const input = {
|
|
1304
|
+
public: 'visible',
|
|
1305
|
+
_private: 'hidden',
|
|
1306
|
+
__internal: 'also hidden',
|
|
1307
|
+
another: 'visible',
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
const result = createMagicProxy(input)
|
|
1311
|
+
const keys = Object.keys(result)
|
|
1312
|
+
|
|
1313
|
+
expect(keys).toContain('public')
|
|
1314
|
+
expect(keys).toContain('another')
|
|
1315
|
+
expect(keys).not.toContain('_private')
|
|
1316
|
+
expect(keys).not.toContain('__internal')
|
|
1317
|
+
})
|
|
1318
|
+
|
|
1319
|
+
it('should hide underscore properties from getOwnPropertyDescriptor', () => {
|
|
1320
|
+
const input = {
|
|
1321
|
+
public: 'visible',
|
|
1322
|
+
_private: 'hidden',
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
const result = createMagicProxy(input)
|
|
1326
|
+
|
|
1327
|
+
expect(Object.getOwnPropertyDescriptor(result, 'public')).toBeDefined()
|
|
1328
|
+
expect(Object.getOwnPropertyDescriptor(result, '_private')).toBe(undefined)
|
|
1329
|
+
})
|
|
1330
|
+
|
|
1331
|
+
it('should hide underscore properties in nested objects', () => {
|
|
1332
|
+
const input = {
|
|
1333
|
+
nested: {
|
|
1334
|
+
public: 'visible',
|
|
1335
|
+
_private: 'hidden',
|
|
1336
|
+
deeper: {
|
|
1337
|
+
_alsoHidden: 'secret',
|
|
1338
|
+
visible: 'shown',
|
|
1339
|
+
},
|
|
1340
|
+
},
|
|
1341
|
+
_topLevel: 'hidden',
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
const result = createMagicProxy(input)
|
|
1345
|
+
|
|
1346
|
+
expect(result._topLevel).toBe(undefined)
|
|
1347
|
+
expect(result.nested.public).toBe('visible')
|
|
1348
|
+
expect(result.nested._private).toBe(undefined)
|
|
1349
|
+
expect(result.nested.deeper._alsoHidden).toBe(undefined)
|
|
1350
|
+
expect(result.nested.deeper.visible).toBe('shown')
|
|
1351
|
+
})
|
|
1352
|
+
|
|
1353
|
+
it('should work with arrays containing objects with underscore properties', () => {
|
|
1354
|
+
const input = {
|
|
1355
|
+
items: [
|
|
1356
|
+
{ public: 'item1', _private: 'hidden1' },
|
|
1357
|
+
{ public: 'item2', _private: 'hidden2' },
|
|
1358
|
+
],
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
const result = createMagicProxy(input)
|
|
1362
|
+
|
|
1363
|
+
expect(result.items[0].public).toBe('item1')
|
|
1364
|
+
expect(result.items[0]._private).toBe(undefined)
|
|
1365
|
+
expect(result.items[1].public).toBe('item2')
|
|
1366
|
+
expect(result.items[1]._private).toBe(undefined)
|
|
1367
|
+
})
|
|
1368
|
+
|
|
1369
|
+
it('should still allow refs to work with underscore hiding', () => {
|
|
1370
|
+
const input = {
|
|
1371
|
+
definitions: {
|
|
1372
|
+
example: {
|
|
1373
|
+
value: 'hello',
|
|
1374
|
+
_internal: 'hidden',
|
|
1375
|
+
},
|
|
1376
|
+
},
|
|
1377
|
+
_hiddenRef: { $ref: '#/definitions/example' },
|
|
1378
|
+
publicRef: { $ref: '#/definitions/example' },
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
const result = createMagicProxy(input)
|
|
1382
|
+
|
|
1383
|
+
// Underscore property should be hidden
|
|
1384
|
+
expect(result._hiddenRef).toBe(undefined)
|
|
1385
|
+
|
|
1386
|
+
// Public ref should work normally
|
|
1387
|
+
expect(result.publicRef['$ref-value'].value).toBe('hello')
|
|
1388
|
+
|
|
1389
|
+
// Underscore properties in referenced objects should be hidden
|
|
1390
|
+
expect(result.publicRef['$ref-value']._internal).toBe(undefined)
|
|
1391
|
+
})
|
|
1392
|
+
})
|
|
1140
1393
|
})
|
package/src/magic-proxy/proxy.ts
CHANGED
|
@@ -18,6 +18,8 @@ const REF_KEY = '$ref'
|
|
|
18
18
|
* will resolve and return the referenced value from the root object.
|
|
19
19
|
* - All nested objects and arrays are recursively wrapped in proxies, so reference resolution
|
|
20
20
|
* works at any depth.
|
|
21
|
+
* - Properties starting with an underscore (_) are hidden and will not be accessible through
|
|
22
|
+
* the proxy (returns undefined on access, false on 'in' checks, excluded from enumeration).
|
|
21
23
|
* - Setting, deleting, and enumerating properties works as expected, including for proxied references.
|
|
22
24
|
*
|
|
23
25
|
* @param target - The object or array to wrap in a magic proxy
|
|
@@ -29,31 +31,45 @@ const REF_KEY = '$ref'
|
|
|
29
31
|
* definitions: {
|
|
30
32
|
* foo: { bar: 123 }
|
|
31
33
|
* },
|
|
32
|
-
* refObj: { $ref: '#/definitions/foo' }
|
|
34
|
+
* refObj: { $ref: '#/definitions/foo' },
|
|
35
|
+
* _internal: 'hidden property'
|
|
33
36
|
* }
|
|
34
37
|
* const proxy = createMagicProxy(input)
|
|
35
38
|
*
|
|
36
39
|
* // Accessing proxy.refObj['$ref-value'] will resolve to { bar: 123 }
|
|
37
40
|
* console.log(proxy.refObj['$ref-value']) // { bar: 123 }
|
|
38
41
|
*
|
|
42
|
+
* // Properties starting with underscore are hidden
|
|
43
|
+
* console.log(proxy._internal) // undefined
|
|
44
|
+
* console.log('_internal' in proxy) // false
|
|
45
|
+
* console.log(Object.keys(proxy)) // ['definitions', 'refObj'] (no '_internal')
|
|
46
|
+
*
|
|
39
47
|
* // Setting and deleting properties works as expected
|
|
40
48
|
* proxy.refObj.extra = 'hello'
|
|
41
49
|
* delete proxy.refObj.extra
|
|
42
50
|
*/
|
|
43
51
|
export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S extends UnknownObject>(
|
|
44
52
|
target: T,
|
|
53
|
+
options?: { showInternal?: boolean },
|
|
45
54
|
root: S | T = target,
|
|
46
55
|
cache = new Map<string, unknown>(),
|
|
56
|
+
proxyCache = new WeakMap<object, T>(),
|
|
47
57
|
) => {
|
|
48
58
|
if (!isObject(target) && !Array.isArray(target)) {
|
|
49
59
|
return target
|
|
50
60
|
}
|
|
51
61
|
|
|
62
|
+
// Return existing proxy for the same target to ensure referential stability
|
|
63
|
+
if (proxyCache.has(target)) {
|
|
64
|
+
return proxyCache.get(target)
|
|
65
|
+
}
|
|
66
|
+
|
|
52
67
|
const handler: ProxyHandler<T> = {
|
|
53
68
|
/**
|
|
54
69
|
* Proxy "get" trap for magic proxy.
|
|
55
70
|
* - If accessing the special isMagicProxy symbol, return true to identify proxy.
|
|
56
71
|
* - If accessing the magicProxyTarget symbol, return the original target object.
|
|
72
|
+
* - Hide properties starting with underscore by returning undefined.
|
|
57
73
|
* - If accessing "$ref-value" and the object has a local $ref, resolve and return the referenced value as a new magic proxy.
|
|
58
74
|
* - For all other properties, recursively wrap the returned value in a magic proxy (if applicable).
|
|
59
75
|
*/
|
|
@@ -70,6 +86,12 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
|
|
|
70
86
|
|
|
71
87
|
const ref = Reflect.get(target, REF_KEY, receiver)
|
|
72
88
|
|
|
89
|
+
// Hide properties starting with underscore - these are considered internal/private properties
|
|
90
|
+
// and should not be accessible through the magic proxy interface
|
|
91
|
+
if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
|
|
92
|
+
return undefined
|
|
93
|
+
}
|
|
94
|
+
|
|
73
95
|
// If accessing "$ref-value" and $ref is a local reference, resolve and return the referenced value
|
|
74
96
|
if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {
|
|
75
97
|
// Check cache first for performance optimization
|
|
@@ -79,7 +101,7 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
|
|
|
79
101
|
|
|
80
102
|
// Resolve the reference and create a new magic proxy
|
|
81
103
|
const resolvedValue = getValueByPath(root, parseJsonPointer(ref))
|
|
82
|
-
const proxiedValue = createMagicProxy(resolvedValue, root, cache)
|
|
104
|
+
const proxiedValue = createMagicProxy(resolvedValue, options, root, cache)
|
|
83
105
|
|
|
84
106
|
// Store in cache for future lookups
|
|
85
107
|
cache.set(ref, proxiedValue)
|
|
@@ -88,16 +110,23 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
|
|
|
88
110
|
|
|
89
111
|
// For all other properties, recursively wrap the value in a magic proxy
|
|
90
112
|
const value = Reflect.get(target, prop, receiver)
|
|
91
|
-
return createMagicProxy(value, root, cache)
|
|
113
|
+
return createMagicProxy(value as T, options, root, cache, proxyCache)
|
|
92
114
|
},
|
|
93
115
|
/**
|
|
94
116
|
* Proxy "set" trap for magic proxy.
|
|
95
117
|
* Allows setting properties on the proxied object.
|
|
96
118
|
* This will update the underlying target object.
|
|
119
|
+
*
|
|
120
|
+
* Note: it will not update if the property starts with an underscore (_)
|
|
121
|
+
* Those will be considered private properties by the proxy
|
|
97
122
|
*/
|
|
98
123
|
set(target, prop, newValue, receiver) {
|
|
99
124
|
const ref = Reflect.get(target, REF_KEY, receiver)
|
|
100
125
|
|
|
126
|
+
if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
|
|
127
|
+
return true
|
|
128
|
+
}
|
|
129
|
+
|
|
101
130
|
if (prop === REF_VALUE && typeof ref === 'string' && isLocalRef(ref)) {
|
|
102
131
|
const segments = getSegmentsFromPath(ref)
|
|
103
132
|
|
|
@@ -131,9 +160,15 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
|
|
|
131
160
|
* - Pretend that "$ref-value" exists if "$ref" exists on the target.
|
|
132
161
|
* This allows expressions like `"$ref-value" in obj` to return true for objects with a $ref,
|
|
133
162
|
* even though "$ref-value" is a virtual property provided by the proxy.
|
|
163
|
+
* - Hide properties starting with underscore by returning false.
|
|
134
164
|
* - For all other properties, defer to the default Reflect.has behavior.
|
|
135
165
|
*/
|
|
136
166
|
has(target, prop) {
|
|
167
|
+
// Hide properties starting with underscore
|
|
168
|
+
if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
|
|
169
|
+
return false
|
|
170
|
+
}
|
|
171
|
+
|
|
137
172
|
// Pretend that "$ref-value" exists if "$ref" exists
|
|
138
173
|
if (prop === REF_VALUE && REF_KEY in target) {
|
|
139
174
|
return true
|
|
@@ -146,22 +181,35 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
|
|
|
146
181
|
* - If the object has a "$ref" property, ensures that "$ref-value" is also included in the keys,
|
|
147
182
|
* even though "$ref-value" is a virtual property provided by the proxy.
|
|
148
183
|
* This allows Object.keys, Reflect.ownKeys, etc. to include "$ref-value" for objects with $ref.
|
|
184
|
+
* - Filters out properties starting with underscore.
|
|
149
185
|
*/
|
|
150
186
|
ownKeys(target) {
|
|
151
187
|
const keys = Reflect.ownKeys(target)
|
|
152
|
-
|
|
153
|
-
|
|
188
|
+
|
|
189
|
+
// Filter out properties starting with underscore
|
|
190
|
+
const filteredKeys = keys.filter(
|
|
191
|
+
(key) => typeof key !== 'string' || !(key.startsWith('_') && !options?.showInternal),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
if (REF_KEY in target && !filteredKeys.includes(REF_VALUE)) {
|
|
195
|
+
filteredKeys.push(REF_VALUE)
|
|
154
196
|
}
|
|
155
|
-
return
|
|
197
|
+
return filteredKeys
|
|
156
198
|
},
|
|
157
199
|
|
|
158
200
|
/**
|
|
159
201
|
* Proxy "getOwnPropertyDescriptor" trap for magic proxy.
|
|
160
202
|
* - For the virtual "$ref-value" property, returns a descriptor that makes it appear as a regular property.
|
|
203
|
+
* - Hide properties starting with underscore by returning undefined.
|
|
161
204
|
* - For all other properties, delegates to the default Reflect.getOwnPropertyDescriptor behavior.
|
|
162
205
|
* - This ensures that Object.getOwnPropertyDescriptor and similar methods work correctly with the virtual property.
|
|
163
206
|
*/
|
|
164
207
|
getOwnPropertyDescriptor(target, prop) {
|
|
208
|
+
// Hide properties starting with underscore
|
|
209
|
+
if (typeof prop === 'string' && prop.startsWith('_') && !options?.showInternal) {
|
|
210
|
+
return undefined
|
|
211
|
+
}
|
|
212
|
+
|
|
165
213
|
const ref = Reflect.get(target, REF_KEY)
|
|
166
214
|
|
|
167
215
|
if (prop === REF_VALUE && typeof ref === 'string') {
|
|
@@ -178,7 +226,9 @@ export const createMagicProxy = <T extends Record<keyof T & symbol, unknown>, S
|
|
|
178
226
|
},
|
|
179
227
|
}
|
|
180
228
|
|
|
181
|
-
|
|
229
|
+
const proxied = new Proxy<T>(target, handler)
|
|
230
|
+
proxyCache.set(target, proxied)
|
|
231
|
+
return proxied
|
|
182
232
|
}
|
|
183
233
|
|
|
184
234
|
/**
|