unreflect 0.0.5 → 0.0.7

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);
@@ -121,52 +23,125 @@ var ReflectionClass = class {
121
23
  }
122
24
  this.values.set(name, value);
123
25
  const proto = Object.getPrototypeOf(obj);
26
+ const classSource = proto.constructor.toString();
27
+ if (!(/* @__PURE__ */ new RegExp(`#${baseName}\\b`)).test(classSource)) return;
124
28
  const protoDescriptors = Object.getOwnPropertyDescriptors(proto);
29
+ let targetGetterName = null;
30
+ let isGetter = true;
125
31
  for (const [propName, descriptor] of Object.entries(protoDescriptors)) {
126
32
  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);
33
+ if (descriptor.get) {
34
+ if (descriptor.get.toString().includes(`#${baseName}`)) {
35
+ targetGetterName = propName;
36
+ break;
147
37
  }
148
38
  }
39
+ }
40
+ if (!targetGetterName) for (const [propName, descriptor] of Object.entries(protoDescriptors)) {
41
+ if (propName === "constructor") continue;
42
+ if (descriptor.get) {
43
+ targetGetterName = propName;
44
+ break;
45
+ }
46
+ }
47
+ if (!targetGetterName) for (const [propName, descriptor] of Object.entries(protoDescriptors)) {
48
+ if (propName === "constructor") continue;
149
49
  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
- });
50
+ if (descriptor.value.toString().includes(`#${baseName}`)) {
51
+ targetGetterName = propName;
52
+ isGetter = false;
53
+ break;
167
54
  }
168
55
  }
169
56
  }
57
+ if (!targetGetterName) for (const [propName, descriptor] of Object.entries(protoDescriptors)) {
58
+ if (propName === "constructor") continue;
59
+ if (typeof descriptor.value === "function") {
60
+ targetGetterName = propName;
61
+ isGetter = false;
62
+ break;
63
+ }
64
+ }
65
+ if (!targetGetterName) return;
66
+ const objId = `__reflection_obj_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
67
+ const valId = `__reflection_val_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
68
+ globalThis[objId] = obj;
69
+ globalThis[valId] = value;
70
+ const session = new inspector.Session();
71
+ session.connect();
72
+ const capturedGetterName = targetGetterName;
73
+ return new Promise((resolve, reject) => {
74
+ let resolved = false;
75
+ const cleanup = () => {
76
+ if (!resolved) {
77
+ resolved = true;
78
+ delete globalThis[objId];
79
+ delete globalThis[valId];
80
+ session.disconnect();
81
+ }
82
+ };
83
+ session.on("Debugger.paused", (message) => {
84
+ const frames = message.params.callFrames;
85
+ if (frames && frames.length > 0) {
86
+ const callFrameId = frames[0].callFrameId;
87
+ session.post("Debugger.evaluateOnCallFrame", {
88
+ callFrameId,
89
+ expression: `this.#${baseName} = globalThis["${valId}"]`
90
+ }, () => {
91
+ session.post("Debugger.resume", () => {
92
+ cleanup();
93
+ resolve();
94
+ });
95
+ });
96
+ }
97
+ });
98
+ session.post("Debugger.enable", (err) => {
99
+ if (err) {
100
+ cleanup();
101
+ reject(err);
102
+ return;
103
+ }
104
+ session.post("Runtime.enable", (err$1) => {
105
+ if (err$1) {
106
+ cleanup();
107
+ reject(err$1);
108
+ return;
109
+ }
110
+ session.post("Runtime.evaluate", { expression: `(function() {
111
+ const proto = Object.getPrototypeOf(globalThis["${objId}"]);
112
+ const desc = Object.getOwnPropertyDescriptor(proto, "${capturedGetterName}");
113
+ return ${isGetter} ? (desc && desc.get) : (desc && desc.value);
114
+ })()` }, (err$2, fnResult) => {
115
+ if (err$2 || !fnResult?.result?.objectId) {
116
+ cleanup();
117
+ reject(err$2 || /* @__PURE__ */ new Error("Failed to get getter function"));
118
+ return;
119
+ }
120
+ session.post("Debugger.setBreakpointOnFunctionCall", { objectId: fnResult.result.objectId }, (err$3, bpResult) => {
121
+ if (err$3) {
122
+ cleanup();
123
+ reject(err$3);
124
+ return;
125
+ }
126
+ const breakpointId = bpResult?.breakpointId;
127
+ setTimeout(() => {
128
+ try {
129
+ if (isGetter) obj[capturedGetterName];
130
+ else obj[capturedGetterName]();
131
+ } catch {}
132
+ }, 0);
133
+ setTimeout(() => {
134
+ if (!resolved) {
135
+ if (breakpointId) session.post("Debugger.removeBreakpoint", { breakpointId }, () => {});
136
+ cleanup();
137
+ reject(/* @__PURE__ */ new Error("Timeout waiting for breakpoint"));
138
+ }
139
+ }, 5e3);
140
+ });
141
+ });
142
+ });
143
+ });
144
+ });
170
145
  }
