@rasputin-ai/node 0.5.0-alpha.5 → 0.5.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +2 -0
- package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +2 -1
- package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +1 -1
- package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
- package/dist/auto-instrumentation/transform-source.d.ts +6 -0
- package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
- package/dist/derivation/analyze-function.d.ts +40 -0
- package/dist/derivation/analyze-function.d.ts.map +1 -0
- package/dist/derivation/build-dependency-graph.d.ts +21 -0
- package/dist/derivation/build-dependency-graph.d.ts.map +1 -0
- package/dist/derivation/derivation-ir.d.ts +33 -0
- package/dist/derivation/derivation-ir.d.ts.map +1 -0
- package/dist/derivation/derivation-proof.d.ts +14 -0
- package/dist/derivation/derivation-proof.d.ts.map +1 -0
- package/dist/derivation/emit-capture-plan.d.ts +8 -0
- package/dist/derivation/emit-capture-plan.d.ts.map +1 -0
- package/dist/execution-recorder/automatic-runtime.d.ts +3 -1
- package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
- package/dist/execution-recorder/execution-journal.d.ts +84 -0
- package/dist/execution-recorder/execution-journal.d.ts.map +1 -0
- package/dist/execution-recorder/execution-recorder-types.d.ts +19 -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/execution-recorder/recorder-memory-budget.d.ts +1 -0
- package/dist/execution-recorder/recorder-memory-budget.d.ts.map +1 -1
- package/dist/execution-recorder/safe-serialize.d.ts +1 -0
- package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
- package/dist/index.js +1058 -317
- package/dist/instrument/build.js +761 -71
- package/dist/instrument/bun.js +742 -66
- package/dist/instrument/node.js +742 -66
- package/dist/sdk-meta.d.ts +1 -1
- package/package.json +2 -2
- package/dist/execution-recorder/call-aware-event-buffer.d.ts +0 -37
- package/dist/execution-recorder/call-aware-event-buffer.d.ts.map +0 -1
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,649 @@ 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
|
+
properties.push({ key: property.name.text, value: nested.expressionId });
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (!ts.isPropertyAssignment(property)) {
|
|
383
|
+
return capture(ctx, node, "unsupported_expression");
|
|
384
|
+
}
|
|
385
|
+
if (ts.isPrivateIdentifier(property.name) || ts.isComputedPropertyName(property.name)) {
|
|
386
|
+
return capture(ctx, node, "unsupported_expression");
|
|
387
|
+
}
|
|
388
|
+
const key = ts.isIdentifier(property.name) ? property.name.text : ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name) ? property.name.text : void 0;
|
|
389
|
+
if (key === void 0) return capture(ctx, node, "unsupported_expression");
|
|
390
|
+
properties.push({ key, value: analyzeExpression(ctx, property.initializer).expressionId });
|
|
391
|
+
}
|
|
392
|
+
return { expressionId: ctx.ir.object(properties), shape: "object" };
|
|
393
|
+
}
|
|
394
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
395
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
396
|
+
const object = analyzeExpression(ctx, node.expression);
|
|
397
|
+
if (object.shape === "object" && ts.isIdentifier(node.name)) {
|
|
398
|
+
return { expressionId: ctx.ir.prop(object.expressionId, node.name.text), shape: "unknown" };
|
|
399
|
+
}
|
|
400
|
+
return capture(ctx, node, "unknown_property");
|
|
401
|
+
}
|
|
402
|
+
if (ts.isElementAccessExpression(node)) {
|
|
403
|
+
if (node.questionDotToken) return capture(ctx, node, "unknown_property");
|
|
404
|
+
const array = analyzeExpression(ctx, node.expression);
|
|
405
|
+
if (array.shape === "array" && ts.isNumericLiteral(node.argumentExpression)) {
|
|
406
|
+
return {
|
|
407
|
+
expressionId: ctx.ir.index(array.expressionId, Number(node.argumentExpression.text)),
|
|
408
|
+
shape: "unknown"
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
return capture(ctx, node, "unknown_property");
|
|
412
|
+
}
|
|
413
|
+
if (ts.isCallExpression(node)) {
|
|
414
|
+
if (node.questionDotToken || node.typeArguments?.length) {
|
|
415
|
+
return capture(ctx, node, "unsupported_expression");
|
|
416
|
+
}
|
|
417
|
+
if (!ts.isIdentifier(node.expression)) return capture(ctx, node, "dynamic_callee");
|
|
418
|
+
const calleeName = node.expression.text;
|
|
419
|
+
if (lookup(ctx, calleeName)) return capture(ctx, node, "opaque_call");
|
|
420
|
+
const provenId = ctx.provenExact.get(calleeName);
|
|
421
|
+
ctx.callEdges.push({
|
|
422
|
+
callerFunctionId: ctx.functionId,
|
|
423
|
+
calleeName,
|
|
424
|
+
...provenId ? { calleeFunctionId: provenId } : {}
|
|
425
|
+
});
|
|
426
|
+
if (!provenId) return capture(ctx, node, "opaque_call");
|
|
427
|
+
const args = node.arguments.map((argument) => {
|
|
428
|
+
if (ts.isSpreadElement(argument)) return void 0;
|
|
429
|
+
return analyzeExpression(ctx, argument).expressionId;
|
|
430
|
+
});
|
|
431
|
+
if (args.some((argument) => argument === void 0))
|
|
432
|
+
return capture(ctx, node, "unsupported_expression");
|
|
433
|
+
return {
|
|
434
|
+
expressionId: ctx.ir.pureCall(
|
|
435
|
+
provenId,
|
|
436
|
+
args.filter((id) => id !== void 0)
|
|
437
|
+
),
|
|
438
|
+
shape: "unknown"
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
if (ts.isNewExpression(node) || ts.isAwaitExpression(node) || ts.isYieldExpression(node)) {
|
|
442
|
+
return capture(
|
|
443
|
+
ctx,
|
|
444
|
+
node,
|
|
445
|
+
node.kind === ts.SyntaxKind.AwaitExpression ? "nondeterministic" : "unsupported_expression"
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
if (ts.isConditionalExpression(node)) {
|
|
449
|
+
const test = analyzeExpression(ctx, node.condition);
|
|
450
|
+
const consequent = analyzeExpression(ctx, node.whenTrue);
|
|
451
|
+
const alternate = analyzeExpression(ctx, node.whenFalse);
|
|
452
|
+
return {
|
|
453
|
+
expressionId: ctx.ir.cond(test.expressionId, consequent.expressionId, alternate.expressionId),
|
|
454
|
+
shape: consequent.shape === alternate.shape ? consequent.shape : "unknown"
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
if (ts.isBinaryExpression(node)) {
|
|
458
|
+
if (node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
|
|
459
|
+
ctx.fallback ??= "mutable_state";
|
|
460
|
+
return capture(ctx, node, "mutable_object");
|
|
461
|
+
}
|
|
462
|
+
if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken || node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
463
|
+
const left2 = analyzeExpression(ctx, node.left);
|
|
464
|
+
const right2 = analyzeExpression(ctx, node.right);
|
|
465
|
+
const op2 = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "and" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "or" : "nullish";
|
|
466
|
+
return {
|
|
467
|
+
expressionId: ctx.ir.logical(op2, left2.expressionId, right2.expressionId),
|
|
468
|
+
shape: "unknown"
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
const op = binaryOp(node.operatorToken.kind);
|
|
472
|
+
if (!op) return capture(ctx, node, "unsupported_expression");
|
|
473
|
+
if (!ts.isExpression(node.left)) return capture(ctx, node, "unsupported_expression");
|
|
474
|
+
const left = analyzeExpression(ctx, node.left);
|
|
475
|
+
const right = analyzeExpression(ctx, node.right);
|
|
476
|
+
const numeric = isNumericBinaryOp(op);
|
|
477
|
+
const expressionId = ctx.ir.binary(
|
|
478
|
+
op,
|
|
479
|
+
left.expressionId,
|
|
480
|
+
right.expressionId,
|
|
481
|
+
numeric ? "numeric" : "primitive"
|
|
482
|
+
);
|
|
483
|
+
if (numeric && (left.captured || right.captured || left.shape !== "primitive" || right.shape !== "primitive")) {
|
|
484
|
+
ctx.hasCoercionGuard = true;
|
|
485
|
+
const fallbackRootId = ctx.nextRootId.value++;
|
|
486
|
+
ctx.guardWraps.push({
|
|
487
|
+
fallbackRootId,
|
|
488
|
+
start: node.getStart(ctx.sourceFile),
|
|
489
|
+
end: node.end,
|
|
490
|
+
op
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
return { expressionId, shape: "primitive" };
|
|
494
|
+
}
|
|
495
|
+
if (ts.isPrefixUnaryExpression(node)) {
|
|
496
|
+
const operand = analyzeExpression(ctx, node.operand);
|
|
497
|
+
if (node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
498
|
+
return { expressionId: ctx.ir.unary("not", operand.expressionId), shape: "primitive" };
|
|
499
|
+
}
|
|
500
|
+
if (node.operator === ts.SyntaxKind.PlusToken || node.operator === ts.SyntaxKind.MinusToken) {
|
|
501
|
+
if (operand.captured || operand.shape !== "primitive") ctx.hasCoercionGuard = true;
|
|
502
|
+
return {
|
|
503
|
+
expressionId: ctx.ir.unary(
|
|
504
|
+
node.operator === ts.SyntaxKind.MinusToken ? "minus" : "plus",
|
|
505
|
+
operand.expressionId
|
|
506
|
+
),
|
|
507
|
+
shape: "primitive"
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
return capture(ctx, node, "unsupported_expression");
|
|
511
|
+
}
|
|
512
|
+
if (ts.isTypeOfExpression(node)) {
|
|
513
|
+
const operand = unwrapParens(node.expression);
|
|
514
|
+
if (ts.isIdentifier(operand) && operand.text !== "undefined" && !lookup(ctx, operand.text)) {
|
|
515
|
+
return capture(ctx, node, "external_state");
|
|
516
|
+
}
|
|
517
|
+
const inner = analyzeExpression(ctx, node.expression);
|
|
518
|
+
return { expressionId: ctx.ir.unary("typeof", inner.expressionId), shape: "primitive" };
|
|
519
|
+
}
|
|
520
|
+
return capture(ctx, node, "unsupported_expression");
|
|
521
|
+
};
|
|
522
|
+
var bindIdentifier = (ctx, name, result, declaration) => {
|
|
523
|
+
const current = ctx.scopes[ctx.scopes.length - 1];
|
|
524
|
+
current?.set(name.text, {
|
|
525
|
+
expressionId: result.expressionId,
|
|
526
|
+
shape: result.shape,
|
|
527
|
+
mutable: false
|
|
528
|
+
});
|
|
529
|
+
const position = loc(name, ctx.sourceFile);
|
|
530
|
+
const available = loc(declaration, ctx.sourceFile);
|
|
531
|
+
ctx.variables.push({
|
|
532
|
+
id: ctx.nextVariableId.value++,
|
|
533
|
+
name: name.text,
|
|
534
|
+
functionId: ctx.functionId,
|
|
535
|
+
scopeId: `${ctx.functionId}:${ctx.scopes.length}`,
|
|
536
|
+
line: position.line,
|
|
537
|
+
column: position.column,
|
|
538
|
+
assignmentId: ctx.nextAssignmentId.value++,
|
|
539
|
+
expressionId: result.expressionId,
|
|
540
|
+
availableAtLine: available.line,
|
|
541
|
+
availableAtColumn: available.column
|
|
542
|
+
});
|
|
543
|
+
};
|
|
544
|
+
var analyzeStatement = (ctx, node) => {
|
|
545
|
+
if (ctx.fallback) return;
|
|
546
|
+
if (ts.isEmptyStatement(node) || ts.isDebuggerStatement(node)) return;
|
|
547
|
+
if (ts.isBlock(node)) {
|
|
548
|
+
ctx.scopes.push(/* @__PURE__ */ new Map());
|
|
549
|
+
for (const statement of node.statements) analyzeStatement(ctx, statement);
|
|
550
|
+
ctx.scopes.pop();
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (ts.isReturnStatement(node)) {
|
|
554
|
+
if (!node.expression) {
|
|
555
|
+
ctx.returnExpressionId = ctx.ir.constValue({ type: "undefined" });
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.expression).expressionId;
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
if (ts.isVariableStatement(node)) {
|
|
562
|
+
if (node.declarationList.flags & ts.NodeFlags.Using) {
|
|
563
|
+
fallback(ctx, "side_effect");
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
for (const declaration of node.declarationList.declarations) {
|
|
567
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
568
|
+
fallback(ctx, "unsupported_expression");
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const result = analyzeExpression(ctx, declaration.initializer);
|
|
572
|
+
bindIdentifier(ctx, declaration.name, result, declaration);
|
|
573
|
+
}
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if (ts.isExpressionStatement(node)) {
|
|
577
|
+
analyzeExpression(ctx, node.expression);
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
if (ts.isThrowStatement(node)) {
|
|
581
|
+
ctx.hasThrow = true;
|
|
582
|
+
analyzeExpression(ctx, node.expression);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (ts.isIfStatement(node) || ts.isSwitchStatement(node) || ts.isTryStatement(node)) {
|
|
586
|
+
fallback(ctx, "unsupported_expression");
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node) || ts.isWhileStatement(node) || ts.isDoStatement(node)) {
|
|
590
|
+
fallback(ctx, "has_loop");
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
fallback(ctx, "unsupported_expression");
|
|
594
|
+
};
|
|
595
|
+
var bindParams = (ctx, node) => {
|
|
596
|
+
let index = 0;
|
|
597
|
+
for (const parameter of node.parameters) {
|
|
598
|
+
if (ts.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
599
|
+
if (!ts.isIdentifier(parameter.name) || parameter.dotDotDotToken) {
|
|
600
|
+
fallback(ctx, "unsupported_expression");
|
|
601
|
+
index++;
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
const expressionId = ctx.ir.param(index);
|
|
605
|
+
ctx.identifierParams.push(parameter.name.text);
|
|
606
|
+
bindIdentifier(ctx, parameter.name, { expressionId, shape: "unknown" }, parameter);
|
|
607
|
+
index++;
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
var analyzeOne = (node, functionId, name, sourceFile, provenExact) => {
|
|
611
|
+
const ctx = {
|
|
612
|
+
sourceFile,
|
|
613
|
+
functionId,
|
|
614
|
+
name,
|
|
615
|
+
ir: new DerivationIrBuilder(),
|
|
616
|
+
variables: [],
|
|
617
|
+
captureWraps: [],
|
|
618
|
+
guardWraps: [],
|
|
619
|
+
callEdges: [],
|
|
620
|
+
scopes: [/* @__PURE__ */ new Map()],
|
|
621
|
+
nextRootId: { value: 0 },
|
|
622
|
+
nextVariableId: { value: 0 },
|
|
623
|
+
nextAssignmentId: { value: 0 },
|
|
624
|
+
hasThrow: false,
|
|
625
|
+
hasCoercionGuard: false,
|
|
626
|
+
provenExact,
|
|
627
|
+
identifierParams: []
|
|
628
|
+
};
|
|
629
|
+
if (node.asteriskToken || node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)) {
|
|
630
|
+
ctx.fallback = "async";
|
|
631
|
+
} else {
|
|
632
|
+
bindParams(ctx, node);
|
|
633
|
+
if (ts.isBlock(node.body)) {
|
|
634
|
+
for (const statement of node.body.statements) analyzeStatement(ctx, statement);
|
|
635
|
+
} else {
|
|
636
|
+
ctx.returnExpressionId = analyzeExpression(ctx, node.body).expressionId;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const plan = {
|
|
640
|
+
...emptyPlan(functionId),
|
|
641
|
+
expressions: ctx.ir.expressions,
|
|
642
|
+
variables: ctx.variables,
|
|
643
|
+
captureBoundaries: ctx.captureWraps.map((wrap) => ({
|
|
644
|
+
id: wrap.rootId,
|
|
645
|
+
reason: wrap.reason,
|
|
646
|
+
expressionId: wrap.expressionId,
|
|
647
|
+
line: wrap.line,
|
|
648
|
+
column: wrap.column
|
|
649
|
+
})),
|
|
650
|
+
controlFacts: [],
|
|
651
|
+
...ctx.returnExpressionId === void 0 ? {} : { returnExpressionId: ctx.returnExpressionId },
|
|
652
|
+
...ctx.fallback ? { fallbackReason: ctx.fallback } : {}
|
|
653
|
+
};
|
|
654
|
+
return {
|
|
655
|
+
functionId,
|
|
656
|
+
name,
|
|
657
|
+
node,
|
|
658
|
+
plan,
|
|
659
|
+
captureWraps: ctx.fallback ? [] : ctx.captureWraps,
|
|
660
|
+
guardWraps: ctx.fallback ? [] : ctx.guardWraps,
|
|
661
|
+
callEdges: ctx.callEdges,
|
|
662
|
+
hasThrow: ctx.hasThrow,
|
|
663
|
+
hasCoercionGuard: ctx.hasCoercionGuard
|
|
664
|
+
};
|
|
665
|
+
};
|
|
666
|
+
var analyzeModuleFunctions = (sourceFile, targets) => {
|
|
667
|
+
const nameCounts = /* @__PURE__ */ new Map();
|
|
668
|
+
for (const target of targets) {
|
|
669
|
+
nameCounts.set(target.name, (nameCounts.get(target.name) ?? 0) + 1);
|
|
670
|
+
}
|
|
671
|
+
const firstPass = targets.map(
|
|
672
|
+
(target) => analyzeOne(target.node, target.functionId, target.name, sourceFile, /* @__PURE__ */ new Map())
|
|
673
|
+
);
|
|
674
|
+
const graph = buildDependencyGraph(
|
|
675
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
676
|
+
firstPass.flatMap((fn) => fn.callEdges)
|
|
677
|
+
);
|
|
678
|
+
const order = topologicalFunctionOrder(
|
|
679
|
+
firstPass.map((fn) => ({ functionId: fn.functionId, name: fn.name })),
|
|
680
|
+
graph
|
|
681
|
+
);
|
|
682
|
+
const byId = new Map(targets.map((target) => [target.functionId, target]));
|
|
683
|
+
const provenExact = /* @__PURE__ */ new Map();
|
|
684
|
+
const analyzed = [];
|
|
685
|
+
for (const functionId of order) {
|
|
686
|
+
const target = byId.get(functionId);
|
|
687
|
+
if (!target) continue;
|
|
688
|
+
const result = analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact);
|
|
689
|
+
if (functionIsExact(result.plan) && nameCounts.get(result.name) === 1) {
|
|
690
|
+
provenExact.set(result.name, result.functionId);
|
|
691
|
+
}
|
|
692
|
+
analyzed.push(result);
|
|
693
|
+
}
|
|
694
|
+
const remaining = targets.filter(
|
|
695
|
+
(target) => !analyzed.some((fn) => fn.functionId === target.functionId)
|
|
696
|
+
);
|
|
697
|
+
for (const target of remaining) {
|
|
698
|
+
analyzed.push(analyzeOne(target.node, target.functionId, target.name, sourceFile, provenExact));
|
|
699
|
+
}
|
|
700
|
+
return analyzed;
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
// src/derivation/emit-capture-plan.ts
|
|
704
|
+
var captureWrapEdits = (wraps, capName) => {
|
|
705
|
+
const edits = [];
|
|
706
|
+
let order = 0;
|
|
707
|
+
for (const wrap of wraps) {
|
|
708
|
+
edits.push({
|
|
709
|
+
position: wrap.start,
|
|
710
|
+
text: `${capName}(${wrap.rootId},`,
|
|
711
|
+
order: order++
|
|
712
|
+
});
|
|
713
|
+
edits.push({
|
|
714
|
+
position: wrap.end,
|
|
715
|
+
text: ")",
|
|
716
|
+
order: order++
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
return edits;
|
|
720
|
+
};
|
|
92
721
|
|
|
93
722
|
// src/execution-recorder/automatic-runtime.ts
|
|
94
723
|
var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
|
|
95
724
|
var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
|
|
96
725
|
|
|
97
726
|
// src/auto-instrumentation/collect-instrumentation-delta.ts
|
|
98
|
-
import
|
|
727
|
+
import ts2 from "typescript";
|
|
99
728
|
var paramBindingFromName = (name, sourceFile) => {
|
|
100
|
-
if (
|
|
101
|
-
if (
|
|
729
|
+
if (ts2.isIdentifier(name)) return { kind: "identifier", name: name.text };
|
|
730
|
+
if (ts2.isArrayBindingPattern(name)) {
|
|
102
731
|
const elements = name.elements.map(
|
|
103
732
|
(element) => {
|
|
104
|
-
if (
|
|
733
|
+
if (ts2.isOmittedExpression(element)) return { kind: "omitted" };
|
|
105
734
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
106
735
|
if (element.dotDotDotToken) return { kind: "rest", binding };
|
|
107
736
|
return binding;
|
|
@@ -111,7 +740,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
111
740
|
}
|
|
112
741
|
const properties = name.elements.map((element) => {
|
|
113
742
|
const binding = paramBindingFromName(element.name, sourceFile);
|
|
114
|
-
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") :
|
|
743
|
+
const key = element.propertyName ? element.propertyName.getText(sourceFile).replace(/^['"]|['"]$/g, "") : ts2.isIdentifier(element.name) ? element.name.text : element.name.getText(sourceFile);
|
|
115
744
|
if (element.dotDotDotToken) {
|
|
116
745
|
const rest = { kind: "rest", binding };
|
|
117
746
|
return { key, binding: rest };
|
|
@@ -123,7 +752,7 @@ var paramBindingFromName = (name, sourceFile) => {
|
|
|
123
752
|
var collectFunctionParams = (node, sourceFile) => {
|
|
124
753
|
const params = [];
|
|
125
754
|
for (const parameter of node.parameters) {
|
|
126
|
-
if (
|
|
755
|
+
if (ts2.isIdentifier(parameter.name) && parameter.name.text === "this") continue;
|
|
127
756
|
const binding = paramBindingFromName(parameter.name, sourceFile);
|
|
128
757
|
params.push(parameter.dotDotDotToken ? { kind: "rest", binding } : binding);
|
|
129
758
|
}
|
|
@@ -164,18 +793,18 @@ var mappedLocation = (sourceFile, position, absolutePath, mapLocation) => {
|
|
|
164
793
|
};
|
|
165
794
|
var unwrapExpression = (expression) => {
|
|
166
795
|
let current = expression;
|
|
167
|
-
while (
|
|
796
|
+
while (ts2.isParenthesizedExpression(current) || ts2.isNonNullExpression(current)) {
|
|
168
797
|
current = current.expression;
|
|
169
798
|
}
|
|
170
|
-
if (
|
|
799
|
+
if (ts2.isAwaitExpression(current)) return unwrapExpression(current.expression);
|
|
171
800
|
return current;
|
|
172
801
|
};
|
|
173
802
|
var calleeFromExpression = (expression, sourceFile) => {
|
|
174
803
|
const current = unwrapExpression(expression);
|
|
175
|
-
if (
|
|
804
|
+
if (ts2.isIdentifier(current)) {
|
|
176
805
|
return { calleeName: current.text, calleeText: current.text };
|
|
177
806
|
}
|
|
178
|
-
if (
|
|
807
|
+
if (ts2.isPropertyAccessExpression(current) && ts2.isIdentifier(current.name)) {
|
|
179
808
|
return {
|
|
180
809
|
calleeName: current.name.text,
|
|
181
810
|
calleeText: current.getText(sourceFile).replace(/\s+/g, "")
|
|
@@ -185,9 +814,9 @@ var calleeFromExpression = (expression, sourceFile) => {
|
|
|
185
814
|
};
|
|
186
815
|
var resultBindingForCall = (call) => {
|
|
187
816
|
let node = call.parent;
|
|
188
|
-
if (
|
|
189
|
-
if (!
|
|
190
|
-
if (!
|
|
817
|
+
if (ts2.isAwaitExpression(node)) node = node.parent;
|
|
818
|
+
if (!ts2.isVariableDeclaration(node)) return null;
|
|
819
|
+
if (!ts2.isIdentifier(node.name)) return null;
|
|
191
820
|
const initializer = node.initializer;
|
|
192
821
|
if (!initializer) return null;
|
|
193
822
|
const inner = unwrapExpression(initializer);
|
|
@@ -197,18 +826,18 @@ var resultBindingForCall = (call) => {
|
|
|
197
826
|
var argIdentifiersForCall = (call) => {
|
|
198
827
|
const identifiers = [];
|
|
199
828
|
call.arguments?.forEach((argument, index) => {
|
|
200
|
-
if (
|
|
829
|
+
if (ts2.isIdentifier(argument)) {
|
|
201
830
|
identifiers.push({ index, kind: "identifier", name: argument.text });
|
|
202
831
|
return;
|
|
203
832
|
}
|
|
204
|
-
if (!
|
|
833
|
+
if (!ts2.isObjectLiteralExpression(argument)) return;
|
|
205
834
|
const properties = [];
|
|
206
835
|
for (const property of argument.properties) {
|
|
207
|
-
if (
|
|
836
|
+
if (ts2.isShorthandPropertyAssignment(property)) {
|
|
208
837
|
properties.push({ key: property.name.text, name: property.name.text });
|
|
209
838
|
continue;
|
|
210
839
|
}
|
|
211
|
-
if (
|
|
840
|
+
if (ts2.isPropertyAssignment(property) && ts2.isIdentifier(property.name) && ts2.isIdentifier(property.initializer)) {
|
|
212
841
|
properties.push({ key: property.name.text, name: property.initializer.text });
|
|
213
842
|
}
|
|
214
843
|
}
|
|
@@ -248,8 +877,8 @@ var collectCallSite = (call, caller, context) => {
|
|
|
248
877
|
};
|
|
249
878
|
};
|
|
250
879
|
var collectParamDestructure = (declaration, caller, context) => {
|
|
251
|
-
if (!declaration.initializer || !
|
|
252
|
-
if (!
|
|
880
|
+
if (!declaration.initializer || !ts2.isIdentifier(declaration.initializer)) return null;
|
|
881
|
+
if (!ts2.isObjectBindingPattern(declaration.name) && !ts2.isArrayBindingPattern(declaration.name)) {
|
|
253
882
|
return null;
|
|
254
883
|
}
|
|
255
884
|
const sourceParam = declaration.initializer.text;
|
|
@@ -273,33 +902,33 @@ var collectParamDestructure = (declaration, caller, context) => {
|
|
|
273
902
|
|
|
274
903
|
// src/auto-instrumentation/transform-source.ts
|
|
275
904
|
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
|
|
905
|
+
if (/\.tsx$/i.test(filePath)) return ts3.ScriptKind.TSX;
|
|
906
|
+
if (/\.jsx$/i.test(filePath)) return ts3.ScriptKind.JSX;
|
|
907
|
+
if (/\.[cm]?ts$/i.test(filePath)) return ts3.ScriptKind.TS;
|
|
908
|
+
return ts3.ScriptKind.JS;
|
|
280
909
|
};
|
|
281
910
|
var functionName = (node, sourceFile) => {
|
|
282
911
|
if (node.name) return node.name.getText(sourceFile).replace(/^['"]|['"]$/g, "");
|
|
283
912
|
const parent = node.parent;
|
|
284
|
-
if (
|
|
285
|
-
if (
|
|
286
|
-
if (
|
|
913
|
+
if (ts3.isVariableDeclaration(parent) && ts3.isIdentifier(parent.name)) return parent.name.text;
|
|
914
|
+
if (ts3.isPropertyAssignment(parent)) return parent.name.getText(sourceFile);
|
|
915
|
+
if (ts3.isPropertyDeclaration(parent) && parent.name) return parent.name.getText(sourceFile);
|
|
287
916
|
const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
288
917
|
return `anonymous@${location.line + 1}:${location.character + 1}`;
|
|
289
918
|
};
|
|
290
919
|
var functionLocationPosition = (node, sourceFile) => {
|
|
291
920
|
if (node.name) return node.name.getStart(sourceFile);
|
|
292
921
|
const parent = node.parent;
|
|
293
|
-
if ((
|
|
922
|
+
if ((ts3.isVariableDeclaration(parent) || ts3.isPropertyAssignment(parent)) && parent.name) {
|
|
294
923
|
return parent.name.getStart(sourceFile);
|
|
295
924
|
}
|
|
296
925
|
return node.getStart(sourceFile);
|
|
297
926
|
};
|
|
298
927
|
var bindingValue = (name, sourceFile) => {
|
|
299
|
-
if (
|
|
300
|
-
if (
|
|
928
|
+
if (ts3.isIdentifier(name)) return name.text;
|
|
929
|
+
if (ts3.isArrayBindingPattern(name)) {
|
|
301
930
|
const values = name.elements.map((element) => {
|
|
302
|
-
if (
|
|
931
|
+
if (ts3.isOmittedExpression(element)) return "";
|
|
303
932
|
const value = bindingValue(element.name, sourceFile);
|
|
304
933
|
return element.dotDotDotToken ? `...${value}` : value;
|
|
305
934
|
});
|
|
@@ -315,14 +944,14 @@ var bindingValue = (name, sourceFile) => {
|
|
|
315
944
|
};
|
|
316
945
|
var arrowArguments = (node) => {
|
|
317
946
|
const values = node.parameters.flatMap((parameter) => {
|
|
318
|
-
if (
|
|
947
|
+
if (ts3.isIdentifier(parameter.name) && parameter.name.text === "this") return [];
|
|
319
948
|
const value = bindingValue(parameter.name, node.getSourceFile());
|
|
320
949
|
return [parameter.dotDotDotToken ? `...${value}` : value];
|
|
321
950
|
});
|
|
322
951
|
return `[${values.join(",")}]`;
|
|
323
952
|
};
|
|
324
953
|
var isSupportedFunction = (node) => {
|
|
325
|
-
if (!
|
|
954
|
+
if (!ts3.isFunctionDeclaration(node) && !ts3.isFunctionExpression(node) && !ts3.isArrowFunction(node) && !ts3.isMethodDeclaration(node) && !ts3.isGetAccessorDeclaration(node) && !ts3.isSetAccessorDeclaration(node)) {
|
|
326
955
|
return false;
|
|
327
956
|
}
|
|
328
957
|
if (!node.body || "asteriskToken" in node && node.asteriskToken) return false;
|
|
@@ -333,7 +962,7 @@ var uniqueName = (source, base) => {
|
|
|
333
962
|
while (source.includes(name)) name += "_";
|
|
334
963
|
return name;
|
|
335
964
|
};
|
|
336
|
-
var isDirectivePrologue = (statement) =>
|
|
965
|
+
var isDirectivePrologue = (statement) => ts3.isExpressionStatement(statement) && ts3.isStringLiteral(statement.expression);
|
|
337
966
|
var insertPositionAfterPrologue = (source, sourceFile) => {
|
|
338
967
|
const shebangEnd = source.startsWith("#!") ? source.indexOf("\n") + 1 : 0;
|
|
339
968
|
let end = shebangEnd;
|
|
@@ -348,7 +977,7 @@ var preambleInsert = (source, position, preamble) => {
|
|
|
348
977
|
const previous = source[position - 1];
|
|
349
978
|
return previous === ";" || previous === "\n" || previous === "\r" ? preamble : `;${preamble}`;
|
|
350
979
|
};
|
|
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)}));`;
|
|
980
|
+
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
981
|
var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
353
982
|
const start = mappedLocation(
|
|
354
983
|
sourceFile,
|
|
@@ -379,80 +1008,127 @@ var collectWrappedFunction = (node, sourceFile, absolutePath, mapLocation) => {
|
|
|
379
1008
|
paramNames
|
|
380
1009
|
};
|
|
381
1010
|
};
|
|
1011
|
+
var canOmitCallValues = (analysis, analyzed) => {
|
|
1012
|
+
if (!functionIsExact(analysis.plan) || analysis.hasThrow || analysis.hasCoercionGuard) {
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
const callers = analyzed.filter(
|
|
1016
|
+
(caller) => caller.callEdges.some((edge) => edge.calleeFunctionId === analysis.functionId)
|
|
1017
|
+
);
|
|
1018
|
+
if (callers.length === 0) return false;
|
|
1019
|
+
return callers.every((caller) => caller.plan.fallbackReason === void 0 && !caller.hasThrow);
|
|
1020
|
+
};
|
|
382
1021
|
var transformSource = (source, options) => {
|
|
383
1022
|
if (source.includes("@rasputin-ignore-file")) return void 0;
|
|
384
|
-
const sourceFile =
|
|
1023
|
+
const sourceFile = ts3.createSourceFile(
|
|
385
1024
|
options.filePath,
|
|
386
1025
|
source,
|
|
387
|
-
|
|
1026
|
+
ts3.ScriptTarget.Latest,
|
|
388
1027
|
true,
|
|
389
1028
|
scriptKindFor(options.filePath)
|
|
390
1029
|
);
|
|
391
1030
|
const runName = uniqueName(source, "__rasputinRun__");
|
|
1031
|
+
const capName = uniqueName(source, "__rasputinCap__");
|
|
392
1032
|
const plansName = uniqueName(source, "__rasputinPlans__");
|
|
393
1033
|
const absolutePath = resolve3(options.filePath);
|
|
1034
|
+
const derivationMode = options.derivationMode ?? "analysis";
|
|
394
1035
|
const edits = [];
|
|
395
1036
|
let order = 0;
|
|
396
1037
|
const delta = {
|
|
397
1038
|
functions: [],
|
|
398
1039
|
callSites: [],
|
|
399
|
-
destructures: []
|
|
1040
|
+
destructures: [],
|
|
1041
|
+
derivationPlans: []
|
|
400
1042
|
};
|
|
401
1043
|
const collectContext = {
|
|
402
1044
|
sourceFile,
|
|
403
1045
|
mapLocation: options.mapLocation,
|
|
404
1046
|
absolutePath
|
|
405
1047
|
};
|
|
1048
|
+
const pending = [];
|
|
406
1049
|
const visit = (node, caller) => {
|
|
407
1050
|
if (isSupportedFunction(node)) {
|
|
408
1051
|
const collected = collectWrappedFunction(node, sourceFile, absolutePath, options.mapLocation);
|
|
409
1052
|
if (!collected) {
|
|
410
|
-
|
|
1053
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
411
1054
|
return;
|
|
412
1055
|
}
|
|
413
|
-
|
|
1056
|
+
pending.push({ node, collected });
|
|
414
1057
|
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));
|
|
1058
|
+
ts3.forEachChild(node, (child) => visit(child, collected));
|
|
435
1059
|
return;
|
|
436
1060
|
}
|
|
437
|
-
if (caller && (
|
|
1061
|
+
if (caller && (ts3.isCallExpression(node) || ts3.isNewExpression(node))) {
|
|
438
1062
|
const site = collectCallSite(node, caller, collectContext);
|
|
439
1063
|
if (site) delta.callSites.push(site);
|
|
440
1064
|
}
|
|
441
|
-
if (caller &&
|
|
1065
|
+
if (caller && ts3.isVariableDeclaration(node)) {
|
|
442
1066
|
const destructure = collectParamDestructure(node, caller, collectContext);
|
|
443
1067
|
if (destructure) delta.destructures.push(destructure);
|
|
444
1068
|
}
|
|
445
|
-
|
|
1069
|
+
ts3.forEachChild(node, (child) => visit(child, caller));
|
|
446
1070
|
};
|
|
447
1071
|
visit(sourceFile);
|
|
448
|
-
if (
|
|
1072
|
+
if (pending.length === 0) return void 0;
|
|
1073
|
+
const analyzed = analyzeModuleFunctions(
|
|
1074
|
+
sourceFile,
|
|
1075
|
+
pending.map((entry) => ({
|
|
1076
|
+
functionId: entry.collected.functionId,
|
|
1077
|
+
name: entry.collected.name,
|
|
1078
|
+
node: entry.node
|
|
1079
|
+
}))
|
|
1080
|
+
);
|
|
1081
|
+
delta.derivationPlans = analyzed.map((fn) => fn.plan);
|
|
1082
|
+
const analyzedById = new Map(analyzed.map((fn) => [fn.functionId, fn]));
|
|
1083
|
+
for (const [planIndex, entry] of pending.entries()) {
|
|
1084
|
+
const analysis = analyzedById.get(entry.collected.functionId);
|
|
1085
|
+
const omitValues = derivationMode === "selective" && analysis && canOmitCallValues(analysis, analyzed);
|
|
1086
|
+
const valueFlag = omitValues ? ",1" : "";
|
|
1087
|
+
const args = ts3.isArrowFunction(entry.node) ? arrowArguments(entry.node) : "arguments";
|
|
1088
|
+
const asyncCallback = entry.node.modifiers?.some(
|
|
1089
|
+
(modifier) => modifier.kind === ts3.SyntaxKind.AsyncKeyword
|
|
1090
|
+
) ? "async " : "";
|
|
1091
|
+
if (ts3.isBlock(entry.node.body)) {
|
|
1092
|
+
edits.push({
|
|
1093
|
+
position: entry.node.body.getStart(sourceFile) + 1,
|
|
1094
|
+
text: `return ${runName}(${planIndex},${args},${asyncCallback}()=>{`,
|
|
1095
|
+
order: order++
|
|
1096
|
+
});
|
|
1097
|
+
edits.push({
|
|
1098
|
+
position: entry.node.body.end - 1,
|
|
1099
|
+
text: `}${valueFlag});`,
|
|
1100
|
+
order: order++
|
|
1101
|
+
});
|
|
1102
|
+
} else {
|
|
1103
|
+
edits.push({
|
|
1104
|
+
position: entry.node.body.getStart(sourceFile),
|
|
1105
|
+
text: `${runName}(${planIndex},${args},${asyncCallback}()=>(`,
|
|
1106
|
+
order: order++
|
|
1107
|
+
});
|
|
1108
|
+
edits.push({
|
|
1109
|
+
position: entry.node.body.end,
|
|
1110
|
+
text: `)${valueFlag})`,
|
|
1111
|
+
order: order++
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
if (analysis) {
|
|
1115
|
+
for (const edit of captureWrapEdits(analysis.captureWraps, capName)) {
|
|
1116
|
+
edits.push({ ...edit, order: order++ });
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
449
1120
|
const insertAt = insertPositionAfterPrologue(source, sourceFile);
|
|
450
1121
|
edits.push({
|
|
451
1122
|
position: insertAt,
|
|
452
1123
|
text: preambleInsert(
|
|
453
1124
|
source,
|
|
454
1125
|
insertAt,
|
|
455
|
-
modulePreamble(
|
|
1126
|
+
modulePreamble(
|
|
1127
|
+
runName,
|
|
1128
|
+
capName,
|
|
1129
|
+
plansName,
|
|
1130
|
+
JSON.stringify(delta.functions.map((fn) => fn.plan))
|
|
1131
|
+
)
|
|
456
1132
|
),
|
|
457
1133
|
order: -1
|
|
458
1134
|
});
|