unreflect 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@ import { ReflectionClass } from "unreflect";
22
22
 
23
23
  class User {
24
24
  #name = "John";
25
- public email = "john@example.com";
25
+ email = "john@example.com";
26
26
 
27
27
  getName() {
28
28
  return this.#name;
@@ -35,22 +35,27 @@ const ref = new ReflectionClass(user);
35
35
  // Get class name
36
36
  ref.getClassName(); // "User"
37
37
 
38
- // Get/set property values (public and private)
39
- await ref.getPropertyValue("name"); // "John"
38
+ // Get/set public properties
40
39
  await ref.getPropertyValue("email"); // "john@example.com"
40
+ ref.setProperty("email", "jane@example.com");
41
41
 
42
- ref.setProperty("name", "Jane");
42
+ // Get/set private properties (use # prefix)
43
+ await ref.getPropertyValue("#name"); // "John"
44
+ ref.setProperty("#name", "Jane");
43
45
  user.getName(); // "Jane"
44
46
 
45
47
  // Get property metadata
46
- await ref.getProperty("name");
47
- // { name: "name", visibility: "private", value: "Jane" }
48
+ await ref.getProperty("#name");
49
+ // { name: "#name", visibility: "private", value: "Jane" }
50
+
51
+ await ref.getProperty("email");
52
+ // { name: "email", visibility: "public", value: "jane@example.com" }
48
53
 
49
54
  // List all properties
50
55
  await ref.getProperties();
51
56
  // [
52
- // { name: "email", visibility: "public", value: "john@example.com" },
53
- // { name: "name", visibility: "private" }
57
+ // { name: "email", visibility: "public", value: "jane@example.com" },
58
+ // { name: "#name", visibility: "private" }
54
59
  // ]
55
60
 
56
61
  // List all methods (including private)
@@ -60,7 +65,8 @@ await ref.getMethods();
60
65
  // ]
61
66
 
62
67
  // Check existence
63
- await ref.hasProperty("name"); // true
68
+ await ref.hasProperty("#name"); // true
69
+ await ref.hasProperty("email"); // true
64
70
  await ref.hasMethod("getName"); // true
65
71
  ```
66
72
 
@@ -69,7 +75,7 @@ await ref.hasMethod("getName"); // true
69
75
  | Method | Description |
70
76
  | -------------------------- | --------------------------------------------------- |
71
77
  | `getClassName()` | Returns the class name |
72
- | `setProperty(name, value)` | Sets a property value (public or private) |
78
+ | `setProperty(name, value)` | Sets a property value (use `#` prefix for private) |
73
79
  | `getProperty(name)` | Returns property metadata with visibility and value |
74
80
  | `getPropertyValue(name)` | Returns just the property value |
75
81
  | `getProperties()` | Returns all properties with visibility |
package/dist/index.d.mts CHANGED
@@ -14,6 +14,9 @@ declare class ReflectionClass {
14
14
  private obj;
15
15
  private values;
16
16
  constructor(obj: any);
17
+ private isPrivateName;
18
+ private getBaseName;
19
+ private stripPrivateInitializers;
17
20
  setProperty(name: string, value: any): void;
18
21
  getProperty(name: string): Promise<ReflectionProperty | undefined>;
19
22
  getPropertyValue(name: string): Promise<any>;
package/dist/index.mjs CHANGED
@@ -8,19 +8,89 @@ var ReflectionClass = class {
8
8
  constructor(obj) {
9
9
  this.obj = obj;
10
10
  }
11
+ isPrivateName(name) {
12
+ return name.startsWith("#");
13
+ }
14
+ getBaseName(name) {
15
+ return name.startsWith("#") ? name.slice(1) : name;
16
+ }
17
+ stripPrivateInitializers(source) {
18
+ const lines = source.split("\n");
19
+ const result = [];
20
+ let skipUntilBalanced = false;
21
+ let braceDepth = 0;
22
+ let bracketDepth = 0;
23
+ let parenDepth = 0;
24
+ for (const line of lines) {
25
+ const isMethod = /^\s*#\w+\s*\(/.test(line);
26
+ const fieldMatch = line.match(/^(\s*)(#\w+)/);
27
+ if (!isMethod && fieldMatch?.[1] !== void 0 && fieldMatch[2] && !skipUntilBalanced) {
28
+ result.push(fieldMatch[1] + fieldMatch[2]);
29
+ if (line.includes("=")) {
30
+ for (const ch of line) switch (ch) {
31
+ case "{":
32
+ braceDepth++;
33
+ break;
34
+ case "}":
35
+ braceDepth--;
36
+ break;
37
+ case "[":
38
+ bracketDepth++;
39
+ break;
40
+ case "]":
41
+ bracketDepth--;
42
+ break;
43
+ case "(":
44
+ parenDepth++;
45
+ break;
46
+ case ")":
47
+ parenDepth--;
48
+ break;
49
+ }
50
+ if (braceDepth > 0 || bracketDepth > 0 || parenDepth > 0) skipUntilBalanced = true;
51
+ }
52
+ continue;
53
+ }
54
+ if (skipUntilBalanced) {
55
+ for (const ch of line) switch (ch) {
56
+ case "{":
57
+ braceDepth++;
58
+ break;
59
+ case "}":
60
+ braceDepth--;
61
+ break;
62
+ case "[":
63
+ bracketDepth++;
64
+ break;
65
+ case "]":
66
+ bracketDepth--;
67
+ break;
68
+ case "(":
69
+ parenDepth++;
70
+ break;
71
+ case ")":
72
+ parenDepth--;
73
+ break;
74
+ }
75
+ if (braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) skipUntilBalanced = false;
76
+ continue;
77
+ }
78
+ result.push(line);
79
+ }
80
+ return result.join("\n");
81
+ }
11
82
  setProperty(name, value) {
12
83
  const obj = this.obj;
13
- if (Object.prototype.hasOwnProperty.call(obj, name)) {
14
- obj[name] = value;
84
+ const isPrivate = this.isPrivateName(name);
85
+ const baseName = this.getBaseName(name);
86
+ if (!isPrivate) {
87
+ obj[baseName] = value;
15
88
  return;
16
89
  }
17
90
  const proto = Object.getPrototypeOf(obj);
18
91
  const classSource = proto.constructor.toString();
19
- if (!classSource.includes(`#${name}`)) {
20
- obj[name] = value;
21
- return;
22
- }
23
- const modifiedSource = classSource.replace(/#(\w+)\s*=\s*[^;]+;/g, "#$1;").replace(/^(class\s+\w+)\s*\{/, `$1 { static __reflectionSet__(obj, val) { obj.#${name} = val; } static __reflectionGet__(obj) { return obj.#${name}; }`);
92
+ if (!classSource.includes(`#${baseName}`)) return;
93
+ const modifiedSource = this.stripPrivateInitializers(classSource).replace(/^(class\s+\w+)\s*\{/, `$1 { static __reflectionSet__(obj, val) { obj.#${baseName} = val; } static __reflectionGet__(obj) { return obj.#${baseName}; }`);
24
94
  const ModifiedClass = vm.runInThisContext(`(${modifiedSource})`);
25
95
  Object.setPrototypeOf(obj, ModifiedClass.prototype);
26
96
  const originalProps = Object.getOwnPropertyDescriptors(obj);
@@ -33,7 +103,7 @@ var ReflectionClass = class {
33
103
  });
34
104
  Object.setPrototypeOf(obj, proto);
35
105
  const methodNames = Object.getOwnPropertyNames(proto).filter((n) => n !== "constructor" && typeof proto[n] === "function");
36
- for (const methodName of methodNames) if (proto[methodName].toString().includes(`#${name}`)) {
106
+ for (const methodName of methodNames) if (proto[methodName].toString().includes(`#${baseName}`)) {
37
107
  const boundMethod = ModifiedClass.prototype[methodName].bind(freshInstance);
38
108
  Object.defineProperty(obj, methodName, {
39
109
  value: boundMethod,
@@ -44,10 +114,21 @@ var ReflectionClass = class {
44
114
  }
45
115
  async getProperty(name) {
46
116
  const obj = this.obj;
47
- if (Object.prototype.hasOwnProperty.call(obj, name)) return {
117
+ const isPrivate = this.isPrivateName(name);
118
+ const baseName = this.getBaseName(name);
119
+ if (!isPrivate) {
120
+ if (Object.prototype.hasOwnProperty.call(obj, baseName)) return {
121
+ name: baseName,
122
+ visibility: "public",
123
+ value: obj[baseName]
124
+ };
125
+ return;
126
+ }
127
+ const cached = this.values.get(name);
128
+ if (cached) return {
48
129
  name,
49
- visibility: "public",
50
- value: obj[name]
130
+ visibility: "private",
131
+ value: cached.ModifiedClass.__reflectionGet__(cached.freshInstance)
51
132
  };
52
133
  const id = `__ref_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
53
134
  globalThis[id] = obj;
@@ -59,7 +140,7 @@ var ReflectionClass = class {
59
140
  const privateProp = (await session.post("Runtime.getProperties", {
60
141
  objectId: result.objectId,
61
142
  ownProperties: true
62
- })).privateProperties?.find((p) => p.name === `#${name}`);
143
+ })).privateProperties?.find((p) => p.name === `#${baseName}`);
63
144
  if (!privateProp) return;
64
145
  let value;
65
146
  if (privateProp.value.type === "object" && privateProp.value.objectId) {
@@ -82,8 +163,6 @@ var ReflectionClass = class {
82
163
  }
83
164
  }
84
165
  async getPropertyValue(name) {
85
- const cached = this.values.get(name);
86
- if (cached) return cached.ModifiedClass.__reflectionGet__(cached.freshInstance);
87
166
  return (await this.getProperty(name))?.value;
88
167
  }
89
168
  getClassName() {
@@ -112,13 +191,10 @@ var ReflectionClass = class {
112
191
  objectId: result.objectId,
113
192
  ownProperties: true
114
193
  });
115
- if (props.privateProperties) for (const prop of props.privateProperties) {
116
- const name = prop.name.replace(/^#/, "");
117
- properties.push({
118
- name,
119
- visibility: "private"
120
- });
121
- }
194
+ if (props.privateProperties) for (const prop of props.privateProperties) properties.push({
195
+ name: prop.name,
196
+ visibility: "private"
197
+ });
122
198
  } finally {
123
199
  delete globalThis[id];
124
200
  session.disconnect();
@@ -163,7 +239,7 @@ var ReflectionClass = class {
163
239
  ownProperties: true
164
240
  });
165
241
  for (const prop of methodsProps.result || []) {
166
- const match = (prop.value?.description || "").match(/^#(\w+)\s*\(/);
242
+ const match = (prop.value?.description || "").match(/^(#\w+)\s*\(/);
167
243
  if (match) methods.push({
168
244
  name: match[1],
169
245
  visibility: "private",
@@ -178,7 +254,7 @@ var ReflectionClass = class {
178
254
  return methods;
179
255
  }
180
256
  async hasProperty(name) {
181
- return (await this.getProperties()).some((p) => p.name === name);
257
+ return await this.getProperty(name) !== void 0;
182
258
  }
183
259
  async hasMethod(name) {
184
260
  return (await this.getMethods()).some((m) => m.name === name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unreflect",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Access and modify private class fields and methods in JavaScript",
5
5
  "repository": "KABBOUCHI/unreflect",
6
6
  "license": "MIT",