171
146
  async getProperty(name) {
172
147
  const obj = this.obj;
@@ -189,33 +164,65 @@ var ReflectionClass = class {
189
164
  globalThis[id] = obj;
190
165
  const session = new inspector.Session();
191
166
  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; }`
167
+ return new Promise((resolve, reject) => {
168
+ session.post("Runtime.enable", (err) => {
169
+ if (err) {
170
+ delete globalThis[id];
171
+ session.disconnect();
172
+ reject(err);
173
+ return;
174
+ }
175
+ session.post("Runtime.evaluate", { expression: `globalThis.${id}` }, (err$1, evalResult) => {
176
+ if (err$1 || !evalResult?.result?.objectId) {
177
+ delete globalThis[id];
178
+ session.disconnect();
179
+ if (err$1) reject(err$1);
180
+ else resolve(void 0);
181
+ return;
182
+ }
183
+ session.post("Runtime.getProperties", {
184
+ objectId: evalResult.result.objectId,
185
+ ownProperties: true
186
+ }, (err$2, propsResult) => {
187
+ if (err$2) {
188
+ delete globalThis[id];
189
+ session.disconnect();
190
+ reject(err$2);
191
+ return;
192
+ }
193
+ const privateProp = propsResult.privateProperties?.find((p) => p.name === `#${baseName}`);
194
+ if (!privateProp) {
195
+ delete globalThis[id];
196
+ session.disconnect();
197
+ resolve(void 0);
198
+ return;
199
+ }
200
+ const extractValue = (prop, callback) => {
201
+ if ((prop.value.type === "object" || prop.value.type === "function") && prop.value.objectId) {
202
+ const extractId = `__extract_${Date.now()}_${Math.random().toString(36).slice(2)}__`;
203
+ session.post("Runtime.callFunctionOn", {
204
+ objectId: prop.value.objectId,
205
+ functionDeclaration: `function() { globalThis["${extractId}"] = this; }`
206
+ }, () => {
207
+ const value = globalThis[extractId];
208
+ delete globalThis[extractId];
209
+ callback(value);
210
+ });
211
+ } else callback(prop.value.value);
212
+ };
213
+ extractValue(privateProp, (value) => {
214
+ delete globalThis[id];
215
+ session.disconnect();
216
+ resolve({
217
+ name,
218
+ visibility: "private",
219
+ value
220
+ });
221
+ });
222
+ });
206
223
  });
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
- }
224
+ });
225
+ });
219
226
  }
