@rasputin-ai/node 0.5.0-alpha.6 → 0.5.0-alpha.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/auto-instrumentation/instrument-compiled-esm.d.ts +2 -0
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +2 -1
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
- package/dist/auto-instrumentation/transform-source.d.ts +6 -0
- package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
- package/dist/derivation/analyze-function.d.ts +40 -0
- package/dist/derivation/analyze-function.d.ts.map +1 -0
- package/dist/derivation/build-dependency-graph.d.ts +21 -0
- package/dist/derivation/build-dependency-graph.d.ts.map +1 -0
- package/dist/derivation/derivation-ir.d.ts +33 -0
- package/dist/derivation/derivation-ir.d.ts.map +1 -0
- package/dist/derivation/derivation-proof.d.ts +14 -0
- package/dist/derivation/derivation-proof.d.ts.map +1 -0
- package/dist/derivation/emit-capture-plan.d.ts +8 -0
- package/dist/derivation/emit-capture-plan.d.ts.map +1 -0
- package/dist/execution-recorder/automatic-runtime.d.ts +3 -1
- package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder-types.d.ts +13 -2
- package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
- package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
- package/dist/index.js +169 -36
- package/dist/instrument/build.js +761 -71
- package/dist/instrument/bun.js +742 -66
- package/dist/instrument/node.js +742 -66
- package/dist/sdk-meta.d.ts +1 -1
- package/package.json +2 -2
package/dist/instrument/node.js
CHANGED
|
@@ -31,7 +31,7 @@ var encodeFunctionPlan = (source) => {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
// src/auto-instrumentation/instrumentation-manifest-registry.ts
|
|
34
|
-
var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.
|
|
34
|
+
var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v3";
|
|
35
35
|
var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
|
|
36
36
|
var getRegistry = () => {
|
|
37
37
|
const existing = Reflect.get(globalThis, registrySymbol);
|
|
@@ -143,20 +143,649 @@ var createSourceMapLocationMapper = (generatedPath, source) => {
|
|
|
143
143
|
|
|
144
144
|
// src/auto-instrumentation/transform-source.ts
|
|
145
145
|
import { resolve as resolve4 } from "node:path";
|
|
146
|
-
import
|
|
146
|
+
import ts3 from "typescript";
|
|
147
|
+
|
|
148
|
+
// src/derivation/analyze-function.ts
|
|
149
|
+
import ts from "typescript";
|
|
150
|
+
|
|
151
|
+
// src/derivation/build-dependency-graph.ts
|
|
152
|
+
var buildDependencyGraph = (functions, edges) => {
|
|
153
|
+
const names = new Set(functions.map((fn) => fn.name));
|
|
154
|
+
const localEdges = edges.filter((edge) => names.has(edge.calleeName));
|
|
155
|
+
const calleesByCaller = /* @__PURE__ */ new Map();
|
|
156
|
+
for (const edge of localEdges) {
|
|
157
|
+
const existing = calleesByCaller.get(edge.callerFunctionId) ?? [];
|
|
158
|
+
existing.push(edge.calleeName);
|
|
159
|
+
calleesByCaller.set(edge.callerFunctionId, existing);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
functions: functions.map((fn) => fn.functionId),
|
|
163
|
+
edges: localEdges,
|
|
164
|
+
calleesByCaller
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
var topologicalFunctionOrder = (functions, graph) => {
|
|
168
|
+
const idByName = new Map(functions.map((fn) => [fn.name, fn.functionId]));
|
|
169
|
+
const remainingCallees = new Map(
|
|
170
|
+
functions.map((fn) => {
|
|
171
|
+
const unique = new Set(
|
|
172
|
+
(graph.calleesByCaller.get(fn.functionId) ?? []).map((name) => idByName.get(name)).filter((id) => Boolean(id) && id !== fn.functionId)
|
|
173
|
+
);
|
|
174
|
+
return [fn.functionId, unique.size];
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
const callersOf = /* @__PURE__ */ new Map();
|
|
178
|
+
for (const fn of functions) {
|
|
179
|
+
for (const calleeName of graph.calleesByCaller.get(fn.functionId) ?? []) {
|
|
180
|
+
const calleeId = idByName.get(calleeName);
|
|
181
|
+
if (!calleeId || calleeId === fn.functionId) continue;
|
|
182
|
+
const callers = callersOf.get(calleeId) ?? [];
|
|
183
|
+
callers.push(fn.functionId);
|
|
184
|
+
callersOf.set(calleeId, callers);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const queue = functions.filter((fn) => (remainingCallees.get(fn.functionId) ?? 0) === 0).map((fn) => fn.functionId);
|
|
188
|
+
const ordered = [];
|
|
189
|
+
const queued = new Set(queue);
|
|
190
|
+
while (queue.length > 0) {
|
|
191
|
+
const current = queue.shift();
|
|
192
|
+
if (current === void 0) break;
|
|
193
|
+
ordered.push(current);
|
|
194
|
+
for (const caller of callersOf.get(current) ?? []) {
|
|
195
|
+
const next = (remainingCallees.get(caller) ?? 0) - 1;
|
|
196
|
+
remainingCallees.set(caller, next);
|
|
197
|
+
if (next === 0 && !queued.has(caller)) {
|
|
198
|
+
queued.add(caller);
|
|
199
|
+
queue.push(caller);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (const fn of functions) {
|
|
204
|
+
if (!queued.has(fn.functionId)) ordered.push(fn.functionId);
|
|
205
|
+
}
|
|
206
|
+
return ordered;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// src/derivation/derivation-ir.ts
|
|
210
|
+
var DerivationIrBuilder = class {
|
|
211
|
+
expressions = [];
|
|
212
|
+
nextId = 0;
|
|
213
|
+
constValue(value) {
|
|
214
|
+
return this.add({ id: this.nextId++, kind: "const", value });
|
|
215
|
+
}
|
|
216
|
+
param(index) {
|
|
217
|
+
return this.add({ id: this.nextId++, kind: "param", index });
|
|
218
|
+
}
|
|
219
|
+
root(rootId) {
|
|
220
|
+
return this.add({ id: this.nextId++, kind: "root", rootId });
|
|
221
|
+
}
|
|
222
|
+
alias(from) {
|
|
223
|
+
return this.add({ id: this.nextId++, kind: "alias", from });
|
|
224
|
+
}
|
|
225
|
+
binary(op, left, right, guard) {
|
|
226
|
+
return this.add({ id: this.nextId++, kind: "binary", op, left, right, guard });
|
|
227
|
+
}
|
|
228
|
+
unary(op, operand) {
|
|
229
|
+
return this.add({ id: this.nextId++, kind: "unary", op, operand });
|
|
230
|
+
}
|
|
231
|
+
logical(op, left, right) {
|
|
232
|
+
return this.add({ id: this.nextId++, kind: "logical", op, left, right });
|
|
233
|
+
}
|
|
234
|
+
cond(test, consequent, alternate) {
|
|
235
|
+
return this.add({ id: this.nextId++, kind: "cond", test, consequent, alternate });
|
|
236
|
+
}
|
|
237
|
+
object(properties) {
|
|
238
|
+
return this.add({ id: this.nextId++, kind: "object", properties });
|
|
239
|
+
}
|
|
240
|
+
array(elements) {
|
|
241
|
+
return this.add({ id: this.nextId++, kind: "array", elements });
|
|
242
|
+
}
|
|
243
|
+
prop(object, key) {
|
|
244
|
+
return this.add({ id: this.nextId++, kind: "prop", object, key });
|
|
245
|
+
}
|
|
246
|
+
index(array, index) {
|
|
247
|
+
return this.add({ id: this.nextId++, kind: "index", array, index });
|
|
248
|
+
}
|
|
249
|
+
pureCall(calleeFunctionId, args) {
|
|
250
|
+
return this.add({ id: this.nextId++, kind: "pure_call", calleeFunctionId, args });
|
|
251
|
+
}
|
|
252
|
+
template(parts) {
|
|
253
|
+
return this.add({ id: this.nextId++, kind: "template", parts });
|
|
254
|
+
}
|
|
255
|
+
add(expression) {
|
|
256
|
+
this.expressions.push(expression);
|
|
257
|
+
return expression.id;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
var emptyPlan = (functionId) => ({
|
|
261
|
+
planId: `dp_${functionId}`,
|
|
262
|
+
functionId,
|
|
263
|
+
expressions: [],
|
|
264
|
+
variables: [],
|
|
265
|
+
captureBoundaries: [],
|
|
266
|
+
controlFacts: []
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// src/derivation/derivation-proof.ts
|
|
270
|
+
var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
|
|
271
|
+
|
|
272
|
+
// src/derivation/analyze-function.ts
|
|
273
|
+
var loc = (node, sourceFile) => {
|
|
274
|
+
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
275
|
+
return { line: position.line + 1, column: position.character + 1 };
|
|
276
|
+
};
|
|
277
|
+
var lookup = (ctx, name) => {
|
|
278
|
+
for (let index = ctx.scopes.length - 1; index >= 0; index--) {
|
|
279
|
+
const binding = ctx.scopes[index]?.get(name);
|
|
280
|
+
if (binding) return binding;
|
|
281
|
+
}
|
|
282
|
+
return void 0;
|
|
283
|
+
};
|
|
284
|
+
var fallback = (ctx, reason) => {
|
|
285
|
+
ctx.fallback ??= reason;
|
|
286
|
+
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
|
|
287
|
+
};
|
|
288
|
+
var capture = (ctx, node, reason) => {
|
|
289
|
+
const rootId = ctx.nextRootId.value++;
|
|
290
|
+
const position = loc(node, ctx.sourceFile);
|
|
291
|
+
const expressionId = ctx.ir.root(rootId);
|
|
292
|
+
ctx.captureWraps.push({
|
|
293
|
+
rootId,
|
|
294
|
+
reason,
|
|
295
|
+
start: node.getStart(ctx.sourceFile),
|
|
296
|
+
end: node.end,
|
|
297
|
+
line: position.line,
|
|
298
|
+
column: position.column,
|
|
299
|
+
expressionId
|
|
300
|
+
});
|
|
301
|
+
return { expressionId, shape: "unknown", captured: true };
|
|
302
|
+
};
|
|
303
|
+
var binaryOp = (kind) => {
|
|
304
|
+
switch (kind) {
|
|
305
|
+
case ts.SyntaxKind.PlusToken:
|
|
306
|
+
return "add";
|
|
307
|
+
case ts.SyntaxKind.MinusToken:
|
|
308
|
+
return "sub";
|
|
309
|
+
case ts.SyntaxKind.AsteriskToken:
|
|
310
|
+
return "mul";
|
|
311
|
+
case ts.SyntaxKind.SlashToken:
|
|
312
|
+
return "div";
|
|
313
|
+
case ts.SyntaxKind.PercentToken:
|
|
314
|
+
return "mod";
|
|
315
|
+
case ts.SyntaxKind.EqualsEqualsToken:
|
|
316
|
+
return "eq";
|
|
317
|
+
case ts.SyntaxKind.ExclamationEqualsToken:
|
|
318
|
+
return "neq";
|
|
319
|
+
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
|
320
|
+
return "seq";
|
|
321
|
+
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
322
|
+
return "sneq";
|
|
323
|
+
case ts.SyntaxKind.LessThanToken:
|
|
324
|
+
return "lt";
|
|
325
|
+
case ts.SyntaxKind.LessThanEqualsToken:
|
|
326
|
+
return "lte";
|
|
327
|
+
case ts.SyntaxKind.GreaterThanToken:
|
|
328
|
+
return "gt";
|
|
329
|
+
case ts.SyntaxKind.GreaterThanEqualsToken:
|
|
330
|
+
return "gte";
|
|
331
|
+
default:
|
|
332
|
+
return void 0;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
|
|
336
|
+
var unwrapParens = (node) => {
|
|
337
|
+
while (ts.isParenthesizedExpression(node)) node = node.expression;
|
|
338
|
+
return node;
|
|
339
|
+
};
|
|
340
|
+
var numericLiteralValue = (node) => {
|
|
341
|
+
const value = Number(node.text);
|
|
342
|
+
return { type: "number", value };
|
|
343
|
+
};
|
|
344
|
+
var analyzeExpression = (ctx, node) => {
|
|
345
|
+
if (ctx.fallback) return fallback(ctx, ctx.fallback);
|
|
346
|
+
if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
347
|
+
if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
|
|
348
|
+
return analyzeExpression(ctx, node.expression);
|
|
349
|
+
}
|
|
350
|
+
if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
|
|
351
|
+
return analyzeExpression(ctx, node.expression);
|
|
352
|
+
}
|
|
353
|
+
if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
354
|
+
if (node.kind === ts.SyntaxKind.NullKeyword) {
|
|
355
|
+
return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
|
|
356
|
+
}
|
|
357
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
358
|
+
return {
|
|
359
|
+
expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
|
|
360
|
+
shape: "primitive"
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
364
|
+
return {
|
|
365
|
+
expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
|
|
366
|
+
shape: "primitive"
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
if (ts.isNumericLiteral(node)) {
|
|
370
|
+
return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
|
|
371
|
+
}
|
|
372
|
+
if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
|
|
373
|
+
const value = -Number(node.operand.text);
|
|
374
|
+
return {
|
|
375
|
+
expressionId: ctx.ir.constValue(
|
|
376
|
+
Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
|
|
377
|
+
),
|
|
378
|
+
shape: "primitive"
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
382
|
+
return {
|
|
383
|
+
expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
|
|
384
|
+
shape: "primitive"
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
if (ts.isBigIntLiteral(node)) {
|
|
388
|
+
return {
|
|
389
|
+
expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
|
|
390
|
+
shape: "primitive"
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
if (ts.isIdentifier(node)) {
|
|
394
|
+
if (node.text === "undefined") {
|
|
395
|
+
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
|
|
396
|
+
}
|
|
397
|
+
const binding = lookup(ctx, node.text);
|
|
398
|
+
if (!binding) return capture(ctx, node, "external_state");
|
|
399
|
+
if (binding.mutable) return capture(ctx, node, "mutable_object");
|
|
400
|
+
return {
|
|
401
|
+
expressionId: ctx.ir.alias(binding.expressionId),
|
|
402
|
+
shape: binding.shape
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
if (ts.isTemplateExpression(node)) {
|
|
406
|
+
const parts = [
|
|
407
|
+
{ kind: "lit", value: node.head.text }
|
|
408
|
+
];
|
|
409
|
+
for (const span of node.templateSpans) {
|
|
410
|
+
const nested = analyzeExpression(ctx, span.expression);
|
|
411
|
+
if (nested.shape !== "primitive" && !nested.captured) {
|
|
412
|
+
return capture(ctx, node, "unsupported_expression");
|
|
413
|
+
}
|
|
414
|
+
parts.push({ kind: "expr", expressionId: nested.expressionId });
|
|
415
|
+
parts.push({ kind: "lit", value: span.literal.text });
|
|
416
|
+
}
|
|
417
|
+
return { expressionId: ctx.ir.template(parts), shape: "primitive" };
|
|
418
|
+
}
|
|
419
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
420
|
+
if (node.elements.some((element) => ts.isSpreadElement(element))) {
|
|
421
|
+
return capture(ctx, node, "unsupported_expression");
|
|
422
|
+
}
|
|
423
|
+
const elements = node.elements.map((element) => {
|
|
424
|
+
if (ts.isOmittedExpression(element)) return { kind: "hole" };
|
|
425
|
+
return analyzeExpression(ctx, element).expressionId;
|
|
426
|
+
});
|
|
427
|
+
return { expressionId: ctx.ir.array(elements), shape: "array" };
|
|
428
|
+
}
|
|
429
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
430
|
+
const properties = [];
|
|
431
|
+
for (const property of node.properties) {
|
|
432
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
433
|
+
const nested = analyzeExpression(ctx, property.name);
|
|
434
|
+
properties.push({ key: property.name.text, value: nested.expressionId });
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (!ts.isPropertyAssignment(property)) {
|
|
438
|
+
return capture(ctx, node, "unsupported_expression");
|
|
439
|
+
}
|
|
440
|
+
if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
|
|
441
|
+
return capture(ctx, node, "unsupported_expression");
|
|
442
|
+
}
|
|
443
|
+
const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
|
|
444
|
+
if (key === void 0) return capture(ctx, node, "unsupported_expression");
|
|
445
|
+
properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
|
|
446
|
+
}
|
|
447
|
+
return { expressionId: ctx.ir.object(properties), shape: "object" };
|
|
448
|
+
}
|
|
449
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
450
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
451
|
+
const object = analyzeExpression(ctx, node.expression);
|
|
452
|
+
if (object.shape === "object" && ts.isIdentifier(node.name)) {
|
|
453
|
+
return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
|
|
454
|
+
}
|
|
455
|
+
return capture(ctx, node, "unknown_property");
|
|
456
|
+
}
|
|
457
|
+
if (ts.isElementAccessExpression(node)) {
|
|
458
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
459
|
+
const array = analyzeExpression(ctx, node.expression);
|
|
460
|
+
if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
|
|
461
|
+
return {
|
|
462
|
+
expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
|
|
463
|
+
shape: "unknown"
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
return capture(ctx, node, "unknown_property");
|
|
467
|
+
}
|
|
468
|
+
if (ts.isCallExpression(node)) {
|
|
469
|
+
if (node.questionDotToken || node.typeArguments?.length) {
|
|
470
|
+
return capture(ctx, node, "unsupported_expression");
|
|
471
|
+
}
|
|
472
|
+
if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
|
|
473
|
+
const calleeName = node.expression.text;
|
|
474
|
+
if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
|
|
475
|
+
const provenId = ctx.provenExact.get(calleeName);
|
|
476
|
+
ctx.callEdges.push({
|
|
477
|
+
callerFunctionId: ctx.functionId,
|
|
478
|
+
calleeName,
|
|
479
|
+
...provenId ? { calleeFunctionId: provenId } : {}
|
|
480
|
+
});
|
|
481
|
+
if (!provenId) return capture(ctx, node, "opaque_call");
|
|
482
|
+
const args = node.arguments.map((argument) => {
|
|
483
|
+
if (ts.isSpreadElement(argument)) return void 0;
|
|
484
|
+
return analyzeExpression(ctx, argument).expressionId;
|
|
485
|
+
});
|
|
486
|
+
if (args.some((argument) => argument === void 0))
|
|
487
|
+
return capture(ctx, node, "unsupported_expression");
|
|
488
|
+
return {
|
|
489
|
+
expressionId: ctx.ir.pureCall(
|
|
490
|
+
provenId,
|
|
491
|
+
args.filter((id) => id !== void 0)
|
|
492
|
+
),
|
|
493
|
+
shape: "unknown"
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
|
|
497
|
+
return capture(
|
|
498
|
+
ctx,
|
|
499
|
+
node,
|
|
500
|
+
node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
if (ts.isConditionalExpression(node)) {
|
|
504
|
+
const test = analyzeExpression(ctx, node.condition);
|
|
505
|
+
const consequent = analyzeExpression(ctx, node.whenTrue);
|
|
506
|
+
const alternate = analyzeExpression(ctx, node.whenFalse);
|
|
507
|
+
return {
|
|
508
|
+
expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
|
|
509
|
+
shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
if (ts.isBinaryExpression(node)) {
|
|
513
|
+
if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
514
|
+
ctx.fallback ??= "mutable_state";
|
|
515
|
+
return capture(ctx, node, "mutable_object");
|
|
516
|
+
}
|
|
517
|
+
if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
518
|
+
const left2 = analyzeExpression(ctx, node.left);
|
|
519
|
+
const right2 = analyzeExpression(ctx, node.right);
|
|
520
|
+
const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
|
|
521
|
+
return {
|
|
522
|
+
expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
|
|
523
|
+
shape: "unknown"
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
const op = binaryOp(node.operatorToken.kind);
|
|
527
|
+
if (!op) return capture(ctx, node, "unsupported_expression");
|
|
528
|
+
if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
|
|
529
|
+
const left = analyzeExpression(ctx, node.left);
|
|
530
|
+
const right = analyzeExpression(ctx, node.right);
|
|
531
|
+
const numeric = isNumericBinaryOp(op);
|
|
532
|
+
const expressionId = ctx.ir.binary(
|
|
533
|
+
op,
|
|
534
|
+
left.expressionId,
|
|
535
|
+
right.expressionId,
|
|
536
|
+
numeric ? "numeric" : "primitive"
|
|
537
|
+
);
|
|
538
|
+
if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
|
|
539
|
+
ctx.hasCoercionGuard = true;
|
|
540
|
+
const fallbackRootId = ctx.nextRootId.value++;
|
|
541
|
+
ctx.guardWraps.push({
|
|
542
|
+
fallbackRootId,
|
|
543
|
+
start: node.getStart(ctx.sourceFile),
|
|
544
|
+
end: node.end,
|
|
545
|
+
op
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
return { expressionId, shape: "primitive" };
|
|
549
|
+
}
|
|
550
|
+
if (ts.isPrefixUnaryExpression(node)) {
|
|
551
|
+
const operand = analyzeExpression(ctx, node.operand);
|
|
552
|
+
if (node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
553
|
+
return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
|
|
554
|
+
}
|
|
555
|
+
if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
|
|
556
|
+
if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
|
|
557
|
+
return {
|
|
558
|
+
expressionId: ctx.ir.unary(
|
|
559
|
+
node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
|
|
560
|
+
operand.expressionId
|
|
561
|
+
),
|
|
562
|
+
shape: "primitive"
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
return capture(ctx, node, "unsupported_expression");
|
|
566
|
+
}
|
|
567
|
+
if (ts.isTypeOfExpression(node)) {
|
|
568
|
+
const operand = unwrapParens(node.expression);
|
|
569
|
+
if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
|
|
570
|
+
return capture(ctx, node, "external_state");
|
|
571
|
+
}
|
|
572
|
+
const inner = analyzeExpression(ctx, node.expression);
|
|
573
|
+
return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
|
|
574
|
+
}
|
|
575
|
+
return capture(ctx, node, "unsupported_expression");
|
|
576
|
+
};
|
|
577
|
+
var bindIdentifier = (ctx, name, result, declaration) => {
|
|
578
|
+
const current = ctx.scopes[ctx.scopes.length - 1];
|
|
579
|
+
current?.set(name.text, {
|
|
580
|
+
expressionId: result.expressionId,
|
|
581
|
+
shape: result.shape,
|
|
582
|
+
mutable: false
|
|
583
|
+
});
|
|
584
|
+
const position = loc(name, ctx.sourceFile);
|
|
585
|
+
const available = loc(declaration, ctx.sourceFile);
|
|
586
|
+
ctx.variables.push({
|
|
587
|
+
id: ctx.nextVariableId.value++,
|
|
588
|
+
name: name.text,
|
|
589
|
+
functionId: ctx.functionId,
|
|
590
|
+
scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
|
|
591
|
+
line: position.line,
|
|
592
|
+
column: position.column,
|
|
593
|
+
assignmentId: ctx.nextAssignmentId.value++,
|
|
594
|
+
expressionId: result.expressionId,
|
|
595
|
+
availableAtLine: available.line,
|
|
596
|
+
availableAtColumn: available.column
|
|
597
|
+
});
|
|
598
|
+
};
|
|
599
|
+
var analyzeStatement = (ctx, node) => {
|
|
600
|
+
if (ctx.fallback) return;
|
|
601
|
+
if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
|
|
602
|
+
if (ts.isBlock(node)) {
|
|
603
|
+
ctx.scopes.push(/* @__PURE__ */ new Map());
|
|
604
|
+
for (const statement of node.statements) analyzeStatement(ctx, statement);
|
|
605
|
+
ctx.scopes.pop();
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
if (ts.isReturnStatement(node)) {
|
|
609
|
+
if (!node.expression) {
|
|
610
|
+
ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
if (ts.isVariableStatement(node)) {
|
|
617
|
+
if (node.declarationList.flags & ts.NodeFlags.Using) {
|
|
618
|
+
fallback(ctx, "side_effect");
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
for (const declaration of node.declarationList.declarations) {
|
|
622
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
623
|
+
fallback(ctx, "unsupported_expression");
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const result = analyzeExpression(ctx, declaration.initializer);
|
|
627
|
+
bindIdentifier(ctx, declaration.name, result, declaration);
|
|
628
|
+
}
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (ts.isExpressionStatement(node)) {
|
|
632
|
+
analyzeExpression(ctx, node.expression);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
if (ts.isThrowStatement(node)) {
|
|
636
|
+
ctx.hasThrow = true;
|
|
637
|
+
analyzeExpression(ctx, node.expression);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
|
|
641
|
+
fallback(ctx, "unsupported_expression");
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
|
|
645
|
+
fallback(ctx, "has_loop");
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
fallback(ctx, "unsupported_expression");
|
|
649
|
+
};
|
|
650
|
+
var bindParams = (ctx, node) => {
|
|
651
|
+
let index = 0;
|
|
652
|
+
for (const parameter of node.parameters) {
|
|
653
|
+
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
654
|
+
if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
|
|
655
|
+
fallback(ctx, "unsupported_expression");
|
|
656
|
+
index++;
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
const expressionId = ctx.ir.param(index);
|
|
660
|
+
ctx.identifierParams.push(parameter.name.text);
|
|
661
|
+
bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
|
|
662
|
+
index++;
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
var analyzeOne = (node, functionId, name, sourceFile, provenExact) => {
|
|
666
|
+
const ctx = {
|
|
667
|
+
sourceFile,
|
|
668
|
+
functionId,
|
|
669
|
+
name,
|
|
670
|
+
ir: new DerivationIrBuilder(),
|
|
671
|
+
variables: [],
|
|
672
|
+
captureWraps: [],
|
|
673
|
+
guardWraps: [],
|
|
674
|
+
callEdges: [],
|
|
675
|
+
scopes: [/* @__PURE__ */ new Map()],
|
|
676
|
+
nextRootId: { value: 0 },
|
|
677
|
+
nextVariableId: { value: 0 },
|
|
678
|
+
nextAssignmentId: { value: 0 },
|
|
679
|
+
hasThrow: false,
|
|
680
|
+
hasCoercionGuard: false,
|
|
681
|
+
provenExact,
|
|
682
|
+
identifierParams: []
|
|
683
|
+
};
|
|
684
|
+
if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
685
|
+
ctx.fallback = "async";
|
|
686
|
+
} else {
|
|
687
|
+
bindParams(ctx, node);
|
|
688
|
+
if (ts.isBlock(node.body)) {
|
|
689
|
+
for (const statement of node.body.statements) analyzeStatement(ctx, statement);
|
|
690
|
+
} else {
|
|
691
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
const plan = {
|
|
695
|
+
...emptyPlan(functionId),
|
|
696
|
+
expressions: ctx.ir.expressions,
|
|
697
|
+
variables: ctx.variables,
|
|
698
|
+
captureBoundaries: ctx.captureWraps.map((wrap) => ({
|
|
699
|
+
id: wrap.rootId,
|
|
700
|
+
reason: wrap.reason,
|
|
701
|
+
expressionId: wrap.expressionId,
|
|
702
|
+
line: wrap.line,
|
|
703
|
+
column: wrap.column
|
|
704
|
+
})),
|
|
705
|
+
controlFacts: [],
|
|
706
|
+
...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
|
|
707
|
+
...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
|
|
708
|
+
};
|
|
709
|
+
return {
|
|
710
|
+
functionId,
|
|
711
|
+
name,
|
|
712
|
+
node,
|
|
713
|
+
plan,
|
|
714
|
+
captureWraps: ctx.fallback ? [] : ctx.captureWraps,
|
|
715
|
+
guardWraps: ctx.fallback ? [] : ctx.guardWraps,
|
|
716
|
+
callEdges: ctx.callEdges,
|
|
717
|
+
hasThrow: ctx.hasThrow,
|
|
718
|
+
hasCoercionGuard: ctx.hasCoercionGuard
|
|
719
|
+
};
|
|
720
|
+
};
|
|
721
|
+
var analyzeModuleFunctions = (sourceFile, targets) => {
|
|
722
|
+
const nameCounts = /* @__PURE__ */ new Map();
|
|
723
|
+
for (const target of targets) {
|
|
724
|
+
nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
|
|
725
|
+
}
|
|
726
|
+
const firstPass = targets.map(
|
|
727
|
+
(target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
|
|
728
|
+
);
|
|
729
|
+
const graph = buildDependencyGraph(
|
|
730
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
731
|
+
firstPass.flatMap((fn) => fn.callEdges)
|
|
732
|
+
);
|
|
733
|
+
const order = topologicalFunctionOrder(
|
|
734
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
735
|
+
graph
|
|
736
|
+
);
|
|
737
|
+
const byId = new Map(targets.map((target) => [target.functionId, target]));
|
|
738
|
+
const provenExact = /* @__PURE__ */ new Map();
|
|
739
|
+
const analyzed = [];
|
|
740
|
+
for (const functionId of order) {
|
|
741
|
+
const target = byId.get(functionId);
|
|
742
|
+
if (!target) continue;
|
|
743
|
+
const result = analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact);
|
|
744
|
+
if (functionIsExact(result.plan) && nameCounts.get(result.name) === 1) {
|
|
745
|
+
provenExact.set(result.name, result.functionId);
|
|
746
|
+
}
|
|
747
|
+
analyzed.push(result);
|
|
748
|
+
}
|
|
749
|
+
const remaining = targets.filter(
|
|
750
|
+
(target) => !analyzed.some((fn) => fn.functionId === target.functionId)
|
|
751
|
+
);
|
|
752
|
+
for (const target of remaining) {
|
|
753
|
+
analyzed.push(analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact));
|
|
754
|
+
}
|
|
755
|
+
return analyzed;
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
// src/derivation/emit-capture-plan.ts
|
|
759
|
+
var captureWrapEdits = (wraps, capName) => {
|
|
760
|
+
const edits = [];
|
|
761
|
+
let order = 0;
|
|
762
|
+
for (const wrap of wraps) {
|
|
763
|
+
edits.push({
|
|
764
|
+
position: wrap.start,
|
|
765
|
+
text: `${capName}(${wrap.rootId},`,
|
|
766
|
+
order: order++
|
|
767
|
+
});
|
|
768
|
+
edits.push({
|
|
769
|
+
position: wrap.end,
|
|
770
|
+
text: ")",
|
|
771
|
+
order: order++
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
return edits;
|
|
775
|
+
};
|
|
147
776
|
|
|
148
777
|
// src/execution-recorder/automatic-runtime.ts
|
|
149
778
|
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
|
|
150
779
|
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
151
780
|
|
|
152
781
|
// src/auto-instrumentation/collect-instrumentation-delta.ts
|
|
153
|
-
import
|
|
782
|
+
import ts2 from "typescript";
|
|
154
783
|
var paramBindingFromName = (name, sourceFile) => {
|
|
155
|
-
if (
|
|
156
|
-
if (
|
|
784
|
+
if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
|
|
785
|
+
if (ts2.isArrayBindingPattern(name)) {
|
|
157
786
|
const elements = name.elements.map(
|
|
158
787
|
(element) => {
|
|
159
|
-
if (
|
|
788
|
+
if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
|
|
160
789
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
161
790
|
if (element.dotDotDotToken) return { kind: "rest", binding };
|
|
162
791
|
return binding;
|
|
@@ -166,7 +795,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
166
795
|
}
|
|
167
796
|
const properties = name.elements.map((element) => {
|
|
168
797
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
169
|
-
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") :
|
|
798
|
+
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
|
|
170
799
|
if (element.dotDotDotToken) {
|
|
171
800
|
const rest = { kind: "rest", binding };
|
|
172
801
|
return { key, binding: rest };
|
|
@@ -178,7 +807,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
178
807
|
var collectFunctionParams = (node, sourceFile) => {
|
|
179
808
|
const params = [];
|
|
180
809
|
for (const parameter of node.parameters) {
|
|
181
|
-
if (
|
|
810
|
+
if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
182
811
|
const binding = paramBindingFromName(parameter.name, sourceFile);
|
|
183
812
|
params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
|
|
184
813
|
}
|
|
@@ -219,18 +848,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
|
|
|
219
848
|
};
|
|
220
849
|
var unwrapExpression = (expression) => {
|
|
221
850
|
let current = expression;
|
|
222
|
-
while (
|
|
851
|
+
while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
|
|
223
852
|
current = current.expression;
|
|
224
853
|
}
|
|
225
|
-
if (
|
|
854
|
+
if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
|
|
226
855
|
return current;
|
|
227
856
|
};
|
|
228
857
|
var calleeFromExpression = (expression, sourceFile) => {
|
|
229
858
|
const current = unwrapExpression(expression);
|
|
230
|
-
if (
|
|
859
|
+
if (ts2.isIdentifier(current)) {
|
|
231
860
|
return { calleeName: current.text, calleeText: current.text };
|
|
232
861
|
}
|
|
233
|
-
if (
|
|
862
|
+
if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
|
|
234
863
|
return {
|
|
235
864
|
calleeName: current.name.text,
|
|
236
865
|
calleeText: current.getText(sourceFile).replace(/\s+/g, "")
|
|
@@ -240,9 +869,9 @@ var calleeFromExpression = (expression, sourceFile) => {
|
|
|
240
869
|
};
|
|
241
870
|
var resultBindingForCall = (call) => {
|
|
242
871
|
let node = call.parent;
|
|
243
|
-
if (
|
|
244
|
-
if (!
|
|
245
|
-
if (!
|
|
872
|
+
if (ts2.isAwaitExpression(node)) node = node.parent;
|
|
873
|
+
if (!ts2.isVariableDeclaration(node)) return null;
|
|
874
|
+
if (!ts2.isIdentifier(node.name)) return null;
|
|
246
875
|
const initializer = node.initializer;
|
|
247
876
|
if (!initializer) return null;
|
|
248
877
|
const inner = unwrapExpression(initializer);
|
|
@@ -252,18 +881,18 @@ var resultBindingForCall = (call) => {
|
|
|
252
881
|
var argIdentifiersForCall = (call) => {
|
|
253
882
|
const identifiers = [];
|
|
254
883
|
call.arguments?.forEach((argument, index) => {
|
|
255
|
-
if (
|
|
884
|
+
if (ts2.isIdentifier(argument)) {
|
|
256
885
|
identifiers.push({ index, kind: "identifier", name: argument.text });
|
|
257
886
|
return;
|
|
258
887
|
}
|
|
259
|
-
if (!
|
|
888
|
+
if (!ts2.isObjectLiteralExpression(argument)) return;
|
|
260
889
|
const properties = [];
|
|
261
890
|
for (const property of argument.properties) {
|
|
262
|
-
if (
|
|
891
|
+
if (ts2.isShorthandPropertyAssignment(property)) {
|
|
263
892
|
properties.push({ key: property.name.text, name: property.name.text });
|
|
264
893
|
continue;
|
|
265
894
|
}
|
|
266
|
-
if (
|
|
895
|
+
if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
|
|
267
896
|
properties.push({ key: property.name.text, name: property.initializer.text });
|
|
268
897
|
}
|
|
269
898
|
}
|
|
@@ -303,8 +932,8 @@ var collectCallSite = (call, caller, context) => {
|
|
|
303
932
|
};
|
|
304
933
|
};
|
|
305
934
|
var collectParamDestructure = (declaration, caller, context) => {
|
|
306
|
-
if (!declaration.initializer || !
|
|
307
|
-
if (!
|
|
935
|
+
if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
|
|
936
|
+
if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
|
|
308
937
|
return null;
|
|
309
938
|
}
|
|
310
939
|
const sourceParam = declaration.initializer.text;
|
|
@@ -328,33 +957,33 @@ var collectParamDestructure = (declaration, caller, context) => {
|
|
|
328
957
|
|
|
329
958
|
// src/auto-instrumentation/transform-source.ts
|
|
330
959
|
var scriptKindFor = (filePath) => {
|
|
331
|
-
if (/\.tsx$/i.test(filePath)) return
|
|
332
|
-
if (/\.jsx$/i.test(filePath)) return
|
|
333
|
-
if (/\.[cm]?ts$/i.test(filePath)) return
|
|
334
|
-
return
|
|
960
|
+
if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
|
|
961
|
+
if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
|
|
962
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
|
|
963
|
+
return ts3.ScriptKind.JS;
|
|
335
964
|
};
|
|
336
965
|
var functionName = (node, sourceFile) => {
|
|
337
966
|
if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
|
|
338
967
|
const parent = node.parent;
|
|
339
|
-
if (
|
|
340
|
-
if (
|
|
341
|
-
if (
|
|
968
|
+
if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
|
|
969
|
+
if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
|
|
970
|
+
if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
|
|
342
971
|
const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
343
972
|
return `anonymous@${location.line + 1}:${location.character + 1}`;
|
|
344
973
|
};
|
|
345
974
|
var functionLocationPosition = (node, sourceFile) => {
|
|
346
975
|
if (node.name) return node.name.getStart(sourceFile);
|
|
347
976
|
const parent = node.parent;
|
|
348
|
-
if ((
|
|
977
|
+
if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
|
|
349
978
|
return parent.name.getStart(sourceFile);
|
|
350
979
|
}
|
|
351
980
|
return node.getStart(sourceFile);
|
|
352
981
|
};
|
|
353
982
|
var bindingValue = (name, sourceFile) => {
|
|
354
|
-
if (
|
|
355
|
-
if (
|
|
983
|
+
if (ts3.isIdentifier(name)) return name.text;
|
|
984
|
+
if (ts3.isArrayBindingPattern(name)) {
|
|
356
985
|
const values = name.elements.map((element) => {
|
|
357
|
-
if (
|
|
986
|
+
if (ts3.isOmittedExpression(element)) return "";
|
|
358
987
|
const value = bindingValue(element.name, sourceFile);
|
|
359
988
|
return element.dotDotDotToken ? `...${value}` : value;
|
|
360
989
|
});
|
|
@@ -370,14 +999,14 @@ var bindingValue = (name, sourceFile) => {
|
|
|
370
999
|
};
|
|
371
1000
|
var arrowArguments = (node) => {
|
|
372
1001
|
const values = node.parameters.flatMap((parameter) => {
|
|
373
|
-
if (
|
|
1002
|
+
if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
|
|
374
1003
|
const value = bindingValue(parameter.name, node.getSourceFile());
|
|
375
1004
|
return [parameter.dotDotDotToken ? `...${value}` : value];
|
|
376
1005
|
});
|
|
377
1006
|
return `[${values.join(",")}]`;
|
|
378
1007
|
};
|
|
379
1008
|
var isSupportedFunction = (node) => {
|
|
380
|
-
if (!
|
|
1009
|
+
if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
|
|
381
1010
|
return false;
|
|
382
1011
|
}
|
|
383
1012
|
if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
|
|
@@ -388,7 +1017,7 @@ var uniqueName = (source, base) => {
|
|
|
388
1017
|
while (source.includes(name)) name += "_";
|
|
389
1018
|
return name;
|
|
390
1019
|
};
|
|
391
|
-
var isDirectivePrologue = (statement) =>
|
|
1020
|
+
var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
|
|
392
1021
|
var insertPositionAfterPrologue = (source, sourceFile) => {
|
|
393
1022
|
const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
|
|
394
1023
|
let end = shebangEnd;
|
|
@@ -403,7 +1032,7 @@ var preambleInsert = (source, position, preamble) => {
|
|
|
403
1032
|
const previous = source[position - 1];
|
|
404
1033
|
return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
|
|
405
1034
|
};
|
|
406
|
-
var modulePreamble = (runName, plansName, plansJson) => `const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],modules:[],register(p){const d={run:(i,a,c)=>c()};const m={plans:p,cell:d};this.modules.push(m);const n=this.runtimes.at(-1);if(n)n.bind(m);return(i,a,c)=>d.run(i,a,c)}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
|
|
1035
|
+
var modulePreamble = (runName, capName, plansName, plansJson) => `const ${plansName}=${plansJson};const ${runName}=((g,k)=>{const r=g[k]??(g[k]={runtimes:[],modules:[],captureRoot(i,v){return v},register(p){const d={run:(i,a,c)=>c()};const m={plans:p,cell:d};this.modules.push(m);const n=this.runtimes.at(-1);if(n)n.bind(m);return(i,a,c,l)=>d.run(i,a,c,l)}});return r.register(${plansName})})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));const ${capName}=((g,k)=>{const r=g[k];return(i,v)=>typeof r?.captureRoot==='function'?r.captureRoot(i,v):v})(globalThis,Symbol.for(${JSON.stringify(AUTOMATIC_RUNTIME_SYMBOL)}));`;
|
|
407
1036
|
var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
408
1037
|
const start = mappedLocation(
|
|
409
1038
|
sourceFile,
|
|
@@ -434,80 +1063,127 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
|
434
1063
|
paramNames
|
|
435
1064
|
};
|
|
436
1065
|
};
|
|
1066
|
+
var canOmitCallValues = (analysis, analyzed) => {
|
|
1067
|
+
if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1070
|
+
const callers = analyzed.filter(
|
|
1071
|
+
(caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
|
|
1072
|
+
);
|
|
1073
|
+
if (callers.length === 0) return false;
|
|
1074
|
+
return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
|
|
1075
|
+
};
|
|
437
1076
|
var transformSource = (source, options) => {
|
|
438
1077
|
if (source.includes("@rasputin-ignore-file")) return void 0;
|
|
439
|
-
const sourceFile =
|
|
1078
|
+
const sourceFile = ts3.createSourceFile(
|
|
440
1079
|
options.filePath,
|
|
441
1080
|
source,
|
|
442
|
-
|
|
1081
|
+
ts3.ScriptTarget.Latest,
|
|
443
1082
|
true,
|
|
444
1083
|
scriptKindFor(options.filePath)
|
|
445
1084
|
);
|
|
446
1085
|
const runName = uniqueName(source, "__rasputinRun__");
|
|
1086
|
+
const capName = uniqueName(source, "__rasputinCap__");
|
|
447
1087
|
const plansName = uniqueName(source, "__rasputinPlans__");
|
|
448
1088
|
const absolutePath = resolve4(options.filePath);
|
|
1089
|
+
const derivationMode = options.derivationMode ?? "analysis";
|
|
449
1090
|
const edits = [];
|
|
450
1091
|
let order = 0;
|
|
451
1092
|
const delta = {
|
|
452
1093
|
functions: [],
|
|
453
1094
|
callSites: [],
|
|
454
|
-
destructures: []
|
|
1095
|
+
destructures: [],
|
|
1096
|
+
derivationPlans: []
|
|
455
1097
|
};
|
|
456
1098
|
const collectContext = {
|
|
457
1099
|
sourceFile,
|
|
458
1100
|
mapLocation: options.mapLocation,
|
|
459
1101
|
absolutePath
|
|
460
1102
|
};
|
|
1103
|
+
const pending = [];
|
|
461
1104
|
const visit = (node, caller) => {
|
|
462
1105
|
if (isSupportedFunction(node)) {
|
|
463
1106
|
const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
|
|
464
1107
|
if (!collected) {
|
|
465
|
-
|
|
1108
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
466
1109
|
return;
|
|
467
1110
|
}
|
|
468
|
-
|
|
1111
|
+
pending.push({ node, collected });
|
|
469
1112
|
delta.functions.push(collected);
|
|
470
|
-
|
|
471
|
-
const asyncCallback = node.modifiers?.some(
|
|
472
|
-
(modifier) => modifier.kind === ts2.SyntaxKind.AsyncKeyword
|
|
473
|
-
) ? "async " : "";
|
|
474
|
-
if (ts2.isBlock(node.body)) {
|
|
475
|
-
edits.push({
|
|
476
|
-
position: node.body.getStart(sourceFile) + 1,
|
|
477
|
-
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
478
|
-
order: order++
|
|
479
|
-
});
|
|
480
|
-
edits.push({ position: node.body.end - 1, text: "});", order: order++ });
|
|
481
|
-
} else {
|
|
482
|
-
edits.push({
|
|
483
|
-
position: node.body.getStart(sourceFile),
|
|
484
|
-
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
485
|
-
order: order++
|
|
486
|
-
});
|
|
487
|
-
edits.push({ position: node.body.end, text: "))", order: order++ });
|
|
488
|
-
}
|
|
489
|
-
ts2.forEachChild(node, (child) => visit(child, collected));
|
|
1113
|
+
ts3.forEachChild(node, (child) => visit(child, collected));
|
|
490
1114
|
return;
|
|
491
1115
|
}
|
|
492
|
-
if (caller && (
|
|
1116
|
+
if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
|
|
493
1117
|
const site = collectCallSite(node, caller, collectContext);
|
|
494
1118
|
if (site) delta.callSites.push(site);
|
|
495
1119
|
}
|
|
496
|
-
if (caller &&
|
|
1120
|
+
if (caller && ts3.isVariableDeclaration(node)) {
|
|
497
1121
|
const destructure = collectParamDestructure(node, caller, collectContext);
|
|
498
1122
|
if (destructure) delta.destructures.push(destructure);
|
|
499
1123
|
}
|
|
500
|
-
|
|
1124
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
501
1125
|
};
|
|
502
1126
|
visit(sourceFile);
|
|
503
|
-
if (
|
|
1127
|
+
if (pending.length === 0) return void 0;
|
|
1128
|
+
const analyzed = analyzeModuleFunctions(
|
|
1129
|
+
sourceFile,
|
|
1130
|
+
pending.map((entry) => ({
|
|
1131
|
+
functionId: entry.collected.functionId,
|
|
1132
|
+
name: entry.collected.name,
|
|
1133
|
+
node: entry.node
|
|
1134
|
+
}))
|
|
1135
|
+
);
|
|
1136
|
+
delta.derivationPlans = analyzed.map((fn) => fn.plan);
|
|
1137
|
+
const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
|
|
1138
|
+
for (const [planIndex, entry] of pending.entries()) {
|
|
1139
|
+
const analysis = analyzedById.get(entry.collected.functionId);
|
|
1140
|
+
const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
|
|
1141
|
+
const valueFlag = omitValues ? ",1" : "";
|
|
1142
|
+
const args = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
|
|
1143
|
+
const asyncCallback = entry.node.modifiers?.some(
|
|
1144
|
+
(modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
|
|
1145
|
+
) ? "async " : "";
|
|
1146
|
+
if (ts3.isBlock(entry.node.body)) {
|
|
1147
|
+
edits.push({
|
|
1148
|
+
position: entry.node.body.getStart(sourceFile) + 1,
|
|
1149
|
+
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
1150
|
+
order: order++
|
|
1151
|
+
});
|
|
1152
|
+
edits.push({
|
|
1153
|
+
position: entry.node.body.end - 1,
|
|
1154
|
+
text: `}${valueFlag});`,
|
|
1155
|
+
order: order++
|
|
1156
|
+
});
|
|
1157
|
+
} else {
|
|
1158
|
+
edits.push({
|
|
1159
|
+
position: entry.node.body.getStart(sourceFile),
|
|
1160
|
+
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
1161
|
+
order: order++
|
|
1162
|
+
});
|
|
1163
|
+
edits.push({
|
|
1164
|
+
position: entry.node.body.end,
|
|
1165
|
+
text: `)${valueFlag})`,
|
|
1166
|
+
order: order++
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
if (analysis) {
|
|
1170
|
+
for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
|
|
1171
|
+
edits.push({ ...edit, order: order++ });
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
504
1175
|
const insertAt = insertPositionAfterPrologue(source, sourceFile);
|
|
505
1176
|
edits.push({
|
|
506
1177
|
position: insertAt,
|
|
507
1178
|
text: preambleInsert(
|
|
508
1179
|
source,
|
|
509
1180
|
insertAt,
|
|
510
|
-
modulePreamble(
|
|
1181
|
+
modulePreamble(
|
|
1182
|
+
runName,
|
|
1183
|
+
capName,
|
|
1184
|
+
plansName,
|
|
1185
|
+
JSON.stringify(delta.functions.map((fn) => fn.plan))
|
|
1186
|
+
)
|
|
511
1187
|
),
|
|
512
1188
|
order: -1
|
|
513
1189
|
});
|