trickle-observe 0.2.130 → 0.2.131
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/observe-esm-hooks.mjs +74 -57
- package/package.json +1 -1
package/observe-esm-hooks.mjs
CHANGED
|
@@ -648,87 +648,104 @@ function transformSource(source, url, originalSource) {
|
|
|
648
648
|
}
|
|
649
649
|
}
|
|
650
650
|
|
|
651
|
-
// ── Detect framework imports and
|
|
652
|
-
//
|
|
651
|
+
// ── Detect framework imports and rewrite to auto-instrument all instances ──
|
|
652
|
+
// Strategy: rewrite the import to wrap the factory/constructor so EVERY
|
|
653
|
+
// instance created from it is automatically instrumented.
|
|
654
|
+
// E.g., `import { Hono } from 'hono'` becomes:
|
|
655
|
+
// import { Hono as __OrigHono } from 'hono'
|
|
656
|
+
// const Hono = (...args) => { const a = new __OrigHono(...args); instrumentHono(a); return a; }
|
|
653
657
|
const frameworkInjects = [];
|
|
654
658
|
const expressImportMatch = source.match(/import\s+(\w+)\s+from\s+['"]express['"]/);
|
|
655
659
|
const fastifyImportMatch = source.match(/import\s+(\w+)\s+from\s+['"]fastify['"]/);
|
|
656
660
|
const koaImportMatch = source.match(/import\s+(\w+)\s+from\s+['"]koa['"]/);
|
|
657
|
-
const honoImportMatch = source.match(/import\s+\{\s*Hono\s*\}\s+from\s+['"]hono['"]/);
|
|
661
|
+
const honoImportMatch = source.match(/import\s+\{\s*Hono\s*(?:,\s*\w+)*\s*\}\s+from\s+['"]hono['"]/);
|
|
658
662
|
|
|
659
663
|
if (expressImportMatch) {
|
|
660
|
-
// Express: imported as factory. Detect `const app = express()` and inject instrument(app)
|
|
661
664
|
const factoryName = expressImportMatch[1];
|
|
662
|
-
|
|
663
|
-
if (appMatch) {
|
|
664
|
-
frameworkInjects.push({ framework: 'express', appVar: appMatch[1], importPath: config.wrapperPath.replace('/wrap.js', '/express.js') });
|
|
665
|
-
}
|
|
665
|
+
frameworkInjects.push({ framework: 'express', factoryName, type: 'factory', importPath: config.wrapperPath.replace('/wrap.js', '/express.js') });
|
|
666
666
|
}
|
|
667
667
|
if (fastifyImportMatch) {
|
|
668
668
|
const factoryName = fastifyImportMatch[1];
|
|
669
|
-
|
|
670
|
-
if (appMatch) {
|
|
671
|
-
frameworkInjects.push({ framework: 'fastify', appVar: appMatch[1], importPath: config.wrapperPath.replace('/wrap.js', '/fastify.js') });
|
|
672
|
-
}
|
|
669
|
+
frameworkInjects.push({ framework: 'fastify', factoryName, type: 'factory', importPath: config.wrapperPath.replace('/wrap.js', '/fastify.js') });
|
|
673
670
|
}
|
|
674
671
|
if (koaImportMatch) {
|
|
675
672
|
const className = koaImportMatch[1];
|
|
676
|
-
|
|
677
|
-
if (appMatch) {
|
|
678
|
-
frameworkInjects.push({ framework: 'koa', appVar: appMatch[1], importPath: config.wrapperPath.replace('/wrap.js', '/koa.js') });
|
|
679
|
-
}
|
|
673
|
+
frameworkInjects.push({ framework: 'koa', factoryName: className, type: 'class', importPath: config.wrapperPath.replace('/wrap.js', '/koa.js') });
|
|
680
674
|
}
|
|
681
675
|
if (honoImportMatch) {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
676
|
+
frameworkInjects.push({ framework: 'hono', factoryName: 'Hono', type: 'class', importPath: config.wrapperPath.replace('/wrap.js', '/hono.js') });
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Rewrite framework imports in the result array
|
|
680
|
+
for (const fi of frameworkInjects) {
|
|
681
|
+
const instrumentFn = fi.framework === 'express' ? 'instrumentExpress'
|
|
682
|
+
: fi.framework === 'fastify' ? 'instrumentFastify'
|
|
683
|
+
: fi.framework === 'koa' ? 'instrumentKoa'
|
|
684
|
+
: 'instrumentHono';
|
|
685
|
+
const fiPath = fi.importPath.replace(/\\/g, '\\\\');
|
|
686
|
+
|
|
687
|
+
for (let ri = 0; ri < result.length; ri++) {
|
|
688
|
+
const line = result[ri];
|
|
689
|
+
|
|
690
|
+
if (fi.framework === 'hono' && /import\s+\{[^}]*Hono[^}]*\}\s+from\s+['"]hono['"]/.test(line)) {
|
|
691
|
+
// Rewrite: import { Hono } from 'hono' → import { Hono as __OrigHono } from 'hono'
|
|
692
|
+
result[ri] = line.replace(/\bHono\b/, 'Hono as __OrigHono');
|
|
693
|
+
// Insert wrapper after imports are hoisted (add at this position, it'll run after all imports)
|
|
694
|
+
result.splice(ri + 1, 0,
|
|
695
|
+
`import { createRequire as __cr_hono } from 'node:module';`,
|
|
696
|
+
`const __rq_hono = __cr_hono(import.meta.url);`,
|
|
697
|
+
`const __fwHono = __rq_hono('${fiPath}');`,
|
|
698
|
+
`const Hono = function(...args) { const a = new __OrigHono(...args); try { __fwHono.${instrumentFn}(a, { environment: process.env.TRICKLE_ENV || 'development' }); } catch(e) {} return a; };`,
|
|
699
|
+
`Hono.prototype = __OrigHono.prototype;`,
|
|
700
|
+
);
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (fi.framework === 'express' && new RegExp(`import\\s+${fi.factoryName}\\s+from\\s+['"]express['"]`).test(line)) {
|
|
705
|
+
result[ri] = line.replace(fi.factoryName, `__OrigExpress`);
|
|
706
|
+
result.splice(ri + 1, 0,
|
|
707
|
+
`import { createRequire as __cr_express } from 'node:module';`,
|
|
708
|
+
`const __rq_express = __cr_express(import.meta.url);`,
|
|
709
|
+
`const __fwExpress = __rq_express('${fiPath}');`,
|
|
710
|
+
`const ${fi.factoryName} = function(...args) { const a = __OrigExpress(...args); try { __fwExpress.${instrumentFn}(a, { environment: process.env.TRICKLE_ENV || 'development' }); } catch(e) {} return a; };`,
|
|
711
|
+
);
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (fi.framework === 'fastify' && new RegExp(`import\\s+${fi.factoryName}\\s+from\\s+['"]fastify['"]`).test(line)) {
|
|
716
|
+
result[ri] = line.replace(fi.factoryName, `__OrigFastify`);
|
|
717
|
+
result.splice(ri + 1, 0,
|
|
718
|
+
`import { createRequire as __cr_fastify } from 'node:module';`,
|
|
719
|
+
`const __rq_fastify = __cr_fastify(import.meta.url);`,
|
|
720
|
+
`const __fwFastify = __rq_fastify('${fiPath}');`,
|
|
721
|
+
`const ${fi.factoryName} = function(...args) { const a = __OrigFastify(...args); try { __fwFastify.${instrumentFn}(a, { environment: process.env.TRICKLE_ENV || 'development' }); } catch(e) {} return a; };`,
|
|
722
|
+
);
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (fi.framework === 'koa' && new RegExp(`import\\s+${fi.factoryName}\\s+from\\s+['"]koa['"]`).test(line)) {
|
|
727
|
+
result[ri] = line.replace(fi.factoryName, `__OrigKoa`);
|
|
728
|
+
result.splice(ri + 1, 0,
|
|
729
|
+
`import { createRequire as __cr_koa } from 'node:module';`,
|
|
730
|
+
`const __rq_koa = __cr_koa(import.meta.url);`,
|
|
731
|
+
`const __fwKoa = __rq_koa('${fiPath}');`,
|
|
732
|
+
`const ${fi.factoryName} = function(...args) { const a = new __OrigKoa(...args); try { __fwKoa.${instrumentFn}(a, { environment: process.env.TRICKLE_ENV || 'development' }); } catch(e) {} return a; };`,
|
|
733
|
+
);
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
685
736
|
}
|
|
686
737
|
}
|
|
687
738
|
|
|
688
|
-
//
|
|
739
|
+
// Framework imports are now rewritten above (constructor/factory wrapping).
|
|
740
|
+
// Early return if nothing else to transform.
|
|
689
741
|
if (exportedFunctions.length === 0 && exportedDefaults.length === 0 && namedExports.length === 0 && !hasVarTracing && frameworkInjects.length === 0) {
|
|
690
742
|
return source;
|
|
691
743
|
}
|
|
692
|
-
|
|
693
|
-
// If ONLY framework injections (no function wrapping needed), inject directly
|
|
694
744
|
if (exportedFunctions.length === 0 && exportedDefaults.length === 0 && namedExports.length === 0 && !hasVarTracing && frameworkInjects.length > 0) {
|
|
695
|
-
const lines = source.split('\n');
|
|
696
|
-
const injections = [];
|
|
697
|
-
for (const fi of frameworkInjects) {
|
|
698
|
-
const instrumentFn = fi.framework === 'express' ? 'instrumentExpress'
|
|
699
|
-
: fi.framework === 'fastify' ? 'instrumentFastify'
|
|
700
|
-
: fi.framework === 'koa' ? 'instrumentKoa'
|
|
701
|
-
: 'instrumentHono';
|
|
702
|
-
const fiPath = fi.importPath.replace(/\\/g, '\\\\');
|
|
703
|
-
injections.push(`import { createRequire as __cr_fw } from 'node:module';`);
|
|
704
|
-
injections.push(`const __req_fw = __cr_fw(import.meta.url);`);
|
|
705
|
-
injections.push(`try { const __fwMod = __req_fw('${fiPath}'); __fwMod.${instrumentFn}(${fi.appVar}, { environment: process.env.TRICKLE_ENV || 'development' }); } catch(__e) { if (${config.debug}) console.error('[trickle/esm] Framework instrumentation failed:', __e.message); }`);
|
|
706
|
-
}
|
|
707
|
-
// Find the line after the app creation and insert
|
|
708
|
-
for (const fi of frameworkInjects) {
|
|
709
|
-
const appPattern = new RegExp(`(?:const|let|var)\\s+${fi.appVar}\\s*=`);
|
|
710
|
-
for (let i = 0; i < lines.length; i++) {
|
|
711
|
-
if (appPattern.test(lines[i])) {
|
|
712
|
-
// Find end of statement
|
|
713
|
-
let endLine = i;
|
|
714
|
-
let depth = 0;
|
|
715
|
-
for (let j = i; j < lines.length; j++) {
|
|
716
|
-
for (const ch of lines[j]) {
|
|
717
|
-
if (ch === '(' || ch === '{' || ch === '[') depth++;
|
|
718
|
-
if (ch === ')' || ch === '}' || ch === ']') depth--;
|
|
719
|
-
}
|
|
720
|
-
if (lines[j].includes(';') && depth <= 0) { endLine = j; break; }
|
|
721
|
-
if (j > i && depth <= 0) { endLine = j - 1; break; }
|
|
722
|
-
}
|
|
723
|
-
lines.splice(endLine + 1, 0, ...injections);
|
|
724
|
-
break;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
745
|
if (config.debug) {
|
|
729
|
-
console.log(`[trickle/esm]
|
|
746
|
+
console.log(`[trickle/esm] Wrapped ${frameworkInjects.map(f => f.framework).join(', ')} constructor(s) in ${moduleName}`);
|
|
730
747
|
}
|
|
731
|
-
return
|
|
748
|
+
return result.join('\n');
|
|
732
749
|
}
|
|
733
750
|
|
|
734
751
|
// Add wrapper import and wrapping code
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trickle-observe",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.131",
|
|
4
4
|
"description": "Zero-code runtime observability for JavaScript/TypeScript. Auto-instruments Express, Fastify, Koa, Hono, OpenAI, Anthropic, Gemini, MCP. Captures functions, variables, LLM calls, agent workflows.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|