unreflect 0.0.4 → 0.0.6

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/dist/index.d.mts CHANGED
@@ -16,8 +16,7 @@ declare class ReflectionClass {
16
16
  constructor(obj: any);
17
17
  private isPrivateName;
18
18
  private getBaseName;
19
- private stripPrivateInitializers;
20
- setProperty(name: string, value: any): void;
19
+ setProperty(name: string, value: any): Promise<void>;
21
20
  getProperty(name: string): Promise<ReflectionProperty | undefined>;
22
21
  getPropertyValue(name: string): Promise<any>;
23
22
  getClassName(): string;
package/dist/index.mjs CHANGED
@@ -1,5 +1,4 @@
1
- import vm from "node:vm";
2
- import inspector from "node:inspector/promises";
1
+ import inspector from "node:inspector";
3
2
 
4
3
  //#region src/index.ts
5
4
  var ReflectionClass = class {
@@ -14,94 +13,7 @@ var ReflectionClass = class {
14
13
  getBaseName(name) {
15
14
  return name.startsWith("#") ? name.slice(1) : name;
16
15
  }
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
- let inConstructor = false;
25
- let constructorBraceDepth = 0;
26
- for (const line of lines) {
27
- if (/^\s*constructor\s*\(/.test(line)) {
28
- inConstructor = true;
29
- constructorBraceDepth = 0;
30
- for (const ch of line) {
31
- if (ch === "{") constructorBraceDepth++;
32
- if (ch === "}") constructorBraceDepth--;
33
- }
34
- result.push(line.replace(/\{.*$/, "{}"));
35
- if (constructorBraceDepth > 0) continue;
36
- inConstructor = false;
37
- continue;
38
- }
39
- if (inConstructor) {
40
- for (const ch of line) {
41
- if (ch === "{") constructorBraceDepth++;
42
- if (ch === "}") constructorBraceDepth--;
43
- }
44
- if (constructorBraceDepth === 0) inConstructor = false;
45
- continue;
46
- }
47
- const isMethod = /^\s*#\w+\s*\(/.test(line);
48
- const fieldMatch = line.match(/^(\s*)(#\w+)/);
49
- if (!isMethod && fieldMatch?.[1] !== void 0 && fieldMatch[2] && !skipUntilBalanced) {
50
- result.push(fieldMatch[1] + fieldMatch[2]);
51
- if (line.includes("=")) {
52
- for (const ch of line) switch (ch) {
53
- case "{":
54
- braceDepth++;
55
- break;
56
- case "}":
57
- braceDepth--;
58
- break;
59
- case "[":
60
- bracketDepth++;
61
- break;
62
- case "]":
63
- bracketDepth--;
64
- break;
65
- case "(":
66
- parenDepth++;
67
- break;
68
- case ")":
69
- parenDepth--;
70
- break;
71
- }
72
- if (braceDepth > 0 || bracketDepth > 0 || parenDepth > 0) skipUntilBalanced = true;
73
- }
74
- continue;
75
- }
76
- if (skipUntilBalanced) {
77
- for (const ch of line) switch (ch) {
78
- case "{":
79
- braceDepth++;
80
- break;
81
- case "}":
82
- braceDepth--;
83
- break;
84
- case "[":
85
- bracketDepth++;
86
- break;
87
- case "]":
88
- bracketDepth--;
89
- break;
90
- case "(":
91
- parenDepth++;
92
- break;
93
- case ")":
94
- parenDepth--;
95
- break;
96
- }
97
- if (braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) skipUntilBalanced = false;
98
- continue;
99
- }
100
- result.push(line);
101
- }
102
- return result.join("\n");
103
- }
104
- setProperty(name, value) {
16
+ async setProperty(name, value) {
105
17
  const obj = this.obj;
106
18
  const isPrivate = this.isPrivateName(name);
107
19
  const baseName = this.getBaseName(name);
@@ -109,51 +21,153 @@ var ReflectionClass = class {
109
21
  obj[baseName] = value;
110
22
  return;
111
23
  }
24
+ this.values.set(name, value);
112
25
  const proto = Object.getPrototypeOf(obj);
113
- const classSource = proto.constructor.toString();
114
- if (!classSource.includes(`#${baseName}`)) return;
115
- const modifiedSource = this.stripPrivateInitializers(classSource).replace(/^(class(?:\s+\w+)?)\s*\{/, `$1 { static __reflectionSet__(obj, val) { obj.#${baseName} = val; } static __reflectionGet__(obj) { return obj.#${baseName}; }`);
116
- const ModifiedClass = vm.runInThisContext(`(${modifiedSource})`);
117
- Object.setPrototypeOf(obj, ModifiedClass.prototype);
118
- const originalProps = Object.getOwnPropertyDescriptors(obj);
119
- const freshInstance = new ModifiedClass();
120
- for (const key of Object.getOwnPropertyNames(originalProps)) Object.defineProperty(freshInstance, key, originalProps[key]);
121
- ModifiedClass.__reflectionSet__(freshInstance, value);
122
- this.values.set(name, {
123
- freshInstance,
124
- ModifiedClass
125
- });
126
- Object.setPrototypeOf(obj, proto);
127
26
  const protoDescriptors = Object.getOwnPropertyDescriptors(proto);
27
+ let targetGetterName = null;
128
28
  for (const [propName, descriptor] of Object.entries(protoDescriptors)) {
129
29
  if (propName === "constructor") continue;
130
- if (typeof descriptor.value === "function") {
131
- if (descriptor.value.toString().includes(`#${baseName}`)) {
132
- const boundMethod = ModifiedClass.prototype[propName].bind(freshInstance);
133
- Object.defineProperty(obj, propName, {
134
- value: boundMethod,
135
- writable: true,
136
- configurable: true
137
- });
30
+ if (descriptor.get) {
31
+ if (descriptor.get.toString().includes(`#${baseName}`)) {
32
+ targetGetterName = propName;
33
+ break;
138
34
  }
139
35
  }
140
- if (descriptor.get || descriptor.set) {
141
- const getterSource = descriptor.get?.toString() || "";
142
- const setterSource = descriptor.set?.toString() || "";
143
- if (getterSource.includes(`#${baseName}`) || setterSource.includes(`#${baseName}`)) {
144
- const modifiedDescriptor = Object.getOwnPropertyDescriptor(ModifiedClass.prototype, propName);
145
- if (modifiedDescriptor) {
146
- const newDescriptor = {
147
- configurable: true,
148
- enumerable: descriptor.enumerable
149
- };
150
- if (modifiedDescriptor.get) newDescriptor.get = modifiedDescriptor.get.bind(freshInstance);
151
- if (modifiedDescriptor.set) newDescriptor.set = modifiedDescriptor.set.bind(freshInstance);
152
- Object.defineProperty(obj, propName, newDescriptor);
153
- }
36
+ }
37
+ if (!targetGetterName) for (const [propName, descriptor] of Object.entries(protoDescriptors)) {
38
+ if (propName === "constructor") continue;
39
+ if (typeof descriptor.value === "function") {
40
+ if (descriptor.value.toString().includes(`#${baseName}`)) {
41
+ targetGetterName = propName;
42
+ break;
154
43
  }
155
44
  }
156
45
  }
46
+ if (!targetGetterName) return;
47
+ const objId = `__reflection_obj_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
48
+ const valId = `__reflection_val_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
49
+ const doneId = `__reflection_done_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
50
+ globalThis[objId] = obj;
51
+ globalThis[valId] = value;
52
+ globalThis[doneId] = false;
53
+ const session = new inspector.Session();
54
+ session.connect();
55
+ return new Promise((resolve, reject) => {
56
+ let callFrameId = null;
57
+ let resolved = false;
58
+ const cleanup = () => {
59
+ if (!resolved) {
60
+ resolved = true;
61
+ delete globalThis[objId];
62
+ delete globalThis[valId];
63
+ delete globalThis[doneId];
64
+ session.disconnect();
65
+ }
66
+ };
67
+ session.on("Debugger.paused", (message) => {
68
+ const frames = message.params.callFrames;
69
+ if (frames && frames.length > 0) {
70
+ callFrameId = frames[0].callFrameId;
71
+ session.post("Debugger.evaluateOnCallFrame", {
72
+ callFrameId,
73
+ expression: `this.#${baseName} = globalThis["${valId}"]`
74
+ }, () => {
75
+ session.post("Debugger.resume", () => {
76
+ cleanup();
77
+ resolve();
78
+ });
79
+ });
80
+ }
81
+ });
82
+ session.post("Debugger.enable", (err) => {
83
+ if (err) {
84
+ cleanup();
85
+ reject(err);
86
+ return;
87
+ }
88
+ const descriptor = protoDescriptors[targetGetterName];
89
+ const targetFn = descriptor?.get || descriptor?.value;
90
+ if (!targetFn) {
91
+ cleanup();
92
+ reject(/* @__PURE__ */ new Error("Target function not found"));
93
+ return;
94
+ }
95
+ const lines = targetFn.toString().split("\n");
96
+ for (const [i, line] of lines.entries()) if (line.includes(`#${baseName}`)) break;
97
+ session.post("Runtime.enable", (err$1) => {
98
+ if (err$1) {
99
+ cleanup();
100
+ reject(err$1);
101
+ return;
102
+ }
103
+ session.post("Runtime.evaluate", { expression: `globalThis["${objId}"]` }, (err$2, result) => {
104
+ if (err$2 || !result?.result?.objectId) {
105
+ cleanup();
106
+ reject(err$2 || /* @__PURE__ */ new Error("Failed to get object"));
107
+ return;
108
+ }
109
+ session.post("Runtime.getProperties", {
110
+ objectId: result.result.objectId,
111
+ ownProperties: true
112
+ }, (err$3, propsResult) => {
113
+ if (err$3) {
114
+ cleanup();
115
+ reject(err$3);
116
+ return;
117
+ }
118
+ const protoInternal = (propsResult.internalProperties || []).find((p) => p.name === "[[Prototype]]");
119
+ if (!protoInternal?.value?.objectId) {
120
+ cleanup();
121
+ reject(/* @__PURE__ */ new Error("Prototype not found"));
122
+ return;
123
+ }
124
+ session.post("Runtime.getProperties", {
125
+ objectId: protoInternal.value.objectId,
126
+ ownProperties: true
127
+ }, (err$4, protoPropsResult) => {
128
+ if (err$4) {
129
+ cleanup();
130
+ reject(err$4);
131
+ return;
132
+ }
133
+ const targetProp = protoPropsResult.result?.find((p) => p.name === targetGetterName);
134
+ let fnObjectId = null;
135
+ if (targetProp?.get?.objectId) fnObjectId = targetProp.get.objectId;
136
+ else if (targetProp?.value?.objectId) fnObjectId = targetProp.value.objectId;
137
+ if (!fnObjectId) {
138
+ cleanup();
139
+ reject(/* @__PURE__ */ new Error("Function not found"));
140
+ return;
141
+ }
142
+ session.post("Debugger.setBreakpointOnFunctionCall", { objectId: fnObjectId }, (err$5, bpResult) => {
143
+ if (err$5) {
144
+ cleanup();
145
+ reject(err$5);
146
+ return;
147
+ }
148
+ const breakpointId = bpResult?.breakpointId;
149
+ setTimeout(() => {
150
+ try {
151
+ if (descriptor?.get) obj[targetGetterName];
152
+ else if (typeof descriptor?.value === "function") try {
153
+ descriptor.value.call(obj);
154
+ } catch {}
155
+ } catch {}
156
+ }, 0);
157
+ setTimeout(() => {
158
+ if (!resolved) {
159
+ if (breakpointId) session.post("Debugger.removeBreakpoint", { breakpointId }, () => {});
160
+ cleanup();
161
+ reject(/* @__PURE__ */ new Error("Timeout waiting for breakpoint"));
162
+ }
163
+ }, 5e3);
164
+ });
165
+ });
166
+ });
167
+ });
168
+ });
169
+ });
170
+ });
157
171
  }
158
172
  async getProperty(name) {
159
173
  const obj = this.obj;
@@ -167,43 +181,74 @@ var ReflectionClass = class {
167
181
  };
168
182
  return;
169
183
  }
170
- const cached = this.values.get(name);
171
- if (cached) return {
184
+ if (this.values.has(name)) return {
172
185
  name,
173
186
  visibility: "private",
174
- value: cached.ModifiedClass.__reflectionGet__(cached.freshInstance)
187
+ value: this.values.get(name)
175
188
  };
176
189
  const id = `__ref_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
177
190
  globalThis[id] = obj;
178
191
  const session = new inspector.Session();
179
192
  session.connect();
180
- try {
181
- await session.post("Runtime.enable");
182
- const { result } = await session.post("Runtime.evaluate", { expression: `globalThis.${id}` });
183
- const privateProp = (await session.post("Runtime.getProperties", {
184
- objectId: result.objectId,
185
- ownProperties: true
186
- })).privateProperties?.find((p) => p.name === `#${baseName}`);
187
- if (!privateProp) return;
188
- let value;
189
- if (privateProp.value.type === "object" && privateProp.value.objectId) {
190
- const extractId = `__extract_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
191
- await session.post("Runtime.callFunctionOn", {
192
- objectId: privateProp.value.objectId,
193
- functionDeclaration: `function() { globalThis.${extractId} = this; }`
193
+ return new Promise((resolve, reject) => {
194
+ session.post("Runtime.enable", (err) => {
195
+ if (err) {
196
+ delete globalThis[id];
197
+ session.disconnect();
198
+ reject(err);
199
+ return;
200
+ }
201
+ session.post("Runtime.evaluate", { expression: `globalThis.${id}` }, (err$1, evalResult) => {
202
+ if (err$1 || !evalResult?.result?.objectId) {
203
+ delete globalThis[id];
204
+ session.disconnect();
205
+ if (err$1) reject(err$1);
206
+ else resolve(void 0);
207
+ return;
208
+ }
209
+ session.post("Runtime.getProperties", {
210
+ objectId: evalResult.result.objectId,
211
+ ownProperties: true
212
+ }, (err$2, propsResult) => {
213
+ if (err$2) {
214
+ delete globalThis[id];
215
+ session.disconnect();
216
+ reject(err$2);
217
+ return;
218
+ }
219
+ const privateProp = propsResult.privateProperties?.find((p) => p.name === `#${baseName}`);
220
+ if (!privateProp) {
221
+ delete globalThis[id];
222
+ session.disconnect();
223
+ resolve(void 0);
224
+ return;
225
+ }
226
+ const extractValue = (prop, callback) => {
227
+ if ((prop.value.type === "object" || prop.value.type === "function") && prop.value.objectId) {
228
+ const extractId = `__extract_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
229
+ session.post("Runtime.callFunctionOn", {
230
+ objectId: prop.value.objectId,
231
+ functionDeclaration: `function() { globalThis["${extractId}"] = this; }`
232
+ }, () => {
233
+ const value = globalThis[extractId];
234
+ delete globalThis[extractId];
235
+ callback(value);
236
+ });
237
+ } else callback(prop.value.value);
238
+ };
239
+ extractValue(privateProp, (value) => {
240
+ delete globalThis[id];
241
+ session.disconnect();
242
+ resolve({
243
+ name,
244
+ visibility: "private",
245
+ value
246
+ });
247
+ });
248
+ });
194
249
  });
195
- value = globalThis[extractId];
196
- delete globalThis[extractId];
197
- } else value = privateProp.value.value;
198
- return {
199
- name,
200
- visibility: "private",
201
- value
202
- };
203
- } finally {
204
- delete globalThis[id];
205
- session.disconnect();
206
- }
250
+ });
251
+ });
207
252
  }
208
253
  async getPropertyValue(name) {
209
254
  return (await this.getProperty(name))?.value;
@@ -227,34 +272,56 @@ var ReflectionClass = class {
227
272
  globalThis[id] = obj;
228
273
  const session = new inspector.Session();
229
274
  session.connect();
230
- try {
231
- await session.post("Runtime.enable");
232
- const { result } = await session.post("Runtime.evaluate", { expression: `globalThis.${id}` });
233
- const props = await session.post("Runtime.getProperties", {
234
- objectId: result.objectId,
235
- ownProperties: true
236
- });
237
- if (props.privateProperties) for (const prop of props.privateProperties) properties.push({
238
- name: prop.name,
239
- visibility: "private"
275
+ return new Promise((resolve, reject) => {
276
+ session.post("Runtime.enable", (err) => {
277
+ if (err) {
278
+ delete globalThis[id];
279
+ session.disconnect();
280
+ reject(err);
281
+ return;
282
+ }
283
+ session.post("Runtime.evaluate", { expression: `globalThis.${id}` }, (err$1, evalResult) => {
284
+ if (err$1 || !evalResult?.result?.objectId) {
285
+ delete globalThis[id];
286
+ session.disconnect();
287
+ if (err$1) reject(err$1);
288
+ else resolve(properties);
289
+ return;
290
+ }
291
+ session.post("Runtime.getProperties", {
292
+ objectId: evalResult.result.objectId,
293
+ ownProperties: true
294
+ }, (err$2, propsResult) => {
295
+ delete globalThis[id];
296
+ session.disconnect();
297
+ if (err$2) {
298
+ reject(err$2);
299
+ return;
300
+ }
301
+ if (propsResult.privateProperties) for (const prop of propsResult.privateProperties) properties.push({
302
+ name: prop.name,
303
+ visibility: "private"
304
+ });
305
+ resolve(properties);
306
+ });
307
+ });
240
308
  });
241
- } finally {
242
- delete globalThis[id];
243
- session.disconnect();
244
- }
245
- return properties;
309
+ });
246
310
  }
247
311
  async getMethods() {
248
312
  const obj = this.obj;
249
313
  const proto = Object.getPrototypeOf(obj);
250
314
  const constructor = proto.constructor;
251
315
  const methods = [];
252
- const protoMethods = Object.getOwnPropertyNames(proto).filter((n) => n !== "constructor" && typeof proto[n] === "function");
253
- for (const name of protoMethods) methods.push({
254
- name,
255
- visibility: "public",
256
- isStatic: false
257
- });
316
+ const protoDescriptors = Object.getOwnPropertyDescriptors(proto);
317
+ for (const [name, descriptor] of Object.entries(protoDescriptors)) {
318
+ if (name === "constructor") continue;
319
+ if (typeof descriptor.value === "function") methods.push({
320
+ name,
321
+ visibility: "public",
322
+ isStatic: false
323
+ });
324
+ }
258
325
  const staticMethods = Object.getOwnPropertyNames(constructor).filter((n) => typeof constructor[n] === "function" && ![
259
326
  "length",
260
327
  "name",
@@ -269,32 +336,63 @@ var ReflectionClass = class {
269
336
  globalThis[id] = obj;
270
337
  const session = new inspector.Session();
271
338
  session.connect();
272
- try {
273
- await session.post("Runtime.enable");
274
- const { result } = await session.post("Runtime.evaluate", { expression: `globalThis.${id}` });
275
- const privateMethods = (await session.post("Runtime.getProperties", {
276
- objectId: result.objectId,
277
- ownProperties: true
278
- })).internalProperties?.find((p) => p.name === "[[PrivateMethods]]");
279
- if (privateMethods?.value?.objectId) {
280
- const methodsProps = await session.post("Runtime.getProperties", {
281
- objectId: privateMethods.value.objectId,
282
- ownProperties: true
283
- });
284
- for (const prop of methodsProps.result || []) {
285
- const match = (prop.value?.description || "").match(/^(#\w+)\s*\(/);
286
- if (match) methods.push({
287
- name: match[1],
288
- visibility: "private",
289
- isStatic: false
290
- });
339
+ return new Promise((resolve, reject) => {
340
+ session.post("Runtime.enable", (err) => {
341
+ if (err) {
342
+ delete globalThis[id];
343
+ session.disconnect();
344
+ reject(err);
345
+ return;
291
346
  }
292
- }
293
- } finally {
294
- delete globalThis[id];
295
- session.disconnect();
296
- }
297
- return methods;
347
+ session.post("Runtime.evaluate", { expression: `globalThis.${id}` }, (err$1, evalResult) => {
348
+ if (err$1 || !evalResult?.result?.objectId) {
349
+ delete globalThis[id];
350
+ session.disconnect();
351
+ if (err$1) reject(err$1);
352
+ else resolve(methods);
353
+ return;
354
+ }
355
+ session.post("Runtime.getProperties", {
356
+ objectId: evalResult.result.objectId,
357
+ ownProperties: true
358
+ }, (err$2, propsResult) => {
359
+ if (err$2) {
360
+ delete globalThis[id];
361
+ session.disconnect();
362
+ reject(err$2);
363
+ return;
364
+ }
365
+ const privateMethods = propsResult.internalProperties?.find((p) => p.name === "[[PrivateMethods]]");
366
+ if (!privateMethods?.value?.objectId) {
367
+ delete globalThis[id];
368
+ session.disconnect();
369
+ resolve(methods);
370
+ return;
371
+ }
372
+ session.post("Runtime.getProperties", {
373
+ objectId: privateMethods.value.objectId,
374
+ ownProperties: true
375
+ }, (err$3, methodsPropsResult) => {
376
+ delete globalThis[id];
377
+ session.disconnect();
378
+ if (err$3) {
379
+ reject(err$3);
380
+ return;
381
+ }
382
+ for (const prop of methodsPropsResult.result || []) {
383
+ const match = (prop.value?.description || "").match(/^(#\w+)\s*\(/);
384
+ if (match) methods.push({
385
+ name: match[1],
386
+ visibility: "private",
387
+ isStatic: false
388
+ });
389
+ }
390
+ resolve(methods);
391
+ });
392
+ });
393
+ });
394
+ });
395
+ });
298
396
  }
299
397
  async hasProperty(name) {
300
398
  return await this.getProperty(name) !== void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unreflect",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Access and modify private class fields and methods in JavaScript",
5
5
  "repository": "KABBOUCHI/unreflect",
6
6
  "license": "MIT",