trickle-observe 0.1.0
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/auto-env.js +13 -0
- package/auto-esm.mjs +128 -0
- package/auto.js +3 -0
- package/dist/auto-codegen.d.ts +29 -0
- package/dist/auto-codegen.js +999 -0
- package/dist/auto-register.d.ts +16 -0
- package/dist/auto-register.js +99 -0
- package/dist/cache.d.ts +27 -0
- package/dist/cache.js +52 -0
- package/dist/env-detect.d.ts +5 -0
- package/dist/env-detect.js +35 -0
- package/dist/express.d.ts +44 -0
- package/dist/express.js +342 -0
- package/dist/fetch-observer.d.ts +24 -0
- package/dist/fetch-observer.js +217 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +172 -0
- package/dist/observe-register.d.ts +29 -0
- package/dist/observe-register.js +455 -0
- package/dist/observe.d.ts +44 -0
- package/dist/observe.js +109 -0
- package/dist/proxy-tracker.d.ts +15 -0
- package/dist/proxy-tracker.js +172 -0
- package/dist/register.d.ts +21 -0
- package/dist/register.js +105 -0
- package/dist/transport.d.ts +22 -0
- package/dist/transport.js +228 -0
- package/dist/type-hash.d.ts +5 -0
- package/dist/type-hash.js +60 -0
- package/dist/type-inference.d.ts +14 -0
- package/dist/type-inference.js +259 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.js +2 -0
- package/dist/wrap.d.ts +10 -0
- package/dist/wrap.js +247 -0
- package/observe-esm-hooks.mjs +367 -0
- package/observe-esm.mjs +40 -0
- package/observe.js +2 -0
- package/package.json +26 -0
- package/register.js +2 -0
- package/src/auto-codegen.ts +1058 -0
- package/src/auto-register.ts +102 -0
- package/src/cache.ts +53 -0
- package/src/env-detect.ts +22 -0
- package/src/express.ts +386 -0
- package/src/fetch-observer.ts +226 -0
- package/src/index.ts +199 -0
- package/src/observe-register.ts +453 -0
- package/src/observe.ts +127 -0
- package/src/proxy-tracker.ts +208 -0
- package/src/register.ts +110 -0
- package/src/transport.ts +207 -0
- package/src/type-hash.ts +71 -0
- package/src/type-inference.ts +285 -0
- package/src/types.ts +61 -0
- package/src/wrap.ts +289 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESM loader hooks for trickle observation.
|
|
3
|
+
*
|
|
4
|
+
* Transforms user ESM modules to wrap exported functions with trickle
|
|
5
|
+
* observation. Runs in a separate loader thread (Node.js >= 20.6).
|
|
6
|
+
*
|
|
7
|
+
* Strategy: For each user module, the `load` hook:
|
|
8
|
+
* 1. Strips `export` from function/const declarations
|
|
9
|
+
* 2. Appends wrapper code that wraps each exported function
|
|
10
|
+
* 3. Re-exports the wrapped versions
|
|
11
|
+
*/
|
|
12
|
+
import { createRequire } from 'node:module';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { basename, sep } from 'node:path';
|
|
15
|
+
|
|
16
|
+
let config = {
|
|
17
|
+
wrapperPath: '',
|
|
18
|
+
transportPath: '',
|
|
19
|
+
envDetectPath: '',
|
|
20
|
+
backendUrl: 'http://localhost:4888',
|
|
21
|
+
debug: false,
|
|
22
|
+
includePatterns: [],
|
|
23
|
+
excludePatterns: [],
|
|
24
|
+
initialized: false,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function initialize(data) {
|
|
28
|
+
config = { ...config, ...data, initialized: true };
|
|
29
|
+
|
|
30
|
+
// Configure trickle transport from the loader thread
|
|
31
|
+
try {
|
|
32
|
+
const require = createRequire(import.meta.url);
|
|
33
|
+
const { configure } = require(config.transportPath);
|
|
34
|
+
const { detectEnvironment } = require(config.envDetectPath);
|
|
35
|
+
const environment = process.env.TRICKLE_ENV || detectEnvironment();
|
|
36
|
+
|
|
37
|
+
configure({
|
|
38
|
+
backendUrl: config.backendUrl,
|
|
39
|
+
batchIntervalMs: 2000,
|
|
40
|
+
debug: config.debug,
|
|
41
|
+
enabled: true,
|
|
42
|
+
environment,
|
|
43
|
+
});
|
|
44
|
+
} catch (err) {
|
|
45
|
+
if (config.debug) {
|
|
46
|
+
console.error('[trickle/esm] Failed to configure transport:', err.message);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Determine if a URL should be observed.
|
|
53
|
+
*/
|
|
54
|
+
function shouldObserve(url) {
|
|
55
|
+
// Only file:// URLs
|
|
56
|
+
if (!url.startsWith('file://')) return false;
|
|
57
|
+
|
|
58
|
+
let filePath;
|
|
59
|
+
try {
|
|
60
|
+
filePath = fileURLToPath(url);
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Skip node_modules
|
|
66
|
+
if (filePath.includes(`${sep}node_modules${sep}`)) return false;
|
|
67
|
+
|
|
68
|
+
// Skip trickle's own modules
|
|
69
|
+
if (filePath.includes(`${sep}client-js${sep}`)) return false;
|
|
70
|
+
if (filePath.includes(`${sep}trickle${sep}dist${sep}`)) return false;
|
|
71
|
+
|
|
72
|
+
// Only JS/TS files
|
|
73
|
+
if (!/\.(m?js|jsx|ts|tsx)$/.test(filePath)) return false;
|
|
74
|
+
|
|
75
|
+
// Apply include filters
|
|
76
|
+
if (config.includePatterns.length > 0) {
|
|
77
|
+
if (!config.includePatterns.some(p => filePath.includes(p))) return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Apply exclude filters
|
|
81
|
+
if (config.excludePatterns.length > 0) {
|
|
82
|
+
if (config.excludePatterns.some(p => filePath.includes(p))) return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Extract module name from a file URL.
|
|
90
|
+
*/
|
|
91
|
+
function moduleNameFromUrl(url) {
|
|
92
|
+
try {
|
|
93
|
+
const filePath = fileURLToPath(url);
|
|
94
|
+
return basename(filePath).replace(/\.(m?js|jsx|ts|tsx)$/, '');
|
|
95
|
+
} catch {
|
|
96
|
+
return 'unknown';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Transform ESM source to wrap exported functions.
|
|
102
|
+
*
|
|
103
|
+
* Handles:
|
|
104
|
+
* - export function name(...) { ... }
|
|
105
|
+
* - export async function name(...) { ... }
|
|
106
|
+
* - export const name = (...) => ...
|
|
107
|
+
* - export const name = function(...) { ... }
|
|
108
|
+
* - export const name = async (...) => ...
|
|
109
|
+
* - export default function name(...) { ... }
|
|
110
|
+
* - export default function(...) { ... }
|
|
111
|
+
* - export { name1, name2 }
|
|
112
|
+
*/
|
|
113
|
+
/**
|
|
114
|
+
* Extract parameter names from a function source snippet (everything after the function name).
|
|
115
|
+
*/
|
|
116
|
+
function extractParamNamesFromSource(source, startIdx) {
|
|
117
|
+
// Find the opening paren
|
|
118
|
+
const openParen = source.indexOf('(', startIdx);
|
|
119
|
+
if (openParen === -1) return [];
|
|
120
|
+
// Find matching close paren (handles nested parens in TS type annotations)
|
|
121
|
+
let depth = 1;
|
|
122
|
+
let i = openParen + 1;
|
|
123
|
+
while (i < source.length && depth > 0) {
|
|
124
|
+
if (source[i] === '(') depth++;
|
|
125
|
+
else if (source[i] === ')') depth--;
|
|
126
|
+
i++;
|
|
127
|
+
}
|
|
128
|
+
if (depth !== 0) return [];
|
|
129
|
+
const closeParen = i - 1;
|
|
130
|
+
const paramStr = source.slice(openParen + 1, closeParen).trim();
|
|
131
|
+
if (!paramStr) return [];
|
|
132
|
+
// Split on top-level commas only (skip commas inside nested parens/generics)
|
|
133
|
+
const params = [];
|
|
134
|
+
let current = '';
|
|
135
|
+
let parenDepth = 0;
|
|
136
|
+
let angleDepth = 0;
|
|
137
|
+
for (let j = 0; j < paramStr.length; j++) {
|
|
138
|
+
const ch = paramStr[j];
|
|
139
|
+
if (ch === '(' || ch === '{' || ch === '[') parenDepth++;
|
|
140
|
+
else if (ch === ')' || ch === '}' || ch === ']') parenDepth--;
|
|
141
|
+
else if (ch === '<') angleDepth++;
|
|
142
|
+
else if (ch === '>' && paramStr[j - 1] !== '=') {
|
|
143
|
+
// Don't count '>' in '=>' (arrow functions) as closing a generic
|
|
144
|
+
if (angleDepth > 0) angleDepth--;
|
|
145
|
+
}
|
|
146
|
+
else if (ch === ',' && parenDepth === 0 && angleDepth === 0) {
|
|
147
|
+
params.push(current);
|
|
148
|
+
current = '';
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
current += ch;
|
|
152
|
+
}
|
|
153
|
+
if (current.trim()) params.push(current);
|
|
154
|
+
return params.map(p => {
|
|
155
|
+
// Strip TS type annotation and default value: "name: Type = default" → "name"
|
|
156
|
+
const trimmed = p.trim().split(':')[0].trim().split('=')[0].trim();
|
|
157
|
+
if (trimmed.startsWith('{') || trimmed.startsWith('[') || trimmed.startsWith('...')) return '';
|
|
158
|
+
return trimmed;
|
|
159
|
+
}).filter(Boolean);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Compute the byte offset of a given line index within the full source.
|
|
164
|
+
*/
|
|
165
|
+
function lineOffset(source, lineIdx) {
|
|
166
|
+
let off = 0;
|
|
167
|
+
const lines = source.split('\n');
|
|
168
|
+
for (let i = 0; i < lineIdx && i < lines.length; i++) {
|
|
169
|
+
off += lines[i].length + 1; // +1 for \n
|
|
170
|
+
}
|
|
171
|
+
return off;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function transformSource(source, url) {
|
|
175
|
+
const moduleName = moduleNameFromUrl(url);
|
|
176
|
+
const lines = source.split('\n');
|
|
177
|
+
const exportedFunctions = []; // { name, paramNames }
|
|
178
|
+
const exportedDefaults = []; // { name, paramNames }
|
|
179
|
+
const namedExports = []; // from `export { name }` statements
|
|
180
|
+
const result = [];
|
|
181
|
+
|
|
182
|
+
for (let i = 0; i < lines.length; i++) {
|
|
183
|
+
const line = lines[i];
|
|
184
|
+
const trimmed = line.trimStart();
|
|
185
|
+
|
|
186
|
+
// Skip TS-only exports: export interface, export type, export enum
|
|
187
|
+
if (/^export\s+(interface|type|enum|abstract|declare)\s/.test(trimmed)) {
|
|
188
|
+
result.push(line);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Skip re-exports: export { ... } from '...' or export * from '...'
|
|
193
|
+
if (/^export\s+\{[^}]*\}\s+from\s/.test(trimmed) || /^export\s+\*\s+from\s/.test(trimmed)) {
|
|
194
|
+
result.push(line);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// export function name(...) or export function name<T>(...)
|
|
199
|
+
const funcMatch = trimmed.match(/^export\s+(async\s+)?function\s+(\w+)\s*(?:<[^>]*>)?\s*\(/);
|
|
200
|
+
if (funcMatch) {
|
|
201
|
+
const name = funcMatch[2];
|
|
202
|
+
// Use full source from this line's offset for multiline param extraction
|
|
203
|
+
const srcOffset = lineOffset(source, i) + (line.length - trimmed.length);
|
|
204
|
+
const parenPos = source.indexOf('(', srcOffset + funcMatch[0].indexOf('('));
|
|
205
|
+
const paramNames = parenPos >= 0 ? extractParamNamesFromSource(source, parenPos) : [];
|
|
206
|
+
// Remove 'export ' prefix, keep the function
|
|
207
|
+
result.push(line.replace(/^(\s*)export\s+/, '$1'));
|
|
208
|
+
exportedFunctions.push({ name, paramNames });
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// export const name = (...) => or export const name = function or export const name = async
|
|
213
|
+
const constFuncMatch = trimmed.match(/^export\s+(const|let)\s+(\w+)\s*=\s*(async\s+)?(\(|function\b)/);
|
|
214
|
+
if (constFuncMatch) {
|
|
215
|
+
const name = constFuncMatch[2];
|
|
216
|
+
const srcOffset = lineOffset(source, i) + (line.length - trimmed.length);
|
|
217
|
+
const eqPos = source.indexOf('=', srcOffset);
|
|
218
|
+
const parenPos = eqPos >= 0 ? source.indexOf('(', eqPos) : -1;
|
|
219
|
+
const paramNames = parenPos >= 0 ? extractParamNamesFromSource(source, parenPos) : [];
|
|
220
|
+
result.push(line.replace(/^(\s*)export\s+/, '$1'));
|
|
221
|
+
exportedFunctions.push({ name, paramNames });
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// export const name = someValue (non-function — keep as-is)
|
|
226
|
+
const constNonFuncMatch = trimmed.match(/^export\s+(const|let|var)\s+(\w+)\s*=/);
|
|
227
|
+
if (constNonFuncMatch && !constFuncMatch) {
|
|
228
|
+
// Keep the export as-is for non-function values
|
|
229
|
+
result.push(line);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// export default function name(...) or export default function name<T>(...)
|
|
234
|
+
const defaultNamedMatch = trimmed.match(/^export\s+default\s+(async\s+)?function\s+(\w+)\s*(?:<[^>]*>)?\s*\(/);
|
|
235
|
+
if (defaultNamedMatch) {
|
|
236
|
+
const name = defaultNamedMatch[2];
|
|
237
|
+
const srcOffset = lineOffset(source, i) + (line.length - trimmed.length);
|
|
238
|
+
const parenPos = source.indexOf('(', srcOffset + defaultNamedMatch[0].indexOf('('));
|
|
239
|
+
const paramNames = parenPos >= 0 ? extractParamNamesFromSource(source, parenPos) : [];
|
|
240
|
+
// Remove 'export default'
|
|
241
|
+
result.push(line.replace(/^(\s*)export\s+default\s+/, '$1'));
|
|
242
|
+
exportedDefaults.push({ name, paramNames });
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// export default function(...) (anonymous)
|
|
247
|
+
const defaultAnonMatch = trimmed.match(/^export\s+default\s+(async\s+)?function\s*\(/);
|
|
248
|
+
if (defaultAnonMatch) {
|
|
249
|
+
const srcOffset = lineOffset(source, i) + (line.length - trimmed.length);
|
|
250
|
+
const parenPos = source.indexOf('(', srcOffset);
|
|
251
|
+
const paramNames = parenPos >= 0 ? extractParamNamesFromSource(source, parenPos) : [];
|
|
252
|
+
// Convert to named: const __trickle_default = function(...)
|
|
253
|
+
result.push(line.replace(
|
|
254
|
+
/^(\s*)export\s+default\s+(async\s+)?function\s*\(/,
|
|
255
|
+
'$1const __trickle_default = $2function('
|
|
256
|
+
));
|
|
257
|
+
exportedDefaults.push({ name: '__trickle_default', paramNames });
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// export { name1, name2 } (local named exports, not re-exports)
|
|
262
|
+
const namedExportMatch = trimmed.match(/^export\s+\{([^}]+)\}\s*;?\s*$/);
|
|
263
|
+
if (namedExportMatch) {
|
|
264
|
+
const names = namedExportMatch[1].split(',').map(s => s.trim()).filter(Boolean);
|
|
265
|
+
for (const nameSpec of names) {
|
|
266
|
+
// Handle: name or name as alias
|
|
267
|
+
const parts = nameSpec.split(/\s+as\s+/);
|
|
268
|
+
namedExports.push({ local: parts[0].trim(), exported: (parts[1] || parts[0]).trim() });
|
|
269
|
+
}
|
|
270
|
+
// Remove this export statement — we'll re-export at the bottom
|
|
271
|
+
result.push('// [trickle] moved export to bottom');
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// export class, export type, export interface — leave as-is
|
|
276
|
+
result.push(line);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// If nothing to wrap, return original
|
|
280
|
+
if (exportedFunctions.length === 0 && exportedDefaults.length === 0 && namedExports.length === 0) {
|
|
281
|
+
return source;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Add wrapper import and wrapping code at the bottom
|
|
285
|
+
const wrapperPathEscaped = config.wrapperPath.replace(/\\/g, '\\\\');
|
|
286
|
+
|
|
287
|
+
result.push('');
|
|
288
|
+
result.push('// [trickle] Auto-observation wrappers');
|
|
289
|
+
result.push(`import { createRequire as __cr } from 'node:module';`);
|
|
290
|
+
result.push(`const __require = __cr(import.meta.url);`);
|
|
291
|
+
result.push(`const { wrapFunction: __tw } = __require('${wrapperPathEscaped}');`);
|
|
292
|
+
result.push(`const __twOpts = (name, paramNames) => { const o = { functionName: name, module: '${moduleName}', trackArgs: true, trackReturn: true, sampleRate: 1, maxDepth: 5, environment: process.env.TRICKLE_ENV || 'development', enabled: true }; if (paramNames && paramNames.length) o.paramNames = paramNames; return o; };`);
|
|
293
|
+
|
|
294
|
+
// Wrap and re-export named functions
|
|
295
|
+
const reExports = [];
|
|
296
|
+
for (const { name, paramNames } of exportedFunctions) {
|
|
297
|
+
const wrappedName = `__trickle_${name}`;
|
|
298
|
+
const pn = paramNames.length > 0 ? JSON.stringify(paramNames) : 'null';
|
|
299
|
+
result.push(`const ${wrappedName} = typeof ${name} === 'function' ? __tw(${name}, __twOpts('${name}', ${pn})) : ${name};`);
|
|
300
|
+
reExports.push(`${wrappedName} as ${name}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Handle named exports from export { } statements
|
|
304
|
+
for (const { local, exported } of namedExports) {
|
|
305
|
+
const wrappedName = `__trickle_ne_${exported}`;
|
|
306
|
+
result.push(`const ${wrappedName} = typeof ${local} === 'function' ? __tw(${local}, __twOpts('${exported}', null)) : ${local};`);
|
|
307
|
+
reExports.push(`${wrappedName} as ${exported}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (reExports.length > 0) {
|
|
311
|
+
result.push(`export { ${reExports.join(', ')} };`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Handle default exports
|
|
315
|
+
for (const { name, paramNames } of exportedDefaults) {
|
|
316
|
+
const displayName = name === '__trickle_default' ? 'default' : name;
|
|
317
|
+
const pn = paramNames.length > 0 ? JSON.stringify(paramNames) : 'null';
|
|
318
|
+
result.push(`const __trickle_default_wrapped = typeof ${name} === 'function' ? __tw(${name}, __twOpts('${displayName}', ${pn})) : ${name};`);
|
|
319
|
+
result.push(`export default __trickle_default_wrapped;`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const transformed = result.join('\n');
|
|
323
|
+
|
|
324
|
+
if (config.debug) {
|
|
325
|
+
const fnCount = exportedFunctions.length + exportedDefaults.length + namedExports.length;
|
|
326
|
+
console.log(`[trickle/esm] Transformed ${fnCount} exports from ${moduleName} (${fileURLToPath(url)})`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return transformed;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* ESM load hook — intercepts module loading to transform user modules.
|
|
334
|
+
*/
|
|
335
|
+
export async function load(url, context, nextLoad) {
|
|
336
|
+
const result = await nextLoad(url, context);
|
|
337
|
+
|
|
338
|
+
// Only transform ESM modules we should observe
|
|
339
|
+
if (!shouldObserve(url)) return result;
|
|
340
|
+
const isModule = result.format === 'module';
|
|
341
|
+
const isTypeScript = result.format === 'module-typescript';
|
|
342
|
+
if (!isModule && !isTypeScript) return result;
|
|
343
|
+
|
|
344
|
+
const source = result.source;
|
|
345
|
+
if (!source) return result;
|
|
346
|
+
|
|
347
|
+
const sourceStr = typeof source === 'string'
|
|
348
|
+
? source
|
|
349
|
+
: Buffer.from(source).toString('utf-8');
|
|
350
|
+
|
|
351
|
+
// Only transform if the module has exports
|
|
352
|
+
if (!sourceStr.includes('export ')) return result;
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
const transformed = transformSource(sourceStr, url);
|
|
356
|
+
return {
|
|
357
|
+
...result,
|
|
358
|
+
source: transformed,
|
|
359
|
+
shortCircuit: true,
|
|
360
|
+
};
|
|
361
|
+
} catch (err) {
|
|
362
|
+
if (config.debug) {
|
|
363
|
+
console.error(`[trickle/esm] Failed to transform ${url}:`, err.message);
|
|
364
|
+
}
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
}
|
package/observe-esm.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESM auto-observation registration script.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* node --import trickle/observe-esm app.mjs
|
|
6
|
+
*
|
|
7
|
+
* Registers ESM loader hooks that wrap exported functions from user modules
|
|
8
|
+
* with trickle observation — capturing types and sample data for every call.
|
|
9
|
+
*/
|
|
10
|
+
import { register } from 'node:module';
|
|
11
|
+
import { pathToFileURL } from 'node:url';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const hooksPath = join(__dirname, 'observe-esm-hooks.mjs');
|
|
17
|
+
|
|
18
|
+
const backendUrl = process.env.TRICKLE_BACKEND_URL || 'http://localhost:4888';
|
|
19
|
+
const debug = process.env.TRICKLE_DEBUG === '1' || process.env.TRICKLE_DEBUG === 'true';
|
|
20
|
+
|
|
21
|
+
if (debug) {
|
|
22
|
+
console.log(`[trickle/esm] Registering ESM observation hooks (backend: ${backendUrl})`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
register(pathToFileURL(hooksPath).href, {
|
|
26
|
+
parentURL: import.meta.url,
|
|
27
|
+
data: {
|
|
28
|
+
wrapperPath: join(__dirname, 'dist', 'wrap.js'),
|
|
29
|
+
transportPath: join(__dirname, 'dist', 'transport.js'),
|
|
30
|
+
envDetectPath: join(__dirname, 'dist', 'env-detect.js'),
|
|
31
|
+
backendUrl,
|
|
32
|
+
debug,
|
|
33
|
+
includePatterns: process.env.TRICKLE_OBSERVE_INCLUDE
|
|
34
|
+
? process.env.TRICKLE_OBSERVE_INCLUDE.split(',').map(s => s.trim())
|
|
35
|
+
: [],
|
|
36
|
+
excludePatterns: process.env.TRICKLE_OBSERVE_EXCLUDE
|
|
37
|
+
? process.env.TRICKLE_OBSERVE_EXCLUDE.split(',').map(s => s.trim())
|
|
38
|
+
: [],
|
|
39
|
+
},
|
|
40
|
+
});
|
package/observe.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "trickle-observe",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Runtime type observability for JavaScript applications",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./register": "./register.js",
|
|
10
|
+
"./observe": "./observe.js",
|
|
11
|
+
"./observe-esm": "./observe-esm.mjs",
|
|
12
|
+
"./auto": "./auto.js",
|
|
13
|
+
"./auto-env": "./auto-env.js",
|
|
14
|
+
"./auto-esm": "./auto-esm.mjs"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.4.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": ["observability", "types", "runtime", "lambda", "express"],
|
|
25
|
+
"license": "MIT"
|
|
26
|
+
}
|
package/register.js
ADDED