taist 0.1.11 → 0.1.13

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.
Files changed (2) hide show
  1. package/lib/transform.js +61 -4
  2. package/package.json +1 -1
package/lib/transform.js CHANGED
@@ -30,7 +30,7 @@ export function hasExports(code) {
30
30
  /**
31
31
  * Find all exports in the source code
32
32
  * @param {string} source - Source code
33
- * @returns {Array<{name: string, type: 'function'|'const'|'class', declaration: string|null}>}
33
+ * @returns {Array<{name: string, type: 'function'|'const'|'class'|'object', declaration: string|null}>}
34
34
  */
35
35
  export function findExports(source) {
36
36
  const exports = [];
@@ -49,6 +49,16 @@ export function findExports(source) {
49
49
  exports.push({ name: match[1], type: "const", declaration: "inline" });
50
50
  }
51
51
 
52
+ // Match: export const name = { - object literal exports
53
+ // Negative lookahead to avoid matching functions/arrows already caught above
54
+ const objectExportRegex = /export\s+const\s+(\w+)\s*=\s*\{/g;
55
+ while ((match = objectExportRegex.exec(source)) !== null) {
56
+ const name = match[1];
57
+ // Skip if already found as a function/const export
58
+ if (exports.some(e => e.name === name)) continue;
59
+ exports.push({ name, type: "object", declaration: "inline" });
60
+ }
61
+
52
62
  // Match: export class ClassName
53
63
  const classExportRegex = /export\s+class\s+(\w+)/g;
54
64
  while ((match = classExportRegex.exec(source)) !== null) {
@@ -66,13 +76,14 @@ export function findExports(source) {
66
76
  const isFunction = new RegExp(`function\\s+${name}\\s*\\(`).test(source) ||
67
77
  new RegExp(`(const|let|var)\\s+${name}\\s*=\\s*(async\\s*)?\\(`).test(source) ||
68
78
  new RegExp(`(const|let|var)\\s+${name}\\s*=\\s*(async\\s+)?function`).test(source);
79
+ const isObject = new RegExp(`(const|let|var)\\s+${name}\\s*=\\s*\\{`).test(source);
69
80
 
70
81
  // Skip if already found as inline export
71
82
  if (exports.some(e => e.name === name)) continue;
72
83
 
73
84
  exports.push({
74
85
  name,
75
- type: isClass ? "class" : isFunction ? "function" : "unknown",
86
+ type: isClass ? "class" : isFunction ? "function" : isObject ? "object" : "unknown",
76
87
  declaration: "named"
77
88
  });
78
89
  }
@@ -134,13 +145,16 @@ export function transformSource(source, moduleNameOrOptions, tracerImportPath) {
134
145
  import { getGlobalReporter as __taist_getReporter } from "${reporterPath}";
135
146
  import { getContext as __taist_getContext, runWithContext as __taist_runWithContext, generateId as __taist_generateId } from "${traceContextPath}";
136
147
  const __taist_reporter = __taist_getReporter();
148
+ const __taist_debug = process.env.TAIST_DEBUG === 'true';
137
149
  // Eagerly connect to collector if socket path is set (build-time instrumentation)
138
150
  if (process.env.TAIST_COLLECTOR_SOCKET && !__taist_reporter.isConnected()) {
139
151
  __taist_reporter.connectEager();
140
152
  }
141
153
  const __taist_wrap = (fn, name) => {
142
154
  if (typeof fn !== 'function') return fn;
155
+ if (__taist_debug) console.log('[taist] wrapping:', name);
143
156
  const wrapped = function(...args) {
157
+ if (__taist_debug) console.log('[taist] CALLED:', name);
144
158
  const parentCtx = __taist_getContext();
145
159
  const id = __taist_generateId();
146
160
  const depth = parentCtx.depth;
@@ -205,6 +219,21 @@ const __taist_instrumentClass = (cls, name) => {
205
219
  }
206
220
  return cls;
207
221
  };
222
+ const __taist_instrumentObject = (obj, name, visited = new WeakSet()) => {
223
+ if (!obj || typeof obj !== 'object' || visited.has(obj)) return obj;
224
+ if (__taist_debug) console.log('[taist] instrumentObject:', name, Object.keys(obj));
225
+ visited.add(obj);
226
+ for (const key of Object.keys(obj)) {
227
+ const value = obj[key];
228
+ if (typeof value === 'function') {
229
+ if (__taist_debug) console.log('[taist] found method:', name + '.' + key);
230
+ obj[key] = __taist_wrap(value, name + '.' + key);
231
+ } else if (value && typeof value === 'object' && !Array.isArray(value)) {
232
+ __taist_instrumentObject(value, name + '.' + key, visited);
233
+ }
234
+ }
235
+ return obj;
236
+ };
208
237
  const __taist_module = "${moduleName}";
209
238
  // --- END TAIST ---
210
239
 
@@ -227,6 +256,20 @@ const __taist_instrumentClass = (cls, name) => {
227
256
  __taist.instrument(cls, name);
228
257
  return cls;
229
258
  };
259
+ const __taist_instrumentObject = (obj, name, visited = new WeakSet()) => {
260
+ if (!__taist?.options?.enabled) return obj;
261
+ if (!obj || typeof obj !== 'object' || visited.has(obj)) return obj;
262
+ visited.add(obj);
263
+ for (const key of Object.keys(obj)) {
264
+ const value = obj[key];
265
+ if (typeof value === 'function') {
266
+ obj[key] = __taist_wrap(value, name + '.' + key);
267
+ } else if (value && typeof value === 'object' && !Array.isArray(value)) {
268
+ __taist_instrumentObject(value, name + '.' + key, visited);
269
+ }
270
+ }
271
+ return obj;
272
+ };
230
273
  const __taist_module = "${moduleName}";
231
274
  // --- END TAIST ---
232
275
 
@@ -246,9 +289,10 @@ const __taist_module = "${moduleName}";
246
289
 
247
290
  let transformed = shebang + injection + sourceWithoutShebang;
248
291
 
249
- // Separate classes from functions - classes need different handling to preserve hoisting
292
+ // Separate exports by type - each needs different handling
250
293
  const classExports = exports.filter(e => e.type === "class");
251
- const functionExports = exports.filter(e => e.type !== "class");
294
+ const objectExports = exports.filter(e => e.type === "object");
295
+ const functionExports = exports.filter(e => e.type !== "class" && e.type !== "object");
252
296
 
253
297
  // For CLASSES: Keep original export, instrument in-place (preserves hoisting)
254
298
  // This avoids TDZ issues with circular dependencies
@@ -355,6 +399,15 @@ const __taist_module = "${moduleName}";
355
399
  })
356
400
  .join("\n");
357
401
 
402
+ // Add in-place instrumentation for OBJECT LITERALS (wraps nested methods)
403
+ // __taist_instrumentObject recursively wraps function properties
404
+ const objectInstrumentations = objectExports
405
+ .map((exp) => {
406
+ const nameExpr = `(__taist_module === "${exp.name}" ? "${exp.name}" : __taist_module + ".${exp.name}")`;
407
+ return `__taist_instrumentObject(${exp.name}, ${nameExpr});`;
408
+ })
409
+ .join("\n");
410
+
358
411
  transformed += `\n\n// --- TAIST INSTRUMENTATION ---\n`;
359
412
 
360
413
  if (functionReexports) {
@@ -365,6 +418,10 @@ const __taist_module = "${moduleName}";
365
418
  transformed += `// In-place class instrumentation (preserves hoisting)\n${classInstrumentations}\n`;
366
419
  }
367
420
 
421
+ if (objectInstrumentations) {
422
+ transformed += `// In-place object literal instrumentation (wraps nested methods)\n${objectInstrumentations}\n`;
423
+ }
424
+
368
425
  // Add default exports at the end (after the wrapped versions are defined)
369
426
  for (const name of defaultExports) {
370
427
  transformed += `export default ${name};\n`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taist",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Token-Optimized Testing Framework for AI-Assisted Development",
5
5
  "main": "index.js",
6
6
  "type": "module",