220
227
  async getPropertyValue(name) {
221
228
  return (await this.getProperty(name))?.value;
@@ -239,34 +246,56 @@ var ReflectionClass = class {
239
246
  globalThis[id] = obj;
240
247
  const session = new inspector.Session();
241
248
  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"
249
+ return new Promise((resolve, reject) => {
250
+ session.post("Runtime.enable", (err) => {
251
+ if (err) {
252
+ delete globalThis[id];
253
+ session.disconnect();
254
+ reject(err);
255
+ return;
256
+ }
257
+ session.post("Runtime.evaluate", { expression: `globalThis.${id}` }, (err$1, evalResult) => {
258
+ if (err$1 || !evalResult?.result?.objectId) {
259
+ delete globalThis[id];
260
+ session.disconnect();
261
+ if (err$1) reject(err$1);
262
+ else resolve(properties);
263
+ return;
264
+ }
265
+ session.post("Runtime.getProperties", {
266
+ objectId: evalResult.result.objectId,
267
+ ownProperties: true
268
+ }, (err$2, propsResult) => {
269
+ delete globalThis[id];
270
+ session.disconnect();
271
+ if (err$2) {
272
+ reject(err$2);
273
+ return;
274
+ }
275
+ if (propsResult.privateProperties) for (const prop of propsResult.privateProperties) properties.push({
276
+ name: prop.name,
277
+ visibility: "private"
278
+ });
279
+ resolve(properties);
280
+ });
281
+ });
252
282
  });
253
- } finally {
254
- delete globalThis[id];
255
- session.disconnect();
256
- }
257
- return properties;
283
+ });
258
284
  }
259
285
  async getMethods() {
260
286
  const obj = this.obj;
261
287
  const proto = Object.getPrototypeOf(obj);
262
288
  const constructor = proto.constructor;
263
289
  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
- });
290
+ const protoDescriptors = Object.getOwnPropertyDescriptors(proto);
291
+ for (const [name, descriptor] of Object.entries(protoDescriptors)) {
292
+ if (name === "constructor") continue;
293
+ if (typeof descriptor.value === "function") methods.push({
294
+ name,
295
+ visibility: "public",
296
+ isStatic: false
297
+ });
298
+ }
270
299
  const staticMethods = Object.getOwnPropertyNames(constructor).filter((n) => typeof constructor[n] === "function" && ![
271
300
  "length",
272
301
  "name",
@@ -281,32 +310,63 @@ var ReflectionClass = class {
281
310
  globalThis[id] = obj;
282
311
  const session = new inspector.Session();
283
312
  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
- });
313
+ return new Promise((resolve, reject) => {
314
+ session.post("Runtime.enable", (err) => {
315
+ if (err) {
316
+ delete globalThis[id];
317
+ session.disconnect();
318
+ reject(err);
319
+ return;
303
320
  }
304
- }
305
- } finally {
306
- delete globalThis[id];
307
- session.disconnect();
308
- }
309
- return methods;
321
+ session.post("Runtime.evaluate", { expression: `globalThis.${id}` }, (err$1, evalResult) => {
322
+ if (err$1 || !evalResult?.result?.objectId) {
323
+ delete globalThis[id];
324
+ session.disconnect();
325
+ if (err$1) reject(err$1);
326
+ else resolve(methods);
327
+ return;
328
+ }
329
+ session.post("Runtime.getProperties", {
330
+ objectId: evalResult.result.objectId,
331
+ ownProperties: true
332
+ }, (err$2, propsResult) => {
333
+ if (err$2) {
334
+ delete globalThis[id];
335
+ session.disconnect();
336
+ reject(err$2);
337
+ return;
338
+ }
339
+ const privateMethods = propsResult.internalProperties?.find((p) => p.name === "[[PrivateMethods]]");
340
+ if (!privateMethods?.value?.objectId) {
341
+ delete globalThis[id];
342
+ session.disconnect();
343
+ resolve(methods);
344
+ return;
345
+ }
346
+ session.post("Runtime.getProperties", {
347
+ objectId: privateMethods.value.objectId,
348
+ ownProperties: true
349
+ }, (err$3, methodsPropsResult) => {
350
+ delete globalThis[id];
351
+ session.disconnect();
352
+ if (err$3) {
353
+ reject(err$3);
354
+ return;
355
+ }
356
+ for (const prop of methodsPropsResult.result || []) {
357
+ const match = (prop.value?.description || "").match(/^(#\w+)\s*\(/);
358
+ if (match) methods.push({
359
+ name: match[1],
360
+ visibility: "private",
361
+ isStatic: false
362
+ });
363
+ }
364
+ resolve(methods);
365
+ });
366
+ });
367
+ });
368
+ });
369
+ });
310
370
  }
311
371
  async hasProperty(name) {
312
372
  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.7",
4
4
  "description": "Access and modify private class fields and methods in JavaScript",
5
5
  "repository": "KABBOUCHI/unreflect",
6
6
  "license": "MIT",