@rasputin-ai/node 0.5.0-alpha.6 → 0.5.0-alpha.8
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 +42 -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 +767 -71
- package/dist/instrument/bun.js +748 -66
- package/dist/instrument/node.js +748 -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,655 @@ 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
|
+
if (nested.captured) {
|
|
435
|
+
const wrap = ctx.captureWraps.find(
|
|
436
|
+
(candidate) => candidate.expressionId === nested.expressionId
|
|
437
|
+
);
|
|
438
|
+
if (wrap) wrap.shorthandPropertyName = property.name.text;
|
|
439
|
+
}
|
|
440
|
+
properties.push({ key: property.name.text, value: nested.expressionId });
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (!ts.isPropertyAssignment(property)) {
|
|
444
|
+
return capture(ctx, node, "unsupported_expression");
|
|
445
|
+
}
|
|
446
|
+
if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
|
|
447
|
+
return capture(ctx, node, "unsupported_expression");
|
|
448
|
+
}
|
|
449
|
+
const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
|
|
450
|
+
if (key === void 0) return capture(ctx, node, "unsupported_expression");
|
|
451
|
+
properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
|
|
452
|
+
}
|
|
453
|
+
return { expressionId: ctx.ir.object(properties), shape: "object" };
|
|
454
|
+
}
|
|
455
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
456
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
457
|
+
const object = analyzeExpression(ctx, node.expression);
|
|
458
|
+
if (object.shape === "object" && ts.isIdentifier(node.name)) {
|
|
459
|
+
return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
|
|
460
|
+
}
|
|
461
|
+
return capture(ctx, node, "unknown_property");
|
|
462
|
+
}
|
|
463
|
+
if (ts.isElementAccessExpression(node)) {
|
|
464
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
465
|
+
const array = analyzeExpression(ctx, node.expression);
|
|
466
|
+
if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
|
|
467
|
+
return {
|
|
468
|
+
expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
|
|
469
|
+
shape: "unknown"
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
return capture(ctx, node, "unknown_property");
|
|
473
|
+
}
|
|
474
|
+
if (ts.isCallExpression(node)) {
|
|
475
|
+
if (node.questionDotToken || node.typeArguments?.length) {
|
|
476
|
+
return capture(ctx, node, "unsupported_expression");
|
|
477
|
+
}
|
|
478
|
+
if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
|
|
479
|
+
const calleeName = node.expression.text;
|
|
480
|
+
if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
|
|
481
|
+
const provenId = ctx.provenExact.get(calleeName);
|
|
482
|
+
ctx.callEdges.push({
|
|
483
|
+
callerFunctionId: ctx.functionId,
|
|
484
|
+
calleeName,
|
|
485
|
+
...provenId ? { calleeFunctionId: provenId } : {}
|
|
486
|
+
});
|
|
487
|
+
if (!provenId) return capture(ctx, node, "opaque_call");
|
|
488
|
+
const args = node.arguments.map((argument) => {
|
|
489
|
+
if (ts.isSpreadElement(argument)) return void 0;
|
|
490
|
+
return analyzeExpression(ctx, argument).expressionId;
|
|
491
|
+
});
|
|
492
|
+
if (args.some((argument) => argument === void 0))
|
|
493
|
+
return capture(ctx, node, "unsupported_expression");
|
|
494
|
+
return {
|
|
495
|
+
expressionId: ctx.ir.pureCall(
|
|
496
|
+
provenId,
|
|
497
|
+
args.filter((id) => id !== void 0)
|
|
498
|
+
),
|
|
499
|
+
shape: "unknown"
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
|
|
503
|
+
return capture(
|
|
504
|
+
ctx,
|
|
505
|
+
node,
|
|
506
|
+
node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
if (ts.isConditionalExpression(node)) {
|
|
510
|
+
const test = analyzeExpression(ctx, node.condition);
|
|
511
|
+
const consequent = analyzeExpression(ctx, node.whenTrue);
|
|
512
|
+
const alternate = analyzeExpression(ctx, node.whenFalse);
|
|
513
|
+
return {
|
|
514
|
+
expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
|
|
515
|
+
shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
if (ts.isBinaryExpression(node)) {
|
|
519
|
+
if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
520
|
+
ctx.fallback ??= "mutable_state";
|
|
521
|
+
return capture(ctx, node, "mutable_object");
|
|
522
|
+
}
|
|
523
|
+
if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
524
|
+
const left2 = analyzeExpression(ctx, node.left);
|
|
525
|
+
const right2 = analyzeExpression(ctx, node.right);
|
|
526
|
+
const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
|
|
527
|
+
return {
|
|
528
|
+
expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
|
|
529
|
+
shape: "unknown"
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
const op = binaryOp(node.operatorToken.kind);
|
|
533
|
+
if (!op) return capture(ctx, node, "unsupported_expression");
|
|
534
|
+
if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
|
|
535
|
+
const left = analyzeExpression(ctx, node.left);
|
|
536
|
+
const right = analyzeExpression(ctx, node.right);
|
|
537
|
+
const numeric = isNumericBinaryOp(op);
|
|
538
|
+
const expressionId = ctx.ir.binary(
|
|
539
|
+
op,
|
|
540
|
+
left.expressionId,
|
|
541
|
+
right.expressionId,
|
|
542
|
+
numeric ? "numeric" : "primitive"
|
|
543
|
+
);
|
|
544
|
+
if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
|
|
545
|
+
ctx.hasCoercionGuard = true;
|
|
546
|
+
const fallbackRootId = ctx.nextRootId.value++;
|
|
547
|
+
ctx.guardWraps.push({
|
|
548
|
+
fallbackRootId,
|
|
549
|
+
start: node.getStart(ctx.sourceFile),
|
|
550
|
+
end: node.end,
|
|
551
|
+
op
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
return { expressionId, shape: "primitive" };
|
|
555
|
+
}
|
|
556
|
+
if (ts.isPrefixUnaryExpression(node)) {
|
|
557
|
+
const operand = analyzeExpression(ctx, node.operand);
|
|
558
|
+
if (node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
559
|
+
return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
|
|
560
|
+
}
|
|
561
|
+
if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
|
|
562
|
+
if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
|
|
563
|
+
return {
|
|
564
|
+
expressionId: ctx.ir.unary(
|
|
565
|
+
node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
|
|
566
|
+
operand.expressionId
|
|
567
|
+
),
|
|
568
|
+
shape: "primitive"
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
return capture(ctx, node, "unsupported_expression");
|
|
572
|
+
}
|
|
573
|
+
if (ts.isTypeOfExpression(node)) {
|
|
574
|
+
const operand = unwrapParens(node.expression);
|
|
575
|
+
if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
|
|
576
|
+
return capture(ctx, node, "external_state");
|
|
577
|
+
}
|
|
578
|
+
const inner = analyzeExpression(ctx, node.expression);
|
|
579
|
+
return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
|
|
580
|
+
}
|
|
581
|
+
return capture(ctx, node, "unsupported_expression");
|
|
582
|
+
};
|
|
583
|
+
var bindIdentifier = (ctx, name, result, declaration) => {
|
|
584
|
+
const current = ctx.scopes[ctx.scopes.length - 1];
|
|
585
|
+
current?.set(name.text, {
|
|
586
|
+
expressionId: result.expressionId,
|
|
587
|
+
shape: result.shape,
|
|
588
|
+
mutable: false
|
|
589
|
+
});
|
|
590
|
+
const position = loc(name, ctx.sourceFile);
|
|
591
|
+
const available = loc(declaration, ctx.sourceFile);
|
|
592
|
+
ctx.variables.push({
|
|
593
|
+
id: ctx.nextVariableId.value++,
|
|
594
|
+
name: name.text,
|
|
595
|
+
functionId: ctx.functionId,
|
|
596
|
+
scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
|
|
597
|
+
line: position.line,
|
|
598
|
+
column: position.column,
|
|
599
|
+
assignmentId: ctx.nextAssignmentId.value++,
|
|
600
|
+
expressionId: result.expressionId,
|
|
601
|
+
availableAtLine: available.line,
|
|
602
|
+
availableAtColumn: available.column
|
|
603
|
+
});
|
|
604
|
+
};
|
|
605
|
+
var analyzeStatement = (ctx, node) => {
|
|
606
|
+
if (ctx.fallback) return;
|
|
607
|
+
if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
|
|
608
|
+
if (ts.isBlock(node)) {
|
|
609
|
+
ctx.scopes.push(/* @__PURE__ */ new Map());
|
|
610
|
+
for (const statement of node.statements) analyzeStatement(ctx, statement);
|
|
611
|
+
ctx.scopes.pop();
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (ts.isReturnStatement(node)) {
|
|
615
|
+
if (!node.expression) {
|
|
616
|
+
ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (ts.isVariableStatement(node)) {
|
|
623
|
+
if (node.declarationList.flags & ts.NodeFlags.Using) {
|
|
624
|
+
fallback(ctx, "side_effect");
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
for (const declaration of node.declarationList.declarations) {
|
|
628
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
629
|
+
fallback(ctx, "unsupported_expression");
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
const result = analyzeExpression(ctx, declaration.initializer);
|
|
633
|
+
bindIdentifier(ctx, declaration.name, result, declaration);
|
|
634
|
+
}
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (ts.isExpressionStatement(node)) {
|
|
638
|
+
analyzeExpression(ctx, node.expression);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (ts.isThrowStatement(node)) {
|
|
642
|
+
ctx.hasThrow = true;
|
|
643
|
+
analyzeExpression(ctx, node.expression);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
|
|
647
|
+
fallback(ctx, "unsupported_expression");
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
|
|
651
|
+
fallback(ctx, "has_loop");
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
fallback(ctx, "unsupported_expression");
|
|
655
|
+
};
|
|
656
|
+
var bindParams = (ctx, node) => {
|
|
657
|
+
let index = 0;
|
|
658
|
+
for (const parameter of node.parameters) {
|
|
659
|
+
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
660
|
+
if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
|
|
661
|
+
fallback(ctx, "unsupported_expression");
|
|
662
|
+
index++;
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
const expressionId = ctx.ir.param(index);
|
|
666
|
+
ctx.identifierParams.push(parameter.name.text);
|
|
667
|
+
bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
|
|
668
|
+
index++;
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
var analyzeOne = (node, functionId, name, sourceFile, provenExact) => {
|
|
672
|
+
const ctx = {
|
|
673
|
+
sourceFile,
|
|
674
|
+
functionId,
|
|
675
|
+
name,
|
|
676
|
+
ir: new DerivationIrBuilder(),
|
|
677
|
+
variables: [],
|
|
678
|
+
captureWraps: [],
|
|
679
|
+
guardWraps: [],
|
|
680
|
+
callEdges: [],
|
|
681
|
+
scopes: [/* @__PURE__ */ new Map()],
|
|
682
|
+
nextRootId: { value: 0 },
|
|
683
|
+
nextVariableId: { value: 0 },
|
|
684
|
+
nextAssignmentId: { value: 0 },
|
|
685
|
+
hasThrow: false,
|
|
686
|
+
hasCoercionGuard: false,
|
|
687
|
+
provenExact,
|
|
688
|
+
identifierParams: []
|
|
689
|
+
};
|
|
690
|
+
if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
691
|
+
ctx.fallback = "async";
|
|
692
|
+
} else {
|
|
693
|
+
bindParams(ctx, node);
|
|
694
|
+
if (ts.isBlock(node.body)) {
|
|
695
|
+
for (const statement of node.body.statements) analyzeStatement(ctx, statement);
|
|
696
|
+
} else {
|
|
697
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
const plan = {
|
|
701
|
+
...emptyPlan(functionId),
|
|
702
|
+
expressions: ctx.ir.expressions,
|
|
703
|
+
variables: ctx.variables,
|
|
704
|
+
captureBoundaries: ctx.captureWraps.map((wrap) => ({
|
|
705
|
+
id: wrap.rootId,
|
|
706
|
+
reason: wrap.reason,
|
|
707
|
+
expressionId: wrap.expressionId,
|
|
708
|
+
line: wrap.line,
|
|
709
|
+
column: wrap.column
|
|
710
|
+
})),
|
|
711
|
+
controlFacts: [],
|
|
712
|
+
...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
|
|
713
|
+
...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
|
|
714
|
+
};
|
|
715
|
+
return {
|
|
716
|
+
functionId,
|
|
717
|
+
name,
|
|
718
|
+
node,
|
|
719
|
+
plan,
|
|
720
|
+
captureWraps: ctx.fallback ? [] : ctx.captureWraps,
|
|
721
|
+
guardWraps: ctx.fallback ? [] : ctx.guardWraps,
|
|
722
|
+
callEdges: ctx.callEdges,
|
|
723
|
+
hasThrow: ctx.hasThrow,
|
|
724
|
+
hasCoercionGuard: ctx.hasCoercionGuard
|
|
725
|
+
};
|
|
726
|
+
};
|
|
727
|
+
var analyzeModuleFunctions = (sourceFile, targets) => {
|
|
728
|
+
const nameCounts = /* @__PURE__ */ new Map();
|
|
729
|
+
for (const target of targets) {
|
|
730
|
+
nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
|
|
731
|
+
}
|
|
732
|
+
const firstPass = targets.map(
|
|
733
|
+
(target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
|
|
734
|
+
);
|
|
735
|
+
const graph = buildDependencyGraph(
|
|
736
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
737
|
+
firstPass.flatMap((fn) => fn.callEdges)
|
|
738
|
+
);
|
|
739
|
+
const order = topologicalFunctionOrder(
|
|
740
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
741
|
+
graph
|
|
742
|
+
);
|
|
743
|
+
const byId = new Map(targets.map((target) => [target.functionId, target]));
|
|
744
|
+
const provenExact = /* @__PURE__ */ new Map();
|
|
745
|
+
const analyzed = [];
|
|
746
|
+
for (const functionId of order) {
|
|
747
|
+
const target = byId.get(functionId);
|
|
748
|
+
if (!target) continue;
|
|
749
|
+
const result = analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact);
|
|
750
|
+
if (functionIsExact(result.plan) && nameCounts.get(result.name) === 1) {
|
|
751
|
+
provenExact.set(result.name, result.functionId);
|
|
752
|
+
}
|
|
753
|
+
analyzed.push(result);
|
|
754
|
+
}
|
|
755
|
+
const remaining = targets.filter(
|
|
756
|
+
(target) => !analyzed.some((fn) => fn.functionId === target.functionId)
|
|
757
|
+
);
|
|
758
|
+
for (const target of remaining) {
|
|
759
|
+
analyzed.push(analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact));
|
|
760
|
+
}
|
|
761
|
+
return analyzed;
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
// src/derivation/emit-capture-plan.ts
|
|
765
|
+
var captureWrapEdits = (wraps, capName) => {
|
|
766
|
+
const edits = [];
|
|
767
|
+
let order = 0;
|
|
768
|
+
for (const wrap of wraps) {
|
|
769
|
+
edits.push({
|
|
770
|
+
position: wrap.start,
|
|
771
|
+
text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
|
|
772
|
+
order: order++
|
|
773
|
+
});
|
|
774
|
+
edits.push({
|
|
775
|
+
position: wrap.end,
|
|
776
|
+
text: ")",
|
|
777
|
+
order: order++
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
return edits;
|
|
781
|
+
};
|
|
147
782
|
|
|
148
783
|
// src/execution-recorder/automatic-runtime.ts
|
|
149
784
|
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
|
|
150
785
|
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
151
786
|
|
|
152
787
|
// src/auto-instrumentation/collect-instrumentation-delta.ts
|
|
153
|
-
import
|
|
788
|
+
import ts2 from "typescript";
|
|
154
789
|
var paramBindingFromName = (name, sourceFile) => {
|
|
155
|
-
if (
|
|
156
|
-
if (
|
|
790
|
+
if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
|
|
791
|
+
if (ts2.isArrayBindingPattern(name)) {
|
|
157
792
|
const elements = name.elements.map(
|
|
158
793
|
(element) => {
|
|
159
|
-
if (
|
|
794
|
+
if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
|
|
160
795
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
161
796
|
if (element.dotDotDotToken) return { kind: "rest", binding };
|
|
162
797
|
return binding;
|
|
@@ -166,7 +801,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
166
801
|
}
|
|
167
802
|
const properties = name.elements.map((element) => {
|
|
168
803
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
169
|
-
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") :
|
|
804
|
+
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
|
|
170
805
|
if (element.dotDotDotToken) {
|
|
171
806
|
const rest = { kind: "rest", binding };
|
|
172
807
|
return { key, binding: rest };
|
|
@@ -178,7 +813,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
178
813
|
var collectFunctionParams = (node, sourceFile) => {
|
|
179
814
|
const params = [];
|
|
180
815
|
for (const parameter of node.parameters) {
|
|
181
|
-
if (
|
|
816
|
+
if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
182
817
|
const binding = paramBindingFromName(parameter.name, sourceFile);
|
|
183
818
|
params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
|
|
184
819
|
}
|
|
@@ -219,18 +854,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
|
|
|
219
854
|
};
|
|
220
855
|
var unwrapExpression = (expression) => {
|
|
221
856
|
let current = expression;
|
|
222
|
-
while (
|
|
857
|
+
while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
|
|
223
858
|
current = current.expression;
|
|
224
859
|
}
|
|
225
|
-
if (
|
|
860
|
+
if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
|
|
226
861
|
return current;
|
|
227
862
|
};
|
|
228
863
|
var calleeFromExpression = (expression, sourceFile) => {
|
|
229
864
|
const current = unwrapExpression(expression);
|
|
230
|
-
if (
|
|
865
|
+
if (ts2.isIdentifier(current)) {
|
|
231
866
|
return { calleeName: current.text, calleeText: current.text };
|
|
232
867
|
}
|
|
233
|
-
if (
|
|
868
|
+
if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
|
|
234
869
|
return {
|
|
235
870
|
calleeName: current.name.text,
|
|
236
871
|
calleeText: current.getText(sourceFile).replace(/\s+/g, "")
|
|
@@ -240,9 +875,9 @@ var calleeFromExpression = (expression, sourceFile) => {
|
|
|
240
875
|
};
|
|
241
876
|
var resultBindingForCall = (call) => {
|
|
242
877
|
let node = call.parent;
|
|
243
|
-
if (
|
|
244
|
-
if (!
|
|
245
|
-
if (!
|
|
878
|
+
if (ts2.isAwaitExpression(node)) node = node.parent;
|
|
879
|
+
if (!ts2.isVariableDeclaration(node)) return null;
|
|
880
|
+
if (!ts2.isIdentifier(node.name)) return null;
|
|
246
881
|
const initializer = node.initializer;
|
|
247
882
|
if (!initializer) return null;
|
|
248
883
|
const inner = unwrapExpression(initializer);
|
|
@@ -252,18 +887,18 @@ var resultBindingForCall = (call) => {
|
|
|
252
887
|
var argIdentifiersForCall = (call) => {
|
|
253
888
|
const identifiers = [];
|
|
254
889
|
call.arguments?.forEach((argument, index) => {
|
|
255
|
-
if (
|
|
890
|
+
if (ts2.isIdentifier(argument)) {
|
|
256
891
|
identifiers.push({ index, kind: "identifier", name: argument.text });
|
|
257
892
|
return;
|
|
258
893
|
}
|
|
259
|
-
if (!
|
|
894
|
+
if (!ts2.isObjectLiteralExpression(argument)) return;
|
|
260
895
|
const properties = [];
|
|
261
896
|
for (const property of argument.properties) {
|
|
262
|
-
if (
|
|
897
|
+
if (ts2.isShorthandPropertyAssignment(property)) {
|
|
263
898
|
properties.push({ key: property.name.text, name: property.name.text });
|
|
264
899
|
continue;
|
|
265
900
|
}
|
|
266
|
-
if (
|
|
901
|
+
if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
|
|
267
902
|
properties.push({ key: property.name.text, name: property.initializer.text });
|
|
268
903
|
}
|
|
269
904
|
}
|
|
@@ -303,8 +938,8 @@ var collectCallSite = (call, caller, context) => {
|
|
|
303
938
|
};
|
|
304
939
|
};
|
|
305
940
|
var collectParamDestructure = (declaration, caller, context) => {
|
|
306
|
-
if (!declaration.initializer || !
|
|
307
|
-
if (!
|
|
941
|
+
if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
|
|
942
|
+
if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
|
|
308
943
|
return null;
|
|
309
944
|
}
|
|
310
945
|
const sourceParam = declaration.initializer.text;
|
|
@@ -328,33 +963,33 @@ var collectParamDestructure = (declaration, caller, context) => {
|
|
|
328
963
|
|
|
329
964
|
// src/auto-instrumentation/transform-source.ts
|
|
330
965
|
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
|
|
966
|
+
if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
|
|
967
|
+
if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
|
|
968
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
|
|
969
|
+
return ts3.ScriptKind.JS;
|
|
335
970
|
};
|
|
336
971
|
var functionName = (node, sourceFile) => {
|
|
337
972
|
if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
|
|
338
973
|
const parent = node.parent;
|
|
339
|
-
if (
|
|
340
|
-
if (
|
|
341
|
-
if (
|
|
974
|
+
if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
|
|
975
|
+
if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
|
|
976
|
+
if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
|
|
342
977
|
const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
343
978
|
return `anonymous@${location.line + 1}:${location.character + 1}`;
|
|
344
979
|
};
|
|
345
980
|
var functionLocationPosition = (node, sourceFile) => {
|
|
346
981
|
if (node.name) return node.name.getStart(sourceFile);
|
|
347
982
|
const parent = node.parent;
|
|
348
|
-
if ((
|
|
983
|
+
if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
|
|
349
984
|
return parent.name.getStart(sourceFile);
|
|
350
985
|
}
|
|
351
986
|
return node.getStart(sourceFile);
|
|
352
987
|
};
|
|
353
988
|
var bindingValue = (name, sourceFile) => {
|
|
354
|
-
if (
|
|
355
|
-
if (
|
|
989
|
+
if (ts3.isIdentifier(name)) return name.text;
|
|
990
|
+
if (ts3.isArrayBindingPattern(name)) {
|
|
356
991
|
const values = name.elements.map((element) => {
|
|
357
|
-
if (
|
|
992
|
+
if (ts3.isOmittedExpression(element)) return "";
|
|
358
993
|
const value = bindingValue(element.name, sourceFile);
|
|
359
994
|
return element.dotDotDotToken ? `...${value}` : value;
|
|
360
995
|
});
|
|
@@ -370,14 +1005,14 @@ var bindingValue = (name, sourceFile) => {
|
|
|
370
1005
|
};
|
|
371
1006
|
var arrowArguments = (node) => {
|
|
372
1007
|
const values = node.parameters.flatMap((parameter) => {
|
|
373
|
-
if (
|
|
1008
|
+
if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
|
|
374
1009
|
const value = bindingValue(parameter.name, node.getSourceFile());
|
|
375
1010
|
return [parameter.dotDotDotToken ? `...${value}` : value];
|
|
376
1011
|
});
|
|
377
1012
|
return `[${values.join(",")}]`;
|
|
378
1013
|
};
|
|
379
1014
|
var isSupportedFunction = (node) => {
|
|
380
|
-
if (!
|
|
1015
|
+
if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
|
|
381
1016
|
return false;
|
|
382
1017
|
}
|
|
383
1018
|
if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
|
|
@@ -388,7 +1023,7 @@ var uniqueName = (source, base) => {
|
|
|
388
1023
|
while (source.includes(name)) name += "_";
|
|
389
1024
|
return name;
|
|
390
1025
|
};
|
|
391
|
-
var isDirectivePrologue = (statement) =>
|
|
1026
|
+
var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
|
|
392
1027
|
var insertPositionAfterPrologue = (source, sourceFile) => {
|
|
393
1028
|
const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
|
|
394
1029
|
let end = shebangEnd;
|
|
@@ -403,7 +1038,7 @@ var preambleInsert = (source, position, preamble) => {
|
|
|
403
1038
|
const previous = source[position - 1];
|
|
404
1039
|
return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
|
|
405
1040
|
};
|
|
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)}));`;
|
|
1041
|
+
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
1042
|
var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
408
1043
|
const start = mappedLocation(
|
|
409
1044
|
sourceFile,
|
|
@@ -434,80 +1069,127 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
|
434
1069
|
paramNames
|
|
435
1070
|
};
|
|
436
1071
|
};
|
|
1072
|
+
var canOmitCallValues = (analysis, analyzed) => {
|
|
1073
|
+
if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
|
|
1074
|
+
return false;
|
|
1075
|
+
}
|
|
1076
|
+
const callers = analyzed.filter(
|
|
1077
|
+
(caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
|
|
1078
|
+
);
|
|
1079
|
+
if (callers.length === 0) return false;
|
|
1080
|
+
return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
|
|
1081
|
+
};
|
|
437
1082
|
var transformSource = (source, options) => {
|
|
438
1083
|
if (source.includes("@rasputin-ignore-file")) return void 0;
|
|
439
|
-
const sourceFile =
|
|
1084
|
+
const sourceFile = ts3.createSourceFile(
|
|
440
1085
|
options.filePath,
|
|
441
1086
|
source,
|
|
442
|
-
|
|
1087
|
+
ts3.ScriptTarget.Latest,
|
|
443
1088
|
true,
|
|
444
1089
|
scriptKindFor(options.filePath)
|
|
445
1090
|
);
|
|
446
1091
|
const runName = uniqueName(source, "__rasputinRun__");
|
|
1092
|
+
const capName = uniqueName(source, "__rasputinCap__");
|
|
447
1093
|
const plansName = uniqueName(source, "__rasputinPlans__");
|
|
448
1094
|
const absolutePath = resolve4(options.filePath);
|
|
1095
|
+
const derivationMode = options.derivationMode ?? "analysis";
|
|
449
1096
|
const edits = [];
|
|
450
1097
|
let order = 0;
|
|
451
1098
|
const delta = {
|
|
452
1099
|
functions: [],
|
|
453
1100
|
callSites: [],
|
|
454
|
-
destructures: []
|
|
1101
|
+
destructures: [],
|
|
1102
|
+
derivationPlans: []
|
|
455
1103
|
};
|
|
456
1104
|
const collectContext = {
|
|
457
1105
|
sourceFile,
|
|
458
1106
|
mapLocation: options.mapLocation,
|
|
459
1107
|
absolutePath
|
|
460
1108
|
};
|
|
1109
|
+
const pending = [];
|
|
461
1110
|
const visit = (node, caller) => {
|
|
462
1111
|
if (isSupportedFunction(node)) {
|
|
463
1112
|
const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
|
|
464
1113
|
if (!collected) {
|
|
465
|
-
|
|
1114
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
466
1115
|
return;
|
|
467
1116
|
}
|
|
468
|
-
|
|
1117
|
+
pending.push({ node, collected });
|
|
469
1118
|
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));
|
|
1119
|
+
ts3.forEachChild(node, (child) => visit(child, collected));
|
|
490
1120
|
return;
|
|
491
1121
|
}
|
|
492
|
-
if (caller && (
|
|
1122
|
+
if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
|
|
493
1123
|
const site = collectCallSite(node, caller, collectContext);
|
|
494
1124
|
if (site) delta.callSites.push(site);
|
|
495
1125
|
}
|
|
496
|
-
if (caller &&
|
|
1126
|
+
if (caller && ts3.isVariableDeclaration(node)) {
|
|
497
1127
|
const destructure = collectParamDestructure(node, caller, collectContext);
|
|
498
1128
|
if (destructure) delta.destructures.push(destructure);
|
|
499
1129
|
}
|
|
500
|
-
|
|
1130
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
501
1131
|
};
|
|
502
1132
|
visit(sourceFile);
|
|
503
|
-
if (
|
|
1133
|
+
if (pending.length === 0) return void 0;
|
|
1134
|
+
const analyzed = analyzeModuleFunctions(
|
|
1135
|
+
sourceFile,
|
|
1136
|
+
pending.map((entry) => ({
|
|
1137
|
+
functionId: entry.collected.functionId,
|
|
1138
|
+
name: entry.collected.name,
|
|
1139
|
+
node: entry.node
|
|
1140
|
+
}))
|
|
1141
|
+
);
|
|
1142
|
+
delta.derivationPlans = analyzed.map((fn) => fn.plan);
|
|
1143
|
+
const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
|
|
1144
|
+
for (const [planIndex, entry] of pending.entries()) {
|
|
1145
|
+
const analysis = analyzedById.get(entry.collected.functionId);
|
|
1146
|
+
const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
|
|
1147
|
+
const valueFlag = omitValues ? ",1" : "";
|
|
1148
|
+
const args = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
|
|
1149
|
+
const asyncCallback = entry.node.modifiers?.some(
|
|
1150
|
+
(modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
|
|
1151
|
+
) ? "async " : "";
|
|
1152
|
+
if (ts3.isBlock(entry.node.body)) {
|
|
1153
|
+
edits.push({
|
|
1154
|
+
position: entry.node.body.getStart(sourceFile) + 1,
|
|
1155
|
+
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
1156
|
+
order: order++
|
|
1157
|
+
});
|
|
1158
|
+
edits.push({
|
|
1159
|
+
position: entry.node.body.end - 1,
|
|
1160
|
+
text: `}${valueFlag});`,
|
|
1161
|
+
order: order++
|
|
1162
|
+
});
|
|
1163
|
+
} else {
|
|
1164
|
+
edits.push({
|
|
1165
|
+
position: entry.node.body.getStart(sourceFile),
|
|
1166
|
+
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
1167
|
+
order: order++
|
|
1168
|
+
});
|
|
1169
|
+
edits.push({
|
|
1170
|
+
position: entry.node.body.end,
|
|
1171
|
+
text: `)${valueFlag})`,
|
|
1172
|
+
order: order++
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
if (analysis) {
|
|
1176
|
+
for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
|
|
1177
|
+
edits.push({ ...edit, order: order++ });
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
504
1181
|
const insertAt = insertPositionAfterPrologue(source, sourceFile);
|
|
505
1182
|
edits.push({
|
|
506
1183
|
position: insertAt,
|
|
507
1184
|
text: preambleInsert(
|
|
508
1185
|
source,
|
|
509
1186
|
insertAt,
|
|
510
|
-
modulePreamble(
|
|
1187
|
+
modulePreamble(
|
|
1188
|
+
runName,
|
|
1189
|
+
capName,
|
|
1190
|
+
plansName,
|
|
1191
|
+
JSON.stringify(delta.functions.map((fn) => fn.plan))
|
|
1192
|
+
)
|
|
511
1193
|
),
|
|
512
1194
|
order: -1
|
|
513
1195
|
});
|