@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/bun.js
CHANGED
|
@@ -34,7 +34,7 @@ var encodeFunctionPlan = (source) => {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
// src/auto-instrumentation/instrumentation-manifest-registry.ts
|
|
37
|
-
var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.
|
|
37
|
+
var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v3";
|
|
38
38
|
var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
|
|
39
39
|
var getRegistry = () => {
|
|
40
40
|
const existing = Reflect.get(globalThis, registrySymbol);
|
|
@@ -88,20 +88,655 @@ var isApplicationSource = (filePath, options = {}) => {
|
|
|
88
88
|
|
|
89
89
|
// src/auto-instrumentation/transform-source.ts
|
|
90
90
|
import { resolve as resolve3 } from "node:path";
|
|
91
|
-
import
|
|
91
|
+
import ts3 from "typescript";
|
|
92
|
+
|
|
93
|
+
// src/derivation/analyze-function.ts
|
|
94
|
+
import ts from "typescript";
|
|
95
|
+
|
|
96
|
+
// src/derivation/build-dependency-graph.ts
|
|
97
|
+
var buildDependencyGraph = (functions, edges) => {
|
|
98
|
+
const names = new Set(functions.map((fn) => fn.name));
|
|
99
|
+
const localEdges = edges.filter((edge) => names.has(edge.calleeName));
|
|
100
|
+
const calleesByCaller = /* @__PURE__ */ new Map();
|
|
101
|
+
for (const edge of localEdges) {
|
|
102
|
+
const existing = calleesByCaller.get(edge.callerFunctionId) ?? [];
|
|
103
|
+
existing.push(edge.calleeName);
|
|
104
|
+
calleesByCaller.set(edge.callerFunctionId, existing);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
functions: functions.map((fn) => fn.functionId),
|
|
108
|
+
edges: localEdges,
|
|
109
|
+
calleesByCaller
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
var topologicalFunctionOrder = (functions, graph) => {
|
|
113
|
+
const idByName = new Map(functions.map((fn) => [fn.name, fn.functionId]));
|
|
114
|
+
const remainingCallees = new Map(
|
|
115
|
+
functions.map((fn) => {
|
|
116
|
+
const unique = new Set(
|
|
117
|
+
(graph.calleesByCaller.get(fn.functionId) ?? []).map((name) => idByName.get(name)).filter((id) => Boolean(id) && id !== fn.functionId)
|
|
118
|
+
);
|
|
119
|
+
return [fn.functionId, unique.size];
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
const callersOf = /* @__PURE__ */ new Map();
|
|
123
|
+
for (const fn of functions) {
|
|
124
|
+
for (const calleeName of graph.calleesByCaller.get(fn.functionId) ?? []) {
|
|
125
|
+
const calleeId = idByName.get(calleeName);
|
|
126
|
+
if (!calleeId || calleeId === fn.functionId) continue;
|
|
127
|
+
const callers = callersOf.get(calleeId) ?? [];
|
|
128
|
+
callers.push(fn.functionId);
|
|
129
|
+
callersOf.set(calleeId, callers);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const queue = functions.filter((fn) => (remainingCallees.get(fn.functionId) ?? 0) === 0).map((fn) => fn.functionId);
|
|
133
|
+
const ordered = [];
|
|
134
|
+
const queued = new Set(queue);
|
|
135
|
+
while (queue.length > 0) {
|
|
136
|
+
const current = queue.shift();
|
|
137
|
+
if (current === void 0) break;
|
|
138
|
+
ordered.push(current);
|
|
139
|
+
for (const caller of callersOf.get(current) ?? []) {
|
|
140
|
+
const next = (remainingCallees.get(caller) ?? 0) - 1;
|
|
141
|
+
remainingCallees.set(caller, next);
|
|
142
|
+
if (next === 0 && !queued.has(caller)) {
|
|
143
|
+
queued.add(caller);
|
|
144
|
+
queue.push(caller);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
for (const fn of functions) {
|
|
149
|
+
if (!queued.has(fn.functionId)) ordered.push(fn.functionId);
|
|
150
|
+
}
|
|
151
|
+
return ordered;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/derivation/derivation-ir.ts
|
|
155
|
+
var DerivationIrBuilder = class {
|
|
156
|
+
expressions = [];
|
|
157
|
+
nextId = 0;
|
|
158
|
+
constValue(value) {
|
|
159
|
+
return this.add({ id: this.nextId++, kind: "const", value });
|
|
160
|
+
}
|
|
161
|
+
param(index) {
|
|
162
|
+
return this.add({ id: this.nextId++, kind: "param", index });
|
|
163
|
+
}
|
|
164
|
+
root(rootId) {
|
|
165
|
+
return this.add({ id: this.nextId++, kind: "root", rootId });
|
|
166
|
+
}
|
|
167
|
+
alias(from) {
|
|
168
|
+
return this.add({ id: this.nextId++, kind: "alias", from });
|
|
169
|
+
}
|
|
170
|
+
binary(op, left, right, guard) {
|
|
171
|
+
return this.add({ id: this.nextId++, kind: "binary", op, left, right, guard });
|
|
172
|
+
}
|
|
173
|
+
unary(op, operand) {
|
|
174
|
+
return this.add({ id: this.nextId++, kind: "unary", op, operand });
|
|
175
|
+
}
|
|
176
|
+
logical(op, left, right) {
|
|
177
|
+
return this.add({ id: this.nextId++, kind: "logical", op, left, right });
|
|
178
|
+
}
|
|
179
|
+
cond(test, consequent, alternate) {
|
|
180
|
+
return this.add({ id: this.nextId++, kind: "cond", test, consequent, alternate });
|
|
181
|
+
}
|
|
182
|
+
object(properties) {
|
|
183
|
+
return this.add({ id: this.nextId++, kind: "object", properties });
|
|
184
|
+
}
|
|
185
|
+
array(elements) {
|
|
186
|
+
return this.add({ id: this.nextId++, kind: "array", elements });
|
|
187
|
+
}
|
|
188
|
+
prop(object, key) {
|
|
189
|
+
return this.add({ id: this.nextId++, kind: "prop", object, key });
|
|
190
|
+
}
|
|
191
|
+
index(array, index) {
|
|
192
|
+
return this.add({ id: this.nextId++, kind: "index", array, index });
|
|
193
|
+
}
|
|
194
|
+
pureCall(calleeFunctionId, args) {
|
|
195
|
+
return this.add({ id: this.nextId++, kind: "pure_call", calleeFunctionId, args });
|
|
196
|
+
}
|
|
197
|
+
template(parts) {
|
|
198
|
+
return this.add({ id: this.nextId++, kind: "template", parts });
|
|
199
|
+
}
|
|
200
|
+
add(expression) {
|
|
201
|
+
this.expressions.push(expression);
|
|
202
|
+
return expression.id;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var emptyPlan = (functionId) => ({
|
|
206
|
+
planId: `dp_${functionId}`,
|
|
207
|
+
functionId,
|
|
208
|
+
expressions: [],
|
|
209
|
+
variables: [],
|
|
210
|
+
captureBoundaries: [],
|
|
211
|
+
controlFacts: []
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// src/derivation/derivation-proof.ts
|
|
215
|
+
var functionIsExact = (plan) => plan.fallbackReason === void 0 && plan.captureBoundaries.length === 0 && plan.controlFacts.length === 0;
|
|
216
|
+
|
|
217
|
+
// src/derivation/analyze-function.ts
|
|
218
|
+
var loc = (node, sourceFile) => {
|
|
219
|
+
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
220
|
+
return { line: position.line + 1, column: position.character + 1 };
|
|
221
|
+
};
|
|
222
|
+
var lookup = (ctx, name) => {
|
|
223
|
+
for (let index = ctx.scopes.length - 1; index >= 0; index--) {
|
|
224
|
+
const binding = ctx.scopes[index]?.get(name);
|
|
225
|
+
if (binding) return binding;
|
|
226
|
+
}
|
|
227
|
+
return void 0;
|
|
228
|
+
};
|
|
229
|
+
var fallback = (ctx, reason) => {
|
|
230
|
+
ctx.fallback ??= reason;
|
|
231
|
+
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "unknown" };
|
|
232
|
+
};
|
|
233
|
+
var capture = (ctx, node, reason) => {
|
|
234
|
+
const rootId = ctx.nextRootId.value++;
|
|
235
|
+
const position = loc(node, ctx.sourceFile);
|
|
236
|
+
const expressionId = ctx.ir.root(rootId);
|
|
237
|
+
ctx.captureWraps.push({
|
|
238
|
+
rootId,
|
|
239
|
+
reason,
|
|
240
|
+
start: node.getStart(ctx.sourceFile),
|
|
241
|
+
end: node.end,
|
|
242
|
+
line: position.line,
|
|
243
|
+
column: position.column,
|
|
244
|
+
expressionId
|
|
245
|
+
});
|
|
246
|
+
return { expressionId, shape: "unknown", captured: true };
|
|
247
|
+
};
|
|
248
|
+
var binaryOp = (kind) => {
|
|
249
|
+
switch (kind) {
|
|
250
|
+
case ts.SyntaxKind.PlusToken:
|
|
251
|
+
return "add";
|
|
252
|
+
case ts.SyntaxKind.MinusToken:
|
|
253
|
+
return "sub";
|
|
254
|
+
case ts.SyntaxKind.AsteriskToken:
|
|
255
|
+
return "mul";
|
|
256
|
+
case ts.SyntaxKind.SlashToken:
|
|
257
|
+
return "div";
|
|
258
|
+
case ts.SyntaxKind.PercentToken:
|
|
259
|
+
return "mod";
|
|
260
|
+
case ts.SyntaxKind.EqualsEqualsToken:
|
|
261
|
+
return "eq";
|
|
262
|
+
case ts.SyntaxKind.ExclamationEqualsToken:
|
|
263
|
+
return "neq";
|
|
264
|
+
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
|
265
|
+
return "seq";
|
|
266
|
+
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
267
|
+
return "sneq";
|
|
268
|
+
case ts.SyntaxKind.LessThanToken:
|
|
269
|
+
return "lt";
|
|
270
|
+
case ts.SyntaxKind.LessThanEqualsToken:
|
|
271
|
+
return "lte";
|
|
272
|
+
case ts.SyntaxKind.GreaterThanToken:
|
|
273
|
+
return "gt";
|
|
274
|
+
case ts.SyntaxKind.GreaterThanEqualsToken:
|
|
275
|
+
return "gte";
|
|
276
|
+
default:
|
|
277
|
+
return void 0;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
var isNumericBinaryOp = (op) => op === "add" || op === "sub" || op === "mul" || op === "div" || op === "mod";
|
|
281
|
+
var unwrapParens = (node) => {
|
|
282
|
+
while (ts.isParenthesizedExpression(node)) node = node.expression;
|
|
283
|
+
return node;
|
|
284
|
+
};
|
|
285
|
+
var numericLiteralValue = (node) => {
|
|
286
|
+
const value = Number(node.text);
|
|
287
|
+
return { type: "number", value };
|
|
288
|
+
};
|
|
289
|
+
var analyzeExpression = (ctx, node) => {
|
|
290
|
+
if (ctx.fallback) return fallback(ctx, ctx.fallback);
|
|
291
|
+
if (ts.isParenthesizedExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
292
|
+
if (ts.isAsExpression(node) || ts.isNonNullExpression(node)) {
|
|
293
|
+
return analyzeExpression(ctx, node.expression);
|
|
294
|
+
}
|
|
295
|
+
if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
|
|
296
|
+
return analyzeExpression(ctx, node.expression);
|
|
297
|
+
}
|
|
298
|
+
if (ts.isSatisfiesExpression(node)) return analyzeExpression(ctx, node.expression);
|
|
299
|
+
if (node.kind === ts.SyntaxKind.NullKeyword) {
|
|
300
|
+
return { expressionId: ctx.ir.constValue({ type: "null" }), shape: "primitive" };
|
|
301
|
+
}
|
|
302
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword) {
|
|
303
|
+
return {
|
|
304
|
+
expressionId: ctx.ir.constValue({ type: "boolean", value: true }),
|
|
305
|
+
shape: "primitive"
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword) {
|
|
309
|
+
return {
|
|
310
|
+
expressionId: ctx.ir.constValue({ type: "boolean", value: false }),
|
|
311
|
+
shape: "primitive"
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (ts.isNumericLiteral(node)) {
|
|
315
|
+
return { expressionId: ctx.ir.constValue(numericLiteralValue(node)), shape: "primitive" };
|
|
316
|
+
}
|
|
317
|
+
if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand) && node.operator === ts.SyntaxKind.MinusToken) {
|
|
318
|
+
const value = -Number(node.operand.text);
|
|
319
|
+
return {
|
|
320
|
+
expressionId: ctx.ir.constValue(
|
|
321
|
+
Object.is(value, -0) ? { type: "number", value: 0, negativeZero: true } : { type: "number", value }
|
|
322
|
+
),
|
|
323
|
+
shape: "primitive"
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
327
|
+
return {
|
|
328
|
+
expressionId: ctx.ir.constValue({ type: "string", value: node.text }),
|
|
329
|
+
shape: "primitive"
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
if (ts.isBigIntLiteral(node)) {
|
|
333
|
+
return {
|
|
334
|
+
expressionId: ctx.ir.constValue({ type: "bigint", value: node.text.replace(/n$/, "") }),
|
|
335
|
+
shape: "primitive"
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
if (ts.isIdentifier(node)) {
|
|
339
|
+
if (node.text === "undefined") {
|
|
340
|
+
return { expressionId: ctx.ir.constValue({ type: "undefined" }), shape: "primitive" };
|
|
341
|
+
}
|
|
342
|
+
const binding = lookup(ctx, node.text);
|
|
343
|
+
if (!binding) return capture(ctx, node, "external_state");
|
|
344
|
+
if (binding.mutable) return capture(ctx, node, "mutable_object");
|
|
345
|
+
return {
|
|
346
|
+
expressionId: ctx.ir.alias(binding.expressionId),
|
|
347
|
+
shape: binding.shape
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
if (ts.isTemplateExpression(node)) {
|
|
351
|
+
const parts = [
|
|
352
|
+
{ kind: "lit", value: node.head.text }
|
|
353
|
+
];
|
|
354
|
+
for (const span of node.templateSpans) {
|
|
355
|
+
const nested = analyzeExpression(ctx, span.expression);
|
|
356
|
+
if (nested.shape !== "primitive" && !nested.captured) {
|
|
357
|
+
return capture(ctx, node, "unsupported_expression");
|
|
358
|
+
}
|
|
359
|
+
parts.push({ kind: "expr", expressionId: nested.expressionId });
|
|
360
|
+
parts.push({ kind: "lit", value: span.literal.text });
|
|
361
|
+
}
|
|
362
|
+
return { expressionId: ctx.ir.template(parts), shape: "primitive" };
|
|
363
|
+
}
|
|
364
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
365
|
+
if (node.elements.some((element) => ts.isSpreadElement(element))) {
|
|
366
|
+
return capture(ctx, node, "unsupported_expression");
|
|
367
|
+
}
|
|
368
|
+
const elements = node.elements.map((element) => {
|
|
369
|
+
if (ts.isOmittedExpression(element)) return { kind: "hole" };
|
|
370
|
+
return analyzeExpression(ctx, element).expressionId;
|
|
371
|
+
});
|
|
372
|
+
return { expressionId: ctx.ir.array(elements), shape: "array" };
|
|
373
|
+
}
|
|
374
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
375
|
+
const properties = [];
|
|
376
|
+
for (const property of node.properties) {
|
|
377
|
+
if (ts.isShorthandPropertyAssignment(property)) {
|
|
378
|
+
const nested = analyzeExpression(ctx, property.name);
|
|
379
|
+
if (nested.captured) {
|
|
380
|
+
const wrap = ctx.captureWraps.find(
|
|
381
|
+
(candidate) => candidate.expressionId === nested.expressionId
|
|
382
|
+
);
|
|
383
|
+
if (wrap) wrap.shorthandPropertyName = property.name.text;
|
|
384
|
+
}
|
|
385
|
+
properties.push({ key: property.name.text, value: nested.expressionId });
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
if (!ts.isPropertyAssignment(property)) {
|
|
389
|
+
return capture(ctx, node, "unsupported_expression");
|
|
390
|
+
}
|
|
391
|
+
if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
|
|
392
|
+
return capture(ctx, node, "unsupported_expression");
|
|
393
|
+
}
|
|
394
|
+
const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
|
|
395
|
+
if (key === void 0) return capture(ctx, node, "unsupported_expression");
|
|
396
|
+
properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
|
|
397
|
+
}
|
|
398
|
+
return { expressionId: ctx.ir.object(properties), shape: "object" };
|
|
399
|
+
}
|
|
400
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
401
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
402
|
+
const object = analyzeExpression(ctx, node.expression);
|
|
403
|
+
if (object.shape === "object" && ts.isIdentifier(node.name)) {
|
|
404
|
+
return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
|
|
405
|
+
}
|
|
406
|
+
return capture(ctx, node, "unknown_property");
|
|
407
|
+
}
|
|
408
|
+
if (ts.isElementAccessExpression(node)) {
|
|
409
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
410
|
+
const array = analyzeExpression(ctx, node.expression);
|
|
411
|
+
if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
|
|
412
|
+
return {
|
|
413
|
+
expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
|
|
414
|
+
shape: "unknown"
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
return capture(ctx, node, "unknown_property");
|
|
418
|
+
}
|
|
419
|
+
if (ts.isCallExpression(node)) {
|
|
420
|
+
if (node.questionDotToken || node.typeArguments?.length) {
|
|
421
|
+
return capture(ctx, node, "unsupported_expression");
|
|
422
|
+
}
|
|
423
|
+
if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
|
|
424
|
+
const calleeName = node.expression.text;
|
|
425
|
+
if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
|
|
426
|
+
const provenId = ctx.provenExact.get(calleeName);
|
|
427
|
+
ctx.callEdges.push({
|
|
428
|
+
callerFunctionId: ctx.functionId,
|
|
429
|
+
calleeName,
|
|
430
|
+
...provenId ? { calleeFunctionId: provenId } : {}
|
|
431
|
+
});
|
|
432
|
+
if (!provenId) return capture(ctx, node, "opaque_call");
|
|
433
|
+
const args = node.arguments.map((argument) => {
|
|
434
|
+
if (ts.isSpreadElement(argument)) return void 0;
|
|
435
|
+
return analyzeExpression(ctx, argument).expressionId;
|
|
436
|
+
});
|
|
437
|
+
if (args.some((argument) => argument === void 0))
|
|
438
|
+
return capture(ctx, node, "unsupported_expression");
|
|
439
|
+
return {
|
|
440
|
+
expressionId: ctx.ir.pureCall(
|
|
441
|
+
provenId,
|
|
442
|
+
args.filter((id) => id !== void 0)
|
|
443
|
+
),
|
|
444
|
+
shape: "unknown"
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
|
|
448
|
+
return capture(
|
|
449
|
+
ctx,
|
|
450
|
+
node,
|
|
451
|
+
node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
if (ts.isConditionalExpression(node)) {
|
|
455
|
+
const test = analyzeExpression(ctx, node.condition);
|
|
456
|
+
const consequent = analyzeExpression(ctx, node.whenTrue);
|
|
457
|
+
const alternate = analyzeExpression(ctx, node.whenFalse);
|
|
458
|
+
return {
|
|
459
|
+
expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
|
|
460
|
+
shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
if (ts.isBinaryExpression(node)) {
|
|
464
|
+
if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
465
|
+
ctx.fallback ??= "mutable_state";
|
|
466
|
+
return capture(ctx, node, "mutable_object");
|
|
467
|
+
}
|
|
468
|
+
if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
469
|
+
const left2 = analyzeExpression(ctx, node.left);
|
|
470
|
+
const right2 = analyzeExpression(ctx, node.right);
|
|
471
|
+
const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
|
|
472
|
+
return {
|
|
473
|
+
expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
|
|
474
|
+
shape: "unknown"
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
const op = binaryOp(node.operatorToken.kind);
|
|
478
|
+
if (!op) return capture(ctx, node, "unsupported_expression");
|
|
479
|
+
if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
|
|
480
|
+
const left = analyzeExpression(ctx, node.left);
|
|
481
|
+
const right = analyzeExpression(ctx, node.right);
|
|
482
|
+
const numeric = isNumericBinaryOp(op);
|
|
483
|
+
const expressionId = ctx.ir.binary(
|
|
484
|
+
op,
|
|
485
|
+
left.expressionId,
|
|
486
|
+
right.expressionId,
|
|
487
|
+
numeric ? "numeric" : "primitive"
|
|
488
|
+
);
|
|
489
|
+
if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
|
|
490
|
+
ctx.hasCoercionGuard = true;
|
|
491
|
+
const fallbackRootId = ctx.nextRootId.value++;
|
|
492
|
+
ctx.guardWraps.push({
|
|
493
|
+
fallbackRootId,
|
|
494
|
+
start: node.getStart(ctx.sourceFile),
|
|
495
|
+
end: node.end,
|
|
496
|
+
op
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
return { expressionId, shape: "primitive" };
|
|
500
|
+
}
|
|
501
|
+
if (ts.isPrefixUnaryExpression(node)) {
|
|
502
|
+
const operand = analyzeExpression(ctx, node.operand);
|
|
503
|
+
if (node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
504
|
+
return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
|
|
505
|
+
}
|
|
506
|
+
if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
|
|
507
|
+
if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
|
|
508
|
+
return {
|
|
509
|
+
expressionId: ctx.ir.unary(
|
|
510
|
+
node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
|
|
511
|
+
operand.expressionId
|
|
512
|
+
),
|
|
513
|
+
shape: "primitive"
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
return capture(ctx, node, "unsupported_expression");
|
|
517
|
+
}
|
|
518
|
+
if (ts.isTypeOfExpression(node)) {
|
|
519
|
+
const operand = unwrapParens(node.expression);
|
|
520
|
+
if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
|
|
521
|
+
return capture(ctx, node, "external_state");
|
|
522
|
+
}
|
|
523
|
+
const inner = analyzeExpression(ctx, node.expression);
|
|
524
|
+
return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
|
|
525
|
+
}
|
|
526
|
+
return capture(ctx, node, "unsupported_expression");
|
|
527
|
+
};
|
|
528
|
+
var bindIdentifier = (ctx, name, result, declaration) => {
|
|
529
|
+
const current = ctx.scopes[ctx.scopes.length - 1];
|
|
530
|
+
current?.set(name.text, {
|
|
531
|
+
expressionId: result.expressionId,
|
|
532
|
+
shape: result.shape,
|
|
533
|
+
mutable: false
|
|
534
|
+
});
|
|
535
|
+
const position = loc(name, ctx.sourceFile);
|
|
536
|
+
const available = loc(declaration, ctx.sourceFile);
|
|
537
|
+
ctx.variables.push({
|
|
538
|
+
id: ctx.nextVariableId.value++,
|
|
539
|
+
name: name.text,
|
|
540
|
+
functionId: ctx.functionId,
|
|
541
|
+
scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
|
|
542
|
+
line: position.line,
|
|
543
|
+
column: position.column,
|
|
544
|
+
assignmentId: ctx.nextAssignmentId.value++,
|
|
545
|
+
expressionId: result.expressionId,
|
|
546
|
+
availableAtLine: available.line,
|
|
547
|
+
availableAtColumn: available.column
|
|
548
|
+
});
|
|
549
|
+
};
|
|
550
|
+
var analyzeStatement = (ctx, node) => {
|
|
551
|
+
if (ctx.fallback) return;
|
|
552
|
+
if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
|
|
553
|
+
if (ts.isBlock(node)) {
|
|
554
|
+
ctx.scopes.push(/* @__PURE__ */ new Map());
|
|
555
|
+
for (const statement of node.statements) analyzeStatement(ctx, statement);
|
|
556
|
+
ctx.scopes.pop();
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (ts.isReturnStatement(node)) {
|
|
560
|
+
if (!node.expression) {
|
|
561
|
+
ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (ts.isVariableStatement(node)) {
|
|
568
|
+
if (node.declarationList.flags & ts.NodeFlags.Using) {
|
|
569
|
+
fallback(ctx, "side_effect");
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
for (const declaration of node.declarationList.declarations) {
|
|
573
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
574
|
+
fallback(ctx, "unsupported_expression");
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const result = analyzeExpression(ctx, declaration.initializer);
|
|
578
|
+
bindIdentifier(ctx, declaration.name, result, declaration);
|
|
579
|
+
}
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
if (ts.isExpressionStatement(node)) {
|
|
583
|
+
analyzeExpression(ctx, node.expression);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
if (ts.isThrowStatement(node)) {
|
|
587
|
+
ctx.hasThrow = true;
|
|
588
|
+
analyzeExpression(ctx, node.expression);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
|
|
592
|
+
fallback(ctx, "unsupported_expression");
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
|
|
596
|
+
fallback(ctx, "has_loop");
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
fallback(ctx, "unsupported_expression");
|
|
600
|
+
};
|
|
601
|
+
var bindParams = (ctx, node) => {
|
|
602
|
+
let index = 0;
|
|
603
|
+
for (const parameter of node.parameters) {
|
|
604
|
+
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
605
|
+
if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
|
|
606
|
+
fallback(ctx, "unsupported_expression");
|
|
607
|
+
index++;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
const expressionId = ctx.ir.param(index);
|
|
611
|
+
ctx.identifierParams.push(parameter.name.text);
|
|
612
|
+
bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
|
|
613
|
+
index++;
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
var analyzeOne = (node, functionId, name, sourceFile, provenExact) => {
|
|
617
|
+
const ctx = {
|
|
618
|
+
sourceFile,
|
|
619
|
+
functionId,
|
|
620
|
+
name,
|
|
621
|
+
ir: new DerivationIrBuilder(),
|
|
622
|
+
variables: [],
|
|
623
|
+
captureWraps: [],
|
|
624
|
+
guardWraps: [],
|
|
625
|
+
callEdges: [],
|
|
626
|
+
scopes: [/* @__PURE__ */ new Map()],
|
|
627
|
+
nextRootId: { value: 0 },
|
|
628
|
+
nextVariableId: { value: 0 },
|
|
629
|
+
nextAssignmentId: { value: 0 },
|
|
630
|
+
hasThrow: false,
|
|
631
|
+
hasCoercionGuard: false,
|
|
632
|
+
provenExact,
|
|
633
|
+
identifierParams: []
|
|
634
|
+
};
|
|
635
|
+
if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
636
|
+
ctx.fallback = "async";
|
|
637
|
+
} else {
|
|
638
|
+
bindParams(ctx, node);
|
|
639
|
+
if (ts.isBlock(node.body)) {
|
|
640
|
+
for (const statement of node.body.statements) analyzeStatement(ctx, statement);
|
|
641
|
+
} else {
|
|
642
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const plan = {
|
|
646
|
+
...emptyPlan(functionId),
|
|
647
|
+
expressions: ctx.ir.expressions,
|
|
648
|
+
variables: ctx.variables,
|
|
649
|
+
captureBoundaries: ctx.captureWraps.map((wrap) => ({
|
|
650
|
+
id: wrap.rootId,
|
|
651
|
+
reason: wrap.reason,
|
|
652
|
+
expressionId: wrap.expressionId,
|
|
653
|
+
line: wrap.line,
|
|
654
|
+
column: wrap.column
|
|
655
|
+
})),
|
|
656
|
+
controlFacts: [],
|
|
657
|
+
...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
|
|
658
|
+
...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
|
|
659
|
+
};
|
|
660
|
+
return {
|
|
661
|
+
functionId,
|
|
662
|
+
name,
|
|
663
|
+
node,
|
|
664
|
+
plan,
|
|
665
|
+
captureWraps: ctx.fallback ? [] : ctx.captureWraps,
|
|
666
|
+
guardWraps: ctx.fallback ? [] : ctx.guardWraps,
|
|
667
|
+
callEdges: ctx.callEdges,
|
|
668
|
+
hasThrow: ctx.hasThrow,
|
|
669
|
+
hasCoercionGuard: ctx.hasCoercionGuard
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
var analyzeModuleFunctions = (sourceFile, targets) => {
|
|
673
|
+
const nameCounts = /* @__PURE__ */ new Map();
|
|
674
|
+
for (const target of targets) {
|
|
675
|
+
nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
|
|
676
|
+
}
|
|
677
|
+
const firstPass = targets.map(
|
|
678
|
+
(target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
|
|
679
|
+
);
|
|
680
|
+
const graph = buildDependencyGraph(
|
|
681
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
682
|
+
firstPass.flatMap((fn) => fn.callEdges)
|
|
683
|
+
);
|
|
684
|
+
const order = topologicalFunctionOrder(
|
|
685
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
686
|
+
graph
|
|
687
|
+
);
|
|
688
|
+
const byId = new Map(targets.map((target) => [target.functionId, target]));
|
|
689
|
+
const provenExact = /* @__PURE__ */ new Map();
|
|
690
|
+
const analyzed = [];
|
|
691
|
+
for (const functionId of order) {
|
|
692
|
+
const target = byId.get(functionId);
|
|
693
|
+
if (!target) continue;
|
|
694
|
+
const result = analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact);
|
|
695
|
+
if (functionIsExact(result.plan) && nameCounts.get(result.name) === 1) {
|
|
696
|
+
provenExact.set(result.name, result.functionId);
|
|
697
|
+
}
|
|
698
|
+
analyzed.push(result);
|
|
699
|
+
}
|
|
700
|
+
const remaining = targets.filter(
|
|
701
|
+
(target) => !analyzed.some((fn) => fn.functionId === target.functionId)
|
|
702
|
+
);
|
|
703
|
+
for (const target of remaining) {
|
|
704
|
+
analyzed.push(analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact));
|
|
705
|
+
}
|
|
706
|
+
return analyzed;
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
// src/derivation/emit-capture-plan.ts
|
|
710
|
+
var captureWrapEdits = (wraps, capName) => {
|
|
711
|
+
const edits = [];
|
|
712
|
+
let order = 0;
|
|
713
|
+
for (const wrap of wraps) {
|
|
714
|
+
edits.push({
|
|
715
|
+
position: wrap.start,
|
|
716
|
+
text: `${wrap.shorthandPropertyName ? `${wrap.shorthandPropertyName}:` : ""}${capName}(${wrap.rootId},`,
|
|
717
|
+
order: order++
|
|
718
|
+
});
|
|
719
|
+
edits.push({
|
|
720
|
+
position: wrap.end,
|
|
721
|
+
text: ")",
|
|
722
|
+
order: order++
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
return edits;
|
|
726
|
+
};
|
|
92
727
|
|
|
93
728
|
// src/execution-recorder/automatic-runtime.ts
|
|
94
729
|
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
|
|
95
730
|
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
96
731
|
|
|
97
732
|
// src/auto-instrumentation/collect-instrumentation-delta.ts
|
|
98
|
-
import
|
|
733
|
+
import ts2 from "typescript";
|
|
99
734
|
var paramBindingFromName = (name, sourceFile) => {
|
|
100
|
-
if (
|
|
101
|
-
if (
|
|
735
|
+
if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
|
|
736
|
+
if (ts2.isArrayBindingPattern(name)) {
|
|
102
737
|
const elements = name.elements.map(
|
|
103
738
|
(element) => {
|
|
104
|
-
if (
|
|
739
|
+
if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
|
|
105
740
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
106
741
|
if (element.dotDotDotToken) return { kind: "rest", binding };
|
|
107
742
|
return binding;
|
|
@@ -111,7 +746,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
111
746
|
}
|
|
112
747
|
const properties = name.elements.map((element) => {
|
|
113
748
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
114
|
-
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") :
|
|
749
|
+
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
|
|
115
750
|
if (element.dotDotDotToken) {
|
|
116
751
|
const rest = { kind: "rest", binding };
|
|
117
752
|
return { key, binding: rest };
|
|
@@ -123,7 +758,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
123
758
|
var collectFunctionParams = (node, sourceFile) => {
|
|
124
759
|
const params = [];
|
|
125
760
|
for (const parameter of node.parameters) {
|
|
126
|
-
if (
|
|
761
|
+
if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
127
762
|
const binding = paramBindingFromName(parameter.name, sourceFile);
|
|
128
763
|
params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
|
|
129
764
|
}
|
|
@@ -164,18 +799,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
|
|
|
164
799
|
};
|
|
165
800
|
var unwrapExpression = (expression) => {
|
|
166
801
|
let current = expression;
|
|
167
|
-
while (
|
|
802
|
+
while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
|
|
168
803
|
current = current.expression;
|
|
169
804
|
}
|
|
170
|
-
if (
|
|
805
|
+
if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
|
|
171
806
|
return current;
|
|
172
807
|
};
|
|
173
808
|
var calleeFromExpression = (expression, sourceFile) => {
|
|
174
809
|
const current = unwrapExpression(expression);
|
|
175
|
-
if (
|
|
810
|
+
if (ts2.isIdentifier(current)) {
|
|
176
811
|
return { calleeName: current.text, calleeText: current.text };
|
|
177
812
|
}
|
|
178
|
-
if (
|
|
813
|
+
if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
|
|
179
814
|
return {
|
|
180
815
|
calleeName: current.name.text,
|
|
181
816
|
calleeText: current.getText(sourceFile).replace(/\s+/g, "")
|
|
@@ -185,9 +820,9 @@ var calleeFromExpression = (expression, sourceFile) => {
|
|
|
185
820
|
};
|
|
186
821
|
var resultBindingForCall = (call) => {
|
|
187
822
|
let node = call.parent;
|
|
188
|
-
if (
|
|
189
|
-
if (!
|
|
190
|
-
if (!
|
|
823
|
+
if (ts2.isAwaitExpression(node)) node = node.parent;
|
|
824
|
+
if (!ts2.isVariableDeclaration(node)) return null;
|
|
825
|
+
if (!ts2.isIdentifier(node.name)) return null;
|
|
191
826
|
const initializer = node.initializer;
|
|
192
827
|
if (!initializer) return null;
|
|
193
828
|
const inner = unwrapExpression(initializer);
|
|
@@ -197,18 +832,18 @@ var resultBindingForCall = (call) => {
|
|
|
197
832
|
var argIdentifiersForCall = (call) => {
|
|
198
833
|
const identifiers = [];
|
|
199
834
|
call.arguments?.forEach((argument, index) => {
|
|
200
|
-
if (
|
|
835
|
+
if (ts2.isIdentifier(argument)) {
|
|
201
836
|
identifiers.push({ index, kind: "identifier", name: argument.text });
|
|
202
837
|
return;
|
|
203
838
|
}
|
|
204
|
-
if (!
|
|
839
|
+
if (!ts2.isObjectLiteralExpression(argument)) return;
|
|
205
840
|
const properties = [];
|
|
206
841
|
for (const property of argument.properties) {
|
|
207
|
-
if (
|
|
842
|
+
if (ts2.isShorthandPropertyAssignment(property)) {
|
|
208
843
|
properties.push({ key: property.name.text, name: property.name.text });
|
|
209
844
|
continue;
|
|
210
845
|
}
|
|
211
|
-
if (
|
|
846
|
+
if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
|
|
212
847
|
properties.push({ key: property.name.text, name: property.initializer.text });
|
|
213
848
|
}
|
|
214
849
|
}
|
|
@@ -248,8 +883,8 @@ var collectCallSite = (call, caller, context) => {
|
|
|
248
883
|
};
|
|
249
884
|
};
|
|
250
885
|
var collectParamDestructure = (declaration, caller, context) => {
|
|
251
|
-
if (!declaration.initializer || !
|
|
252
|
-
if (!
|
|
886
|
+
if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
|
|
887
|
+
if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
|
|
253
888
|
return null;
|
|
254
889
|
}
|
|
255
890
|
const sourceParam = declaration.initializer.text;
|
|
@@ -273,33 +908,33 @@ var collectParamDestructure = (declaration, caller, context) => {
|
|
|
273
908
|
|
|
274
909
|
// src/auto-instrumentation/transform-source.ts
|
|
275
910
|
var scriptKindFor = (filePath) => {
|
|
276
|
-
if (/\.tsx$/i.test(filePath)) return
|
|
277
|
-
if (/\.jsx$/i.test(filePath)) return
|
|
278
|
-
if (/\.[cm]?ts$/i.test(filePath)) return
|
|
279
|
-
return
|
|
911
|
+
if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
|
|
912
|
+
if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
|
|
913
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
|
|
914
|
+
return ts3.ScriptKind.JS;
|
|
280
915
|
};
|
|
281
916
|
var functionName = (node, sourceFile) => {
|
|
282
917
|
if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
|
|
283
918
|
const parent = node.parent;
|
|
284
|
-
if (
|
|
285
|
-
if (
|
|
286
|
-
if (
|
|
919
|
+
if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
|
|
920
|
+
if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
|
|
921
|
+
if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
|
|
287
922
|
const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
288
923
|
return `anonymous@${location.line + 1}:${location.character + 1}`;
|
|
289
924
|
};
|
|
290
925
|
var functionLocationPosition = (node, sourceFile) => {
|
|
291
926
|
if (node.name) return node.name.getStart(sourceFile);
|
|
292
927
|
const parent = node.parent;
|
|
293
|
-
if ((
|
|
928
|
+
if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
|
|
294
929
|
return parent.name.getStart(sourceFile);
|
|
295
930
|
}
|
|
296
931
|
return node.getStart(sourceFile);
|
|
297
932
|
};
|
|
298
933
|
var bindingValue = (name, sourceFile) => {
|
|
299
|
-
if (
|
|
300
|
-
if (
|
|
934
|
+
if (ts3.isIdentifier(name)) return name.text;
|
|
935
|
+
if (ts3.isArrayBindingPattern(name)) {
|
|
301
936
|
const values = name.elements.map((element) => {
|
|
302
|
-
if (
|
|
937
|
+
if (ts3.isOmittedExpression(element)) return "";
|
|
303
938
|
const value = bindingValue(element.name, sourceFile);
|
|
304
939
|
return element.dotDotDotToken ? `...${value}` : value;
|
|
305
940
|
});
|
|
@@ -315,14 +950,14 @@ var bindingValue = (name, sourceFile) => {
|
|
|
315
950
|
};
|
|
316
951
|
var arrowArguments = (node) => {
|
|
317
952
|
const values = node.parameters.flatMap((parameter) => {
|
|
318
|
-
if (
|
|
953
|
+
if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
|
|
319
954
|
const value = bindingValue(parameter.name, node.getSourceFile());
|
|
320
955
|
return [parameter.dotDotDotToken ? `...${value}` : value];
|
|
321
956
|
});
|
|
322
957
|
return `[${values.join(",")}]`;
|
|
323
958
|
};
|
|
324
959
|
var isSupportedFunction = (node) => {
|
|
325
|
-
if (!
|
|
960
|
+
if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
|
|
326
961
|
return false;
|
|
327
962
|
}
|
|
328
963
|
if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
|
|
@@ -333,7 +968,7 @@ var uniqueName = (source, base) => {
|
|
|
333
968
|
while (source.includes(name)) name += "_";
|
|
334
969
|
return name;
|
|
335
970
|
};
|
|
336
|
-
var isDirectivePrologue = (statement) =>
|
|
971
|
+
var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
|
|
337
972
|
var insertPositionAfterPrologue = (source, sourceFile) => {
|
|
338
973
|
const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
|
|
339
974
|
let end = shebangEnd;
|
|
@@ -348,7 +983,7 @@ var preambleInsert = (source, position, preamble) => {
|
|
|
348
983
|
const previous = source[position - 1];
|
|
349
984
|
return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
|
|
350
985
|
};
|
|
351
|
-
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)}));`;
|
|
986
|
+
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)}));`;
|
|
352
987
|
var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
353
988
|
const start = mappedLocation(
|
|
354
989
|
sourceFile,
|
|
@@ -379,80 +1014,127 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
|
379
1014
|
paramNames
|
|
380
1015
|
};
|
|
381
1016
|
};
|
|
1017
|
+
var canOmitCallValues = (analysis, analyzed) => {
|
|
1018
|
+
if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
|
|
1019
|
+
return false;
|
|
1020
|
+
}
|
|
1021
|
+
const callers = analyzed.filter(
|
|
1022
|
+
(caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
|
|
1023
|
+
);
|
|
1024
|
+
if (callers.length === 0) return false;
|
|
1025
|
+
return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
|
|
1026
|
+
};
|
|
382
1027
|
var transformSource = (source, options) => {
|
|
383
1028
|
if (source.includes("@rasputin-ignore-file")) return void 0;
|
|
384
|
-
const sourceFile =
|
|
1029
|
+
const sourceFile = ts3.createSourceFile(
|
|
385
1030
|
options.filePath,
|
|
386
1031
|
source,
|
|
387
|
-
|
|
1032
|
+
ts3.ScriptTarget.Latest,
|
|
388
1033
|
true,
|
|
389
1034
|
scriptKindFor(options.filePath)
|
|
390
1035
|
);
|
|
391
1036
|
const runName = uniqueName(source, "__rasputinRun__");
|
|
1037
|
+
const capName = uniqueName(source, "__rasputinCap__");
|
|
392
1038
|
const plansName = uniqueName(source, "__rasputinPlans__");
|
|
393
1039
|
const absolutePath = resolve3(options.filePath);
|
|
1040
|
+
const derivationMode = options.derivationMode ?? "analysis";
|
|
394
1041
|
const edits = [];
|
|
395
1042
|
let order = 0;
|
|
396
1043
|
const delta = {
|
|
397
1044
|
functions: [],
|
|
398
1045
|
callSites: [],
|
|
399
|
-
destructures: []
|
|
1046
|
+
destructures: [],
|
|
1047
|
+
derivationPlans: []
|
|
400
1048
|
};
|
|
401
1049
|
const collectContext = {
|
|
402
1050
|
sourceFile,
|
|
403
1051
|
mapLocation: options.mapLocation,
|
|
404
1052
|
absolutePath
|
|
405
1053
|
};
|
|
1054
|
+
const pending = [];
|
|
406
1055
|
const visit = (node, caller) => {
|
|
407
1056
|
if (isSupportedFunction(node)) {
|
|
408
1057
|
const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
|
|
409
1058
|
if (!collected) {
|
|
410
|
-
|
|
1059
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
411
1060
|
return;
|
|
412
1061
|
}
|
|
413
|
-
|
|
1062
|
+
pending.push({ node, collected });
|
|
414
1063
|
delta.functions.push(collected);
|
|
415
|
-
|
|
416
|
-
const asyncCallback = node.modifiers?.some(
|
|
417
|
-
(modifier) => modifier.kind === ts2.SyntaxKind.AsyncKeyword
|
|
418
|
-
) ? "async " : "";
|
|
419
|
-
if (ts2.isBlock(node.body)) {
|
|
420
|
-
edits.push({
|
|
421
|
-
position: node.body.getStart(sourceFile) + 1,
|
|
422
|
-
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
423
|
-
order: order++
|
|
424
|
-
});
|
|
425
|
-
edits.push({ position: node.body.end - 1, text: "});", order: order++ });
|
|
426
|
-
} else {
|
|
427
|
-
edits.push({
|
|
428
|
-
position: node.body.getStart(sourceFile),
|
|
429
|
-
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
430
|
-
order: order++
|
|
431
|
-
});
|
|
432
|
-
edits.push({ position: node.body.end, text: "))", order: order++ });
|
|
433
|
-
}
|
|
434
|
-
ts2.forEachChild(node, (child) => visit(child, collected));
|
|
1064
|
+
ts3.forEachChild(node, (child) => visit(child, collected));
|
|
435
1065
|
return;
|
|
436
1066
|
}
|
|
437
|
-
if (caller && (
|
|
1067
|
+
if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
|
|
438
1068
|
const site = collectCallSite(node, caller, collectContext);
|
|
439
1069
|
if (site) delta.callSites.push(site);
|
|
440
1070
|
}
|
|
441
|
-
if (caller &&
|
|
1071
|
+
if (caller && ts3.isVariableDeclaration(node)) {
|
|
442
1072
|
const destructure = collectParamDestructure(node, caller, collectContext);
|
|
443
1073
|
if (destructure) delta.destructures.push(destructure);
|
|
444
1074
|
}
|
|
445
|
-
|
|
1075
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
446
1076
|
};
|
|
447
1077
|
visit(sourceFile);
|
|
448
|
-
if (
|
|
1078
|
+
if (pending.length === 0) return void 0;
|
|
1079
|
+
const analyzed = analyzeModuleFunctions(
|
|
1080
|
+
sourceFile,
|
|
1081
|
+
pending.map((entry) => ({
|
|
1082
|
+
functionId: entry.collected.functionId,
|
|
1083
|
+
name: entry.collected.name,
|
|
1084
|
+
node: entry.node
|
|
1085
|
+
}))
|
|
1086
|
+
);
|
|
1087
|
+
delta.derivationPlans = analyzed.map((fn) => fn.plan);
|
|
1088
|
+
const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
|
|
1089
|
+
for (const [planIndex, entry] of pending.entries()) {
|
|
1090
|
+
const analysis = analyzedById.get(entry.collected.functionId);
|
|
1091
|
+
const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
|
|
1092
|
+
const valueFlag = omitValues ? ",1" : "";
|
|
1093
|
+
const args = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
|
|
1094
|
+
const asyncCallback = entry.node.modifiers?.some(
|
|
1095
|
+
(modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
|
|
1096
|
+
) ? "async " : "";
|
|
1097
|
+
if (ts3.isBlock(entry.node.body)) {
|
|
1098
|
+
edits.push({
|
|
1099
|
+
position: entry.node.body.getStart(sourceFile) + 1,
|
|
1100
|
+
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
1101
|
+
order: order++
|
|
1102
|
+
});
|
|
1103
|
+
edits.push({
|
|
1104
|
+
position: entry.node.body.end - 1,
|
|
1105
|
+
text: `}${valueFlag});`,
|
|
1106
|
+
order: order++
|
|
1107
|
+
});
|
|
1108
|
+
} else {
|
|
1109
|
+
edits.push({
|
|
1110
|
+
position: entry.node.body.getStart(sourceFile),
|
|
1111
|
+
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
1112
|
+
order: order++
|
|
1113
|
+
});
|
|
1114
|
+
edits.push({
|
|
1115
|
+
position: entry.node.body.end,
|
|
1116
|
+
text: `)${valueFlag})`,
|
|
1117
|
+
order: order++
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
if (analysis) {
|
|
1121
|
+
for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
|
|
1122
|
+
edits.push({ ...edit, order: order++ });
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
449
1126
|
const insertAt = insertPositionAfterPrologue(source, sourceFile);
|
|
450
1127
|
edits.push({
|
|
451
1128
|
position: insertAt,
|
|
452
1129
|
text: preambleInsert(
|
|
453
1130
|
source,
|
|
454
1131
|
insertAt,
|
|
455
|
-
modulePreamble(
|
|
1132
|
+
modulePreamble(
|
|
1133
|
+
runName,
|
|
1134
|
+
capName,
|
|
1135
|
+
plansName,
|
|
1136
|
+
JSON.stringify(delta.functions.map((fn) => fn.plan))
|
|
1137
|
+
)
|
|
456
1138
|
),
|
|
457
1139
|
order: -1
|
|
458
1140
|
});
|