unreflect 0.0.5 → 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,9 +16,7 @@ declare class ReflectionClass {
16
16
  constructor(obj: any);
17
17
  private isPrivateName;
18
18
  private getBaseName;
19
- private stripPrivateInitializers;
20
- private createShadowMethod;
21
- setProperty(name: string, value: any): void;
19
+ setProperty(name: string, value: any): Promise<void>;
22
20
  getProperty(name: string): Promise<ReflectionProperty | undefined>;
23
21
  getPropertyValue(name: string): Promise<any>;
24
22
  getClassName(): string;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import inspector from "node:inspector/promises";
1
+ import inspector from "node:inspector";
2
2
 
3
3
  //#region src/index.ts
4
4
  var ReflectionClass = class {
@@ -13,105 +13,7 @@ var ReflectionClass = class {
13
13
  getBaseName(name) {
14
14
  return name.startsWith("#") ? name.slice(1) : name;
15
15
  }
16
- stripPrivateInitializers(source) {
17
- const lines = source.split("\n");
18
- const result = [];
19
- let skipUntilBalanced = false;
20
- let braceDepth = 0;
21
- let bracketDepth = 0;
22
- let parenDepth = 0;
23
- let inConstructor = false;
24
- let constructorBraceDepth = 0;
25
- for (const line of lines) {
26
- if (/^\s*constructor\s*\(/.test(line)) {
27
- inConstructor = true;
28
- constructorBraceDepth = 0;
29
- for (const ch of line) {
30
- if (ch === "{") constructorBraceDepth++;
31
- if (ch === "}") constructorBraceDepth--;
32
- }
33
- result.push(line.replace(/\{.*$/, "{}"));
34
- if (constructorBraceDepth > 0) continue;
35
- inConstructor = false;
36
- continue;
37
- }
38
- if (inConstructor) {
39
- for (const ch of line) {
40
- if (ch === "{") constructorBraceDepth++;
41
- if (ch === "}") constructorBraceDepth--;
42
- }
43
- if (constructorBraceDepth === 0) inConstructor = false;
44
- continue;
45
- }
46
- const isMethod = /^\s*#\w+\s*\(/.test(line);
47
- const fieldMatch = line.match(/^(\s*)(#\w+)/);
48
- if (!isMethod && fieldMatch?.[1] !== void 0 && fieldMatch[2] && !skipUntilBalanced) {
49
- result.push(fieldMatch[1] + fieldMatch[2]);
50
- if (line.includes("=")) {
51
- for (const ch of line) switch (ch) {
52
- case "{":
53
- braceDepth++;
54
- break;
55
- case "}":
56
- braceDepth--;
57
- break;
58
- case "[":
59
- bracketDepth++;
60
- break;
61
- case "]":
62
- bracketDepth--;
63
- break;
64
- case "(":
65
- parenDepth++;
66
- break;
67
- case ")":
68
- parenDepth--;
69
- break;
70
- }
71
- if (braceDepth > 0 || bracketDepth > 0 || parenDepth > 0) skipUntilBalanced = true;
72
- }
73
- continue;
74
- }
75
- if (skipUntilBalanced) {
76
- for (const ch of line) switch (ch) {
77
- case "{":
78
- braceDepth++;
79
- break;
80
- case "}":
81
- braceDepth--;
82
- break;
83
- case "[":
84
- bracketDepth++;
85
- break;
86
- case "]":
87
- bracketDepth--;
88
- break;
89
- case "(":
90
- parenDepth++;
91
- break;
92
- case ")":
93
- parenDepth--;
94
- break;
95
- }
96
- if (braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) skipUntilBalanced = false;
97
- continue;
98
- }
99
- result.push(line);
100
- }
101
- return result.join("\n");
102
- }
103
- createShadowMethod(originalMethod, baseName, shadowValues, privateName) {
104
- const methodSource = originalMethod.toString();
105
- if (methodSource.includes(`return this.#${baseName}`) || methodSource.includes(`return this.#${baseName};`)) return function(...args) {
106
- if (shadowValues.has(privateName)) return shadowValues.get(privateName);
107
- return originalMethod.apply(this, args);
108
- };
109
- return function(...args) {
110
- if (shadowValues.has(privateName)) return shadowValues.get(privateName);
111
- return originalMethod.apply(this, args);
112
- };
113
- }
114
- setProperty(name, value) {
16
+ async setProperty(name, value) {
115
17
  const obj = this.obj;
116
18
  const isPrivate = this.isPrivateName(name);
117
19
  const baseName = this.getBaseName(name);
@@ -122,51 +24,150 @@ var ReflectionClass = class {
122
24
  this.values.set(name, value);
123
25
  const proto = Object.getPrototypeOf(obj);
124
26
  const protoDescriptors = Object.getOwnPropertyDescriptors(proto);
27
+ let targetGetterName = null;
125
28
  for (const [propName, descriptor] of Object.entries(protoDescriptors)) {
126
29
  if (propName === "constructor") continue;
127
- if (descriptor.get || descriptor.set) {
128
- const getterSource = descriptor.get?.toString() || "";
129
- const setterSource = descriptor.set?.toString() || "";
130
- if (getterSource.includes(`#${baseName}`) || setterSource.includes(`#${baseName}`)) {
131
- const shadowValues = this.values;
132
- const privateName = name;
133
- const originalGetter = descriptor.get;
134
- const originalSetter = descriptor.set;
135
- const newDescriptor = {
136
- configurable: true,
137
- enumerable: descriptor.enumerable
138
- };
139
- if (originalGetter) newDescriptor.get = function() {
140
- if (shadowValues.has(privateName)) return shadowValues.get(privateName);
141
- return originalGetter.call(this);
142
- };
143
- if (originalSetter) newDescriptor.set = function(val) {
144
- shadowValues.set(privateName, val);
145
- };
146
- Object.defineProperty(obj, propName, newDescriptor);
30
+ if (descriptor.get) {
31
+ if (descriptor.get.toString().includes(`#${baseName}`)) {
32
+ targetGetterName = propName;
33
+ break;
147
34
  }
148
35
  }
36
+ }
37
+ if (!targetGetterName) for (const [propName, descriptor] of Object.entries(protoDescriptors)) {
38
+ if (propName === "constructor") continue;
149
39
  if (typeof descriptor.value === "function") {
150
- const methodSource = descriptor.value.toString();
151
- if (methodSource.includes(`#${baseName}`)) {
152
- const shadowValues = this.values;
153
- const privateName = name;
154
- const originalMethod = descriptor.value;
155
- const isSimpleGetter = methodSource.includes(`return this.#${baseName}`) || methodSource.includes(`return this.#${baseName};`);
156
- let wrappedMethod;
157
- if (isSimpleGetter) wrappedMethod = function(...args) {
158
- if (shadowValues.has(privateName)) return shadowValues.get(privateName);
159
- return originalMethod.apply(this, args);
160
- };
161
- else wrappedMethod = this.createShadowMethod(originalMethod, baseName, shadowValues, privateName);
162
- Object.defineProperty(obj, propName, {
163
- value: wrappedMethod,
164
- writable: true,
165
- configurable: true
166
- });
40
+ if (descriptor.value.toString().includes(`#${baseName}`)) {
41
+ targetGetterName = propName;
42
+ break;
167
43
  }
168
44
  }
169
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
+ });
170
171
  }
171
172
  async getProperty(name) {
172
173
  const obj = this.obj;
@@ -189,33 +190,65 @@ var ReflectionClass = class {
189
190
  globalThis[id] = obj;
190
191
  const session = new inspector.Session();
191
192
  session.connect();
192
- try {
193
- await session.post("Runtime.enable");
194
- const { result } = await session.post("Runtime.evaluate", { expression: `globalThis.${id}` });
195
- const privateProp = (await session.post("Runtime.getProperties", {
196
- objectId: result.objectId,
197
- ownProperties: true
198
- })).privateProperties?.find((p) => p.name === `#${baseName}`);
199
- if (!privateProp) return;
200
- let value;
201
- if (privateProp.value.type === "object" && privateProp.value.objectId) {
202
- const extractId = `__extract_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
203
- await session.post("Runtime.callFunctionOn", {
204
- objectId: privateProp.value.objectId,
205
- 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
+ });
206
249
  });
207
- value = globalThis[extractId];
208
- delete globalThis[extractId];
209
- } else value = privateProp.value.value;
210
- return {
211
- name,
212
- visibility: "private",
213
- value
214
- };
215
- } finally {
216
- delete globalThis[id];
217
- session.disconnect();
218
- }
250
+ });
251
+ });
219
252
  }
220
253
  async getPropertyValue(name) {
221
254
  return (await this.getProperty(name))?.value;
@@ -239,34 +272,56 @@ var ReflectionClass = class {
239
272
  globalThis[id] = obj;
240
273
  const session = new inspector.Session();
241
274
  session.connect();
242
- try {
243
- await session.post("Runtime.enable");
244
- const { result } = await session.post("Runtime.evaluate", { expression: `globalThis.${id}` });
245
- const props = await session.post("Runtime.getProperties", {
246
- objectId: result.objectId,
247
- ownProperties: true
248
- });
249
- if (props.privateProperties) for (const prop of props.privateProperties) properties.push({
250
- name: prop.name,
251
- 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
+ });
252
308
  });
253
- } finally {
254
- delete globalThis[id];
255
- session.disconnect();
256
- }
257
- return properties;
309
+ });
258
310
  }
259
311
  async getMethods() {
260
312
  const obj = this.obj;
261
313
  const proto = Object.getPrototypeOf(obj);
262
314
  const constructor = proto.constructor;
263
315
  const methods = [];
264
- const protoMethods = Object.getOwnPropertyNames(proto).filter((n) => n !== "constructor" && typeof proto[n] === "function");
265
- for (const name of protoMethods) methods.push({
266
- name,
267
- visibility: "public",
268
- isStatic: false
269
- });
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
+ }
270
325
  const staticMethods = Object.getOwnPropertyNames(constructor).filter((n) => typeof constructor[n] === "function" && ![
271
326
  "length",
272
327
  "name",
@@ -281,32 +336,63 @@ var ReflectionClass = class {
281
336
  globalThis[id] = obj;
282
337
  const session = new inspector.Session();
283
338
  session.connect();
284
- try {
285
- await session.post("Runtime.enable");
286
- const { result } = await session.post("Runtime.evaluate", { expression: `globalThis.${id}` });
287
- const privateMethods = (await session.post("Runtime.getProperties", {
288
- objectId: result.objectId,
289
- ownProperties: true
290
- })).internalProperties?.find((p) => p.name === "[[PrivateMethods]]");
291
- if (privateMethods?.value?.objectId) {
292
- const methodsProps = await session.post("Runtime.getProperties", {
293
- objectId: privateMethods.value.objectId,
294
- ownProperties: true
295
- });
296
- for (const prop of methodsProps.result || []) {
297
- const match = (prop.value?.description || "").match(/^(#\w+)\s*\(/);
298
- if (match) methods.push({
299
- name: match[1],
300
- visibility: "private",
301
- isStatic: false
302
- });
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;
303
346
  }
304
- }
305
- } finally {
306
- delete globalThis[id];
307
- session.disconnect();
308
- }
309
- 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
+ });
310
396
  }
311
397
  async hasProperty(name) {
312
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.5",
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",