@spotpatch/dev-server 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  createAgentJobManager: () => createAgentJobManager,
37
37
  createIntegrationFileChange: () => createIntegrationFileChange,
38
38
  createRuntimeAiConfig: () => createRuntimeAiConfig,
39
+ createRuntimeDataFlowConfig: () => createRuntimeDataFlowConfig,
39
40
  createSession: () => createSession,
40
41
  createSourceRegistrationService: () => createSourceRegistrationService,
41
42
  createSourceRegistry: () => createSourceRegistry,
@@ -252,7 +253,11 @@ function createAgentJobManager(options) {
252
253
  }
253
254
  };
254
255
  const applyChange = async (job, preparedChange) => {
255
- transition(job, "applying", "Applying validated changes to the project.");
256
+ transition(
257
+ job,
258
+ "applying",
259
+ job.applyMode === "trusted-auto" ? "Applying trusted change directly to the project." : "Applying validated changes to the project."
260
+ );
256
261
  try {
257
262
  await dependencies.applyChange(preparedChange);
258
263
  transition(job, "applied", "Changes were applied to local project files.");
@@ -291,11 +296,15 @@ function createAgentJobManager(options) {
291
296
  );
292
297
  }
293
298
  };
299
+ const execution = Object.freeze({
300
+ ...options.ai.execution,
301
+ applyMode: job.applyMode
302
+ });
294
303
  const preparedChange = await dependencies.executeChange({
295
304
  annotation: job.annotation,
296
305
  callbacks,
297
306
  credential: job.credential,
298
- execution: options.ai.execution,
307
+ execution,
299
308
  jobId: job.id,
300
309
  model: job.model,
301
310
  provider: job.provider,
@@ -777,7 +786,7 @@ var DEFAULT_EXCLUDE = Object.freeze([
777
786
  /(?:^|\/)dist(?:\/|$)/,
778
787
  /(?:^|\/)coverage(?:\/|$)/
779
788
  ]);
780
- var DEFAULT_INCLUDE = Object.freeze([/(?:^|[/\\])src[/\\].+\.(?:jsx|tsx)$/]);
789
+ var DEFAULT_INCLUDE = Object.freeze([/(?:^|[/\\])src[/\\].+\.(?:js|jsx|ts|tsx)$/]);
781
790
  var DEFAULT_BUDGET = Object.freeze({
782
791
  totalCharacters: 16e3,
783
792
  domCharacters: 3e3,
@@ -798,7 +807,12 @@ var DEFAULT_OPTIONS = Object.freeze({
798
807
  debug: false,
799
808
  locale: "auto",
800
809
  maxTargets: 8,
801
- ai: false
810
+ ai: false,
811
+ dataFlow: Object.freeze({
812
+ enabled: false,
813
+ runtime: "dispatch",
814
+ limits: import_shared2.DEFAULT_DATA_FLOW_LIMITS
815
+ })
802
816
  });
803
817
  var PROFILE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
804
818
  var ENV_NAME_PATTERN = /^[A-Z][A-Z0-9_]{1,127}$/;
@@ -1090,6 +1104,36 @@ function assertPositiveBudget(budget) {
1090
1104
  }
1091
1105
  }
1092
1106
  }
1107
+ function resolveDataFlowOptions(options) {
1108
+ if (options === void 0 || options === false) {
1109
+ return DEFAULT_OPTIONS.dataFlow;
1110
+ }
1111
+ const candidate = options;
1112
+ if (typeof candidate !== "object" || candidate === null) {
1113
+ throw new RangeError("SpotPatch dataFlow configuration is invalid.");
1114
+ }
1115
+ const runtime = options.runtime ?? "dispatch";
1116
+ if (runtime !== "dispatch") {
1117
+ throw new RangeError("SpotPatch dataFlow runtime mode is invalid.");
1118
+ }
1119
+ return Object.freeze({
1120
+ enabled: true,
1121
+ runtime,
1122
+ limits: import_shared2.DEFAULT_DATA_FLOW_LIMITS
1123
+ });
1124
+ }
1125
+ function createRuntimeDataFlowConfig(options) {
1126
+ return Object.freeze({
1127
+ enabled: options.enabled,
1128
+ runtime: options.runtime,
1129
+ limits: Object.freeze({
1130
+ observationMaxEntries: options.limits.observationMaxEntries,
1131
+ observationMaxBytes: options.limits.observationMaxBytes,
1132
+ observationTtlMs: options.limits.observationTtlMs,
1133
+ reportMaxBytes: options.limits.reportMaxBytes
1134
+ })
1135
+ });
1136
+ }
1093
1137
  function resolveOptions(options = {}, environmentAi) {
1094
1138
  if (options.trustedFastMode !== void 0 && typeof options.trustedFastMode !== "boolean") {
1095
1139
  throw new RangeError("SpotPatch trustedFastMode must be a boolean.");
@@ -1125,7 +1169,8 @@ function resolveOptions(options = {}, environmentAi) {
1125
1169
  debug: options.debug ?? DEFAULT_OPTIONS.debug,
1126
1170
  locale,
1127
1171
  maxTargets,
1128
- ai: resolveAiOptions(options.ai ?? environmentAi)
1172
+ ai: resolveAiOptions(options.ai ?? environmentAi),
1173
+ dataFlow: resolveDataFlowOptions(options.dataFlow)
1129
1174
  };
1130
1175
  if (resolved.shortcut.trim().length === 0 || resolved.shortcut.length > 128 || resolved.shortcut.includes("\0")) {
1131
1176
  throw new RangeError("SpotPatch shortcut is invalid.");
@@ -1311,33 +1356,58 @@ function createSourceRegistry(options = {}) {
1311
1356
  const createId = options.createId ?? createRandomSourceId;
1312
1357
  const pathToId = /* @__PURE__ */ new Map();
1313
1358
  const idToPath = /* @__PURE__ */ new Map();
1359
+ const componentAnchors = /* @__PURE__ */ new Map();
1360
+ const componentIdsByPath = /* @__PURE__ */ new Map();
1361
+ function registerSourcePath(absolutePath) {
1362
+ const normalizedPath = normalizeAbsolutePath(absolutePath);
1363
+ const existingId = pathToId.get(normalizedPath);
1364
+ if (existingId !== void 0) {
1365
+ return existingId;
1366
+ }
1367
+ let fileId = createId();
1368
+ while (idToPath.has(fileId)) fileId = createId();
1369
+ pathToId.set(normalizedPath, fileId);
1370
+ idToPath.set(fileId, normalizedPath);
1371
+ return fileId;
1372
+ }
1314
1373
  return Object.freeze({
1315
1374
  register(absolutePath) {
1375
+ return registerSourcePath(absolutePath);
1376
+ },
1377
+ registerDataFlowComponents(absolutePath, sourceVersion, components) {
1316
1378
  const normalizedPath = normalizeAbsolutePath(absolutePath);
1317
- const existingId = pathToId.get(normalizedPath);
1318
- if (existingId !== void 0) {
1319
- return existingId;
1379
+ const previousIds = componentIdsByPath.get(normalizedPath);
1380
+ for (const componentSourceId of previousIds ?? []) {
1381
+ componentAnchors.delete(componentSourceId);
1320
1382
  }
1321
- let fileId = createId();
1322
- while (idToPath.has(fileId)) {
1323
- fileId = createId();
1383
+ const fileId = registerSourcePath(normalizedPath);
1384
+ const currentIds = /* @__PURE__ */ new Set();
1385
+ for (const component of components) {
1386
+ currentIds.add(component.componentSourceId);
1387
+ componentAnchors.set(
1388
+ component.componentSourceId,
1389
+ Object.freeze({ ...component, fileId, sourceVersion })
1390
+ );
1324
1391
  }
1325
- pathToId.set(normalizedPath, fileId);
1326
- idToPath.set(fileId, normalizedPath);
1327
- return fileId;
1392
+ componentIdsByPath.set(normalizedPath, currentIds);
1328
1393
  },
1329
1394
  resolve(fileId) {
1330
1395
  return idToPath.get(fileId);
1331
1396
  },
1397
+ resolveDataFlowComponent(componentSourceId) {
1398
+ return componentAnchors.get(componentSourceId);
1399
+ },
1332
1400
  clear() {
1333
1401
  pathToId.clear();
1334
1402
  idToPath.clear();
1403
+ componentAnchors.clear();
1404
+ componentIdsByPath.clear();
1335
1405
  }
1336
1406
  });
1337
1407
  }
1338
1408
 
1339
1409
  // src/server/middleware.ts
1340
- var import_shared10 = require("@spotpatch/shared");
1410
+ var import_shared11 = require("@spotpatch/shared");
1341
1411
 
1342
1412
  // src/server/agent-http.ts
1343
1413
  var import_shared7 = require("@spotpatch/shared");
@@ -2150,10 +2220,177 @@ function createEditorLauncher(dependencies = DEFAULT_DEPENDENCIES2) {
2150
2220
  }
2151
2221
  var launchConfiguredEditor = createEditorLauncher();
2152
2222
 
2153
- // src/server/request-security.ts
2223
+ // src/server/data-flow-http.ts
2154
2224
  var import_node_crypto4 = require("crypto");
2155
- var import_node_net = require("net");
2225
+ var import_analyzer = require("@spotpatch/analyzer");
2156
2226
  var import_shared8 = require("@spotpatch/shared");
2227
+ function envelopeBytes(report) {
2228
+ return Buffer.byteLength(JSON.stringify({ ok: true, data: report }), "utf8");
2229
+ }
2230
+ function limitDataFlowReportToBytes(report, maximumBytes) {
2231
+ const structurallyLimited = (0, import_shared8.limitDataFlowReportCollections)(report);
2232
+ if (envelopeBytes(structurallyLimited) <= maximumBytes) {
2233
+ return structurallyLimited;
2234
+ }
2235
+ let limited = (0, import_shared8.limitDataFlowReportCollections)(structurallyLimited, {
2236
+ forceTruncation: true,
2237
+ maximumDependencies: 0,
2238
+ truncatedBy: "bytes"
2239
+ });
2240
+ if (envelopeBytes(limited) > maximumBytes) {
2241
+ throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.INTERNAL_ERROR);
2242
+ }
2243
+ for (let maximumDependencies = 1; maximumDependencies <= structurallyLimited.dependencies.length; maximumDependencies += 1) {
2244
+ const candidate = (0, import_shared8.limitDataFlowReportCollections)(structurallyLimited, {
2245
+ forceTruncation: true,
2246
+ maximumDependencies,
2247
+ truncatedBy: "bytes"
2248
+ });
2249
+ if (envelopeBytes(candidate) > maximumBytes) break;
2250
+ limited = candidate;
2251
+ }
2252
+ return limited;
2253
+ }
2254
+ function createDataFlowAnalyzer(options) {
2255
+ if (!options.options.dataFlow.enabled) return void 0;
2256
+ return (0, import_analyzer.createStaticDataFlowAnalyzer)({
2257
+ root: options.root,
2258
+ registryEpoch: options.session.id,
2259
+ registerSource: (absolutePath) => options.registry.register(absolutePath),
2260
+ limits: options.options.dataFlow.limits
2261
+ });
2262
+ }
2263
+ async function analyzeTarget(request, analyzer, options) {
2264
+ const resolvedRequest = (() => {
2265
+ if ("componentSourceId" in request) {
2266
+ const anchor = options.registry.resolveDataFlowComponent(
2267
+ request.componentSourceId
2268
+ );
2269
+ if (anchor?.sourceVersion !== request.sourceVersion) {
2270
+ throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.DATA_FLOW_SOURCE_STALE);
2271
+ }
2272
+ return anchor;
2273
+ }
2274
+ return request;
2275
+ })();
2276
+ const absolutePath = await resolveSourceFile({
2277
+ fileId: resolvedRequest.fileId,
2278
+ registry: options.registry,
2279
+ root: options.root
2280
+ });
2281
+ const report = analyzer.analyzeComponent({
2282
+ absolutePath,
2283
+ line: resolvedRequest.line,
2284
+ column: resolvedRequest.column
2285
+ });
2286
+ if (resolvedRequest.sourceVersion !== void 0 && resolvedRequest.sourceVersion !== report.component.source.sourceVersion) {
2287
+ throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.DATA_FLOW_SOURCE_STALE);
2288
+ }
2289
+ return limitDataFlowReportToBytes(
2290
+ report,
2291
+ options.options.dataFlow.limits.reportMaxBytes
2292
+ );
2293
+ }
2294
+ function requireAnalyzer(analyzer) {
2295
+ if (analyzer === void 0) {
2296
+ throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.DATA_FLOW_DISABLED);
2297
+ }
2298
+ return analyzer;
2299
+ }
2300
+ async function handleComponentDataFlowReport(request, analyzer, options) {
2301
+ const parsed = import_shared8.dataFlowComponentReportRequestSchema.safeParse(
2302
+ await readJsonRequestBody(
2303
+ request,
2304
+ options.options.dataFlow.limits.protocolRequestMaxBytes
2305
+ )
2306
+ );
2307
+ if (!parsed.success) {
2308
+ throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.INVALID_REQUEST);
2309
+ }
2310
+ return analyzeTarget(parsed.data, requireAnalyzer(analyzer), options);
2311
+ }
2312
+ async function handlePageDataFlowReport(request, analyzer, options) {
2313
+ const parsed = import_shared8.dataFlowPageReportRequestSchema.safeParse(
2314
+ await readJsonRequestBody(
2315
+ request,
2316
+ options.options.dataFlow.limits.protocolRequestMaxBytes
2317
+ )
2318
+ );
2319
+ if (!parsed.success) {
2320
+ throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.INVALID_REQUEST);
2321
+ }
2322
+ const activeAnalyzer = requireAnalyzer(analyzer);
2323
+ const componentReports = await Promise.all(
2324
+ parsed.data.targets.map((target) => analyzeTarget(target, activeAnalyzer, options))
2325
+ );
2326
+ const dependencies = new Map(
2327
+ componentReports.flatMap(
2328
+ (report2) => report2.dependencies.map((dependency) => [dependency.id, dependency])
2329
+ )
2330
+ );
2331
+ const evidence = new Map(
2332
+ componentReports.flatMap(
2333
+ (report2) => report2.evidence.map((entry) => [entry.id, entry])
2334
+ )
2335
+ );
2336
+ const diagnostics = new Map(
2337
+ componentReports.flatMap(
2338
+ (report2) => report2.diagnostics.map((entry) => [entry.id, entry])
2339
+ )
2340
+ );
2341
+ const analyzedVersions = new Set(
2342
+ componentReports.flatMap((report2) => report2.baseline.analyzedSourceVersions)
2343
+ );
2344
+ const reportId = `page_${(0, import_node_crypto4.createHash)("sha256").update(componentReports.map((report2) => report2.reportId).join("\0")).digest("base64url").slice(0, 22)}`;
2345
+ const complete = componentReports.every((report2) => report2.completeness.complete);
2346
+ const report = Object.freeze({
2347
+ schemaVersion: import_shared8.DATA_FLOW_SCHEMA_VERSION,
2348
+ reportId,
2349
+ baseline: Object.freeze({
2350
+ registryEpoch: options.session.id,
2351
+ analyzerVersion: componentReports[0]?.baseline.analyzerVersion ?? "unavailable",
2352
+ adapterSetHash: componentReports[0]?.baseline.adapterSetHash ?? "unavailable",
2353
+ analyzedSourceVersions: Object.freeze([...analyzedVersions].sort())
2354
+ }),
2355
+ capability: Object.freeze({
2356
+ enabled: true,
2357
+ staticAnalysis: complete ? "available" : "partial",
2358
+ runtimeObservation: "dispatch-only",
2359
+ responseShape: "consumed-fields-only",
2360
+ aiAssistance: "disabled",
2361
+ reasons: Object.freeze(
2362
+ componentReports.flatMap((report2) => report2.capability.reasons)
2363
+ )
2364
+ }),
2365
+ dependencies: Object.freeze([...dependencies.values()]),
2366
+ evidence: Object.freeze([...evidence.values()]),
2367
+ diagnostics: Object.freeze([...diagnostics.values()]),
2368
+ completeness: Object.freeze({
2369
+ complete,
2370
+ visitedModules: componentReports.reduce(
2371
+ (total, report2) => total + report2.completeness.visitedModules,
2372
+ 0
2373
+ ),
2374
+ visitedCallsites: componentReports.reduce(
2375
+ (total, report2) => total + report2.completeness.visitedCallsites,
2376
+ 0
2377
+ ),
2378
+ frontierCount: componentReports.reduce(
2379
+ (total, report2) => total + report2.completeness.frontierCount,
2380
+ 0
2381
+ )
2382
+ })
2383
+ });
2384
+ return limitDataFlowReportToBytes(
2385
+ report,
2386
+ options.options.dataFlow.limits.reportMaxBytes
2387
+ );
2388
+ }
2389
+
2390
+ // src/server/request-security.ts
2391
+ var import_node_crypto5 = require("crypto");
2392
+ var import_node_net = require("net");
2393
+ var import_shared9 = require("@spotpatch/shared");
2157
2394
  function getSingleHeader(request, name) {
2158
2395
  const value = request.headers[name.toLowerCase()];
2159
2396
  return Array.isArray(value) ? value[0] : value;
@@ -2164,7 +2401,7 @@ function tokensMatch(actual, expected) {
2164
2401
  }
2165
2402
  const actualBytes = Buffer.from(actual);
2166
2403
  const expectedBytes = Buffer.from(expected);
2167
- return actualBytes.byteLength === expectedBytes.byteLength && (0, import_node_crypto4.timingSafeEqual)(actualBytes, expectedBytes);
2404
+ return actualBytes.byteLength === expectedBytes.byteLength && (0, import_node_crypto5.timingSafeEqual)(actualBytes, expectedBytes);
2168
2405
  }
2169
2406
  function isLoopbackHostname(hostname) {
2170
2407
  const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
@@ -2198,32 +2435,32 @@ function parseOrigin(value) {
2198
2435
  }
2199
2436
  }
2200
2437
  function assertRequestAuthorized(request, options) {
2201
- const actualToken = getSingleHeader(request, import_shared8.SPOTPATCH_TOKEN_HEADER);
2438
+ const actualToken = getSingleHeader(request, import_shared9.SPOTPATCH_TOKEN_HEADER);
2202
2439
  if (!tokensMatch(actualToken, options.sessionToken)) {
2203
- throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.INVALID_TOKEN);
2440
+ throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.INVALID_TOKEN);
2204
2441
  }
2205
2442
  const hostHeader = getSingleHeader(request, "host");
2206
2443
  const originHeader = getSingleHeader(request, "origin");
2207
2444
  const host = hostHeader === void 0 ? void 0 : parseHost(hostHeader);
2208
2445
  const origin = originHeader === void 0 ? void 0 : parseOrigin(originHeader);
2209
2446
  if (host === void 0 || origin === void 0) {
2210
- throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.ORIGIN_NOT_ALLOWED);
2447
+ throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.ORIGIN_NOT_ALLOWED);
2211
2448
  }
2212
2449
  const hostIsLoopback = isLoopbackHostname(host.hostname);
2213
2450
  const originIsLoopback = isLoopbackHostname(origin.hostname);
2214
2451
  if (!options.allowLan) {
2215
2452
  if (!hostIsLoopback || !originIsLoopback) {
2216
- throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.ORIGIN_NOT_ALLOWED);
2453
+ throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.ORIGIN_NOT_ALLOWED);
2217
2454
  }
2218
2455
  return;
2219
2456
  }
2220
2457
  if (!originIsLoopback && origin.host.toLowerCase() !== host.host.toLowerCase()) {
2221
- throw new import_shared8.SpotPatchError(import_shared8.ERROR_CODES.ORIGIN_NOT_ALLOWED);
2458
+ throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.ORIGIN_NOT_ALLOWED);
2222
2459
  }
2223
2460
  }
2224
2461
 
2225
2462
  // src/server/runtime-bootstrap.ts
2226
- var import_shared9 = require("@spotpatch/shared");
2463
+ var import_shared10 = require("@spotpatch/shared");
2227
2464
  function getSingleHeader2(request, name) {
2228
2465
  const value = request.headers[name.toLowerCase()];
2229
2466
  return Array.isArray(value) ? value[0] : value;
@@ -2238,7 +2475,7 @@ function resolveRuntimeBootstrapOptions(options) {
2238
2475
  if (expectedOrigin.origin !== options.expectedOrigin || expectedOrigin.protocol !== "http:" || !isLoopbackHostname(expectedOrigin.hostname)) {
2239
2476
  throw new TypeError("The SpotPatch bootstrap origin must be a loopback origin.");
2240
2477
  }
2241
- const parsedConfig = import_shared9.runtimeConfigSchema.safeParse(options.runtimeConfig);
2478
+ const parsedConfig = import_shared10.runtimeConfigSchema.safeParse(options.runtimeConfig);
2242
2479
  if (!parsedConfig.success) {
2243
2480
  throw new TypeError("The SpotPatch Runtime configuration is invalid.");
2244
2481
  }
@@ -2250,7 +2487,7 @@ function resolveRuntimeBootstrapOptions(options) {
2250
2487
  function assertRuntimeBootstrapRequest(request, expectedOrigin) {
2251
2488
  const contentType = getSingleHeader2(request, "content-type")?.split(";", 1)[0]?.trim().toLowerCase();
2252
2489
  if (request.method !== "POST" || contentType !== "application/json") {
2253
- throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.INVALID_REQUEST);
2490
+ throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
2254
2491
  }
2255
2492
  const host = getSingleHeader2(request, "host");
2256
2493
  let hostIsLoopback = false;
@@ -2262,90 +2499,96 @@ function assertRuntimeBootstrapRequest(request, expectedOrigin) {
2262
2499
  }
2263
2500
  }
2264
2501
  if (!hostIsLoopback || getSingleHeader2(request, "origin") !== expectedOrigin || getSingleHeader2(request, "sec-fetch-site") !== "same-origin") {
2265
- throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.ORIGIN_NOT_ALLOWED);
2502
+ throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.ORIGIN_NOT_ALLOWED);
2266
2503
  }
2267
2504
  }
2268
2505
  async function readRuntimeBootstrap(request, options) {
2269
2506
  assertRuntimeBootstrapRequest(request, options.expectedOrigin);
2270
- const parsedBody = import_shared9.runtimeBootstrapRequestSchema.safeParse(
2507
+ const parsedBody = import_shared10.runtimeBootstrapRequestSchema.safeParse(
2271
2508
  await readJsonRequestBody(request)
2272
2509
  );
2273
2510
  if (!parsedBody.success) {
2274
- throw new import_shared9.SpotPatchError(import_shared9.ERROR_CODES.INVALID_REQUEST);
2511
+ throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
2275
2512
  }
2276
2513
  return options.runtimeConfig;
2277
2514
  }
2278
2515
 
2279
2516
  // src/server/middleware.ts
2280
2517
  var STATUS_BY_ERROR = Object.freeze({
2281
- [import_shared10.ERROR_CODES.INVALID_REQUEST]: 400,
2282
- [import_shared10.ERROR_CODES.INVALID_TOKEN]: 401,
2283
- [import_shared10.ERROR_CODES.ORIGIN_NOT_ALLOWED]: 403,
2284
- [import_shared10.ERROR_CODES.SOURCE_NOT_FOUND]: 404,
2285
- [import_shared10.ERROR_CODES.SOURCE_OUTSIDE_ROOT]: 403,
2286
- [import_shared10.ERROR_CODES.SOURCE_TOO_LARGE]: 413,
2287
- [import_shared10.ERROR_CODES.EDITOR_OPEN_FAILED]: 500,
2288
- [import_shared10.ERROR_CODES.AI_DISABLED]: 404,
2289
- [import_shared10.ERROR_CODES.PROVIDER_NOT_CONFIGURED]: 503,
2290
- [import_shared10.ERROR_CODES.PROVIDER_AUTH_FAILED]: 502,
2291
- [import_shared10.ERROR_CODES.PROVIDER_PROTOCOL_UNSUPPORTED]: 502,
2292
- [import_shared10.ERROR_CODES.MODEL_NOT_ALLOWED]: 400,
2293
- [import_shared10.ERROR_CODES.MODEL_TOOL_CALL_UNSUPPORTED]: 422,
2294
- [import_shared10.ERROR_CODES.PROVIDER_RATE_LIMITED]: 429,
2295
- [import_shared10.ERROR_CODES.AGENT_BUSY]: 409,
2296
- [import_shared10.ERROR_CODES.AGENT_LIMIT_EXCEEDED]: 413,
2297
- [import_shared10.ERROR_CODES.AGENT_CANCELLED]: 409,
2298
- [import_shared10.ERROR_CODES.WORKTREE_DIRTY]: 409,
2299
- [import_shared10.ERROR_CODES.WORKTREE_NOT_REPOSITORY]: 409,
2300
- [import_shared10.ERROR_CODES.WORKTREE_OPERATION_IN_PROGRESS]: 409,
2301
- [import_shared10.ERROR_CODES.WORKTREE_CONFLICTED]: 409,
2302
- [import_shared10.ERROR_CODES.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: 413,
2303
- [import_shared10.ERROR_CODES.WORKTREE_UNTRACKED_UNSUPPORTED]: 409,
2304
- [import_shared10.ERROR_CODES.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: 409,
2305
- [import_shared10.ERROR_CODES.TOOL_DENIED]: 403,
2306
- [import_shared10.ERROR_CODES.TOOL_INPUT_INVALID]: 422,
2307
- [import_shared10.ERROR_CODES.TOOL_ARGUMENTS_INVALID]: 422,
2308
- [import_shared10.ERROR_CODES.TOOL_CALL_ID_CONFLICT]: 422,
2309
- [import_shared10.ERROR_CODES.TOOL_PATH_DENIED]: 403,
2310
- [import_shared10.ERROR_CODES.PATCH_REJECTED]: 422,
2311
- [import_shared10.ERROR_CODES.VALIDATION_FAILED]: 422,
2312
- [import_shared10.ERROR_CODES.APPLY_CONFLICT]: 409,
2313
- [import_shared10.ERROR_CODES.INTERNAL_ERROR]: 500
2518
+ [import_shared11.ERROR_CODES.INVALID_REQUEST]: 400,
2519
+ [import_shared11.ERROR_CODES.INVALID_TOKEN]: 401,
2520
+ [import_shared11.ERROR_CODES.ORIGIN_NOT_ALLOWED]: 403,
2521
+ [import_shared11.ERROR_CODES.SOURCE_NOT_FOUND]: 404,
2522
+ [import_shared11.ERROR_CODES.SOURCE_OUTSIDE_ROOT]: 403,
2523
+ [import_shared11.ERROR_CODES.SOURCE_TOO_LARGE]: 413,
2524
+ [import_shared11.ERROR_CODES.EDITOR_OPEN_FAILED]: 500,
2525
+ [import_shared11.ERROR_CODES.DATA_FLOW_DISABLED]: 404,
2526
+ [import_shared11.ERROR_CODES.DATA_FLOW_SOURCE_STALE]: 409,
2527
+ [import_shared11.ERROR_CODES.DATA_FLOW_ANALYSIS_CANCELLED]: 409,
2528
+ [import_shared11.ERROR_CODES.AI_DISABLED]: 404,
2529
+ [import_shared11.ERROR_CODES.PROVIDER_NOT_CONFIGURED]: 503,
2530
+ [import_shared11.ERROR_CODES.PROVIDER_AUTH_FAILED]: 502,
2531
+ [import_shared11.ERROR_CODES.PROVIDER_PROTOCOL_UNSUPPORTED]: 502,
2532
+ [import_shared11.ERROR_CODES.MODEL_NOT_ALLOWED]: 400,
2533
+ [import_shared11.ERROR_CODES.MODEL_TOOL_CALL_UNSUPPORTED]: 422,
2534
+ [import_shared11.ERROR_CODES.PROVIDER_RATE_LIMITED]: 429,
2535
+ [import_shared11.ERROR_CODES.AGENT_BUSY]: 409,
2536
+ [import_shared11.ERROR_CODES.AGENT_LIMIT_EXCEEDED]: 413,
2537
+ [import_shared11.ERROR_CODES.AGENT_CANCELLED]: 409,
2538
+ [import_shared11.ERROR_CODES.WORKTREE_DIRTY]: 409,
2539
+ [import_shared11.ERROR_CODES.WORKTREE_NOT_REPOSITORY]: 409,
2540
+ [import_shared11.ERROR_CODES.WORKTREE_OPERATION_IN_PROGRESS]: 409,
2541
+ [import_shared11.ERROR_CODES.WORKTREE_CONFLICTED]: 409,
2542
+ [import_shared11.ERROR_CODES.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: 413,
2543
+ [import_shared11.ERROR_CODES.WORKTREE_UNTRACKED_UNSUPPORTED]: 409,
2544
+ [import_shared11.ERROR_CODES.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: 409,
2545
+ [import_shared11.ERROR_CODES.TOOL_DENIED]: 403,
2546
+ [import_shared11.ERROR_CODES.TOOL_INPUT_INVALID]: 422,
2547
+ [import_shared11.ERROR_CODES.TOOL_ARGUMENTS_INVALID]: 422,
2548
+ [import_shared11.ERROR_CODES.TOOL_CALL_ID_CONFLICT]: 422,
2549
+ [import_shared11.ERROR_CODES.TOOL_PATH_DENIED]: 403,
2550
+ [import_shared11.ERROR_CODES.PATCH_REJECTED]: 422,
2551
+ [import_shared11.ERROR_CODES.VALIDATION_FAILED]: 422,
2552
+ [import_shared11.ERROR_CODES.APPLY_CONFLICT]: 409,
2553
+ [import_shared11.ERROR_CODES.INTERNAL_ERROR]: 500
2314
2554
  });
2315
2555
  var PUBLIC_MESSAGES = Object.freeze({
2316
- [import_shared10.ERROR_CODES.INVALID_REQUEST]: "The request is invalid.",
2317
- [import_shared10.ERROR_CODES.INVALID_TOKEN]: "The session token is invalid.",
2318
- [import_shared10.ERROR_CODES.ORIGIN_NOT_ALLOWED]: "The request origin is not allowed.",
2319
- [import_shared10.ERROR_CODES.SOURCE_NOT_FOUND]: "The source file is unavailable.",
2320
- [import_shared10.ERROR_CODES.SOURCE_OUTSIDE_ROOT]: "The source file is outside the project root.",
2321
- [import_shared10.ERROR_CODES.SOURCE_TOO_LARGE]: "The source file exceeds the size limit.",
2322
- [import_shared10.ERROR_CODES.EDITOR_OPEN_FAILED]: "The editor request could not be started.",
2323
- [import_shared10.ERROR_CODES.AI_DISABLED]: "AI execution is not enabled.",
2324
- [import_shared10.ERROR_CODES.PROVIDER_NOT_CONFIGURED]: "The AI provider is unavailable.",
2325
- [import_shared10.ERROR_CODES.PROVIDER_AUTH_FAILED]: "The AI provider rejected authentication.",
2326
- [import_shared10.ERROR_CODES.PROVIDER_PROTOCOL_UNSUPPORTED]: "The AI provider protocol is unsupported.",
2327
- [import_shared10.ERROR_CODES.MODEL_NOT_ALLOWED]: "The selected model is not allowed.",
2328
- [import_shared10.ERROR_CODES.MODEL_TOOL_CALL_UNSUPPORTED]: "The selected model cannot run SpotPatch tools.",
2329
- [import_shared10.ERROR_CODES.PROVIDER_RATE_LIMITED]: "The AI provider is rate limited.",
2330
- [import_shared10.ERROR_CODES.AGENT_BUSY]: "Another Agent job is already running.",
2331
- [import_shared10.ERROR_CODES.AGENT_LIMIT_EXCEEDED]: "The Agent job exceeded a safety limit.",
2332
- [import_shared10.ERROR_CODES.AGENT_CANCELLED]: "The Agent job was cancelled.",
2333
- [import_shared10.ERROR_CODES.WORKTREE_DIRTY]: "Local changes require explicit inclusion consent.",
2334
- [import_shared10.ERROR_CODES.WORKTREE_NOT_REPOSITORY]: "The project root is not a Git repository.",
2335
- [import_shared10.ERROR_CODES.WORKTREE_OPERATION_IN_PROGRESS]: "A Git operation is currently in progress.",
2336
- [import_shared10.ERROR_CODES.WORKTREE_CONFLICTED]: "The local workspace contains unresolved merge conflicts.",
2337
- [import_shared10.ERROR_CODES.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: "The local workspace exceeds the safe isolation size limit.",
2338
- [import_shared10.ERROR_CODES.WORKTREE_UNTRACKED_UNSUPPORTED]: "An untracked path cannot be isolated safely.",
2339
- [import_shared10.ERROR_CODES.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: "The local workspace state cannot be isolated safely.",
2340
- [import_shared10.ERROR_CODES.TOOL_DENIED]: "The Agent tool request was denied.",
2341
- [import_shared10.ERROR_CODES.TOOL_INPUT_INVALID]: "The Agent tool input was invalid.",
2342
- [import_shared10.ERROR_CODES.TOOL_ARGUMENTS_INVALID]: "The Agent tool arguments are invalid.",
2343
- [import_shared10.ERROR_CODES.TOOL_CALL_ID_CONFLICT]: "A tool call ID conflicts within one Agent turn.",
2344
- [import_shared10.ERROR_CODES.TOOL_PATH_DENIED]: "The Agent tool path was denied.",
2345
- [import_shared10.ERROR_CODES.PATCH_REJECTED]: "The proposed patch was rejected.",
2346
- [import_shared10.ERROR_CODES.VALIDATION_FAILED]: "The proposed change failed validation.",
2347
- [import_shared10.ERROR_CODES.APPLY_CONFLICT]: "The change conflicts with the current worktree.",
2348
- [import_shared10.ERROR_CODES.INTERNAL_ERROR]: "The request could not be completed."
2556
+ [import_shared11.ERROR_CODES.INVALID_REQUEST]: "The request is invalid.",
2557
+ [import_shared11.ERROR_CODES.INVALID_TOKEN]: "The session token is invalid.",
2558
+ [import_shared11.ERROR_CODES.ORIGIN_NOT_ALLOWED]: "The request origin is not allowed.",
2559
+ [import_shared11.ERROR_CODES.SOURCE_NOT_FOUND]: "The source file is unavailable.",
2560
+ [import_shared11.ERROR_CODES.SOURCE_OUTSIDE_ROOT]: "The source file is outside the project root.",
2561
+ [import_shared11.ERROR_CODES.SOURCE_TOO_LARGE]: "The source file exceeds the size limit.",
2562
+ [import_shared11.ERROR_CODES.EDITOR_OPEN_FAILED]: "The editor request could not be started.",
2563
+ [import_shared11.ERROR_CODES.DATA_FLOW_DISABLED]: "Component data-flow analysis is not enabled.",
2564
+ [import_shared11.ERROR_CODES.DATA_FLOW_SOURCE_STALE]: "The selected source version is stale.",
2565
+ [import_shared11.ERROR_CODES.DATA_FLOW_ANALYSIS_CANCELLED]: "The data-flow analysis was cancelled.",
2566
+ [import_shared11.ERROR_CODES.AI_DISABLED]: "AI execution is not enabled.",
2567
+ [import_shared11.ERROR_CODES.PROVIDER_NOT_CONFIGURED]: "The AI provider is unavailable.",
2568
+ [import_shared11.ERROR_CODES.PROVIDER_AUTH_FAILED]: "The AI provider rejected authentication.",
2569
+ [import_shared11.ERROR_CODES.PROVIDER_PROTOCOL_UNSUPPORTED]: "The AI provider protocol is unsupported.",
2570
+ [import_shared11.ERROR_CODES.MODEL_NOT_ALLOWED]: "The selected model is not allowed.",
2571
+ [import_shared11.ERROR_CODES.MODEL_TOOL_CALL_UNSUPPORTED]: "The selected model cannot run SpotPatch tools.",
2572
+ [import_shared11.ERROR_CODES.PROVIDER_RATE_LIMITED]: "The AI provider is rate limited.",
2573
+ [import_shared11.ERROR_CODES.AGENT_BUSY]: "Another Agent job is already running.",
2574
+ [import_shared11.ERROR_CODES.AGENT_LIMIT_EXCEEDED]: "The Agent job exceeded a safety limit.",
2575
+ [import_shared11.ERROR_CODES.AGENT_CANCELLED]: "The Agent job was cancelled.",
2576
+ [import_shared11.ERROR_CODES.WORKTREE_DIRTY]: "Local changes require explicit inclusion consent.",
2577
+ [import_shared11.ERROR_CODES.WORKTREE_NOT_REPOSITORY]: "The project root is not a Git repository.",
2578
+ [import_shared11.ERROR_CODES.WORKTREE_OPERATION_IN_PROGRESS]: "A Git operation is currently in progress.",
2579
+ [import_shared11.ERROR_CODES.WORKTREE_CONFLICTED]: "The local workspace contains unresolved merge conflicts.",
2580
+ [import_shared11.ERROR_CODES.WORKTREE_LOCAL_CHANGES_TOO_LARGE]: "The local workspace exceeds the safe isolation size limit.",
2581
+ [import_shared11.ERROR_CODES.WORKTREE_UNTRACKED_UNSUPPORTED]: "An untracked path cannot be isolated safely.",
2582
+ [import_shared11.ERROR_CODES.WORKTREE_LOCAL_CHANGES_UNSUPPORTED]: "The local workspace state cannot be isolated safely.",
2583
+ [import_shared11.ERROR_CODES.TOOL_DENIED]: "The Agent tool request was denied.",
2584
+ [import_shared11.ERROR_CODES.TOOL_INPUT_INVALID]: "The Agent tool input was invalid.",
2585
+ [import_shared11.ERROR_CODES.TOOL_ARGUMENTS_INVALID]: "The Agent tool arguments are invalid.",
2586
+ [import_shared11.ERROR_CODES.TOOL_CALL_ID_CONFLICT]: "A tool call ID conflicts within one Agent turn.",
2587
+ [import_shared11.ERROR_CODES.TOOL_PATH_DENIED]: "The Agent tool path was denied.",
2588
+ [import_shared11.ERROR_CODES.PATCH_REJECTED]: "The proposed patch was rejected.",
2589
+ [import_shared11.ERROR_CODES.VALIDATION_FAILED]: "The proposed change failed validation.",
2590
+ [import_shared11.ERROR_CODES.APPLY_CONFLICT]: "The change conflicts with the current worktree.",
2591
+ [import_shared11.ERROR_CODES.INTERNAL_ERROR]: "The request could not be completed."
2349
2592
  });
2350
2593
  function writeJson(response, status, payload) {
2351
2594
  response.statusCode = status;
@@ -2354,11 +2597,11 @@ function writeJson(response, status, payload) {
2354
2597
  response.end(JSON.stringify(payload));
2355
2598
  }
2356
2599
  function asSpotPatchError(error) {
2357
- return error instanceof import_shared10.SpotPatchError ? error : new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INTERNAL_ERROR, void 0, { cause: error });
2600
+ return error instanceof import_shared11.SpotPatchError ? error : new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.INTERNAL_ERROR, void 0, { cause: error });
2358
2601
  }
2359
2602
  function writeError(response, error, logger) {
2360
2603
  const normalized = asSpotPatchError(error);
2361
- if (normalized.code === import_shared10.ERROR_CODES.INTERNAL_ERROR) {
2604
+ if (normalized.code === import_shared11.ERROR_CODES.INTERNAL_ERROR) {
2362
2605
  logger?.warn("[spotpatch:server] Internal request failure.");
2363
2606
  }
2364
2607
  writeJson(response, STATUS_BY_ERROR[normalized.code], {
@@ -2377,11 +2620,11 @@ function requestPath(request) {
2377
2620
  }
2378
2621
  }
2379
2622
  async function handleSourceContext(request, options) {
2380
- const parsed = import_shared10.sourceContextRequestSchema.safeParse(
2623
+ const parsed = import_shared11.sourceContextRequestSchema.safeParse(
2381
2624
  await readJsonRequestBody(request)
2382
2625
  );
2383
2626
  if (!parsed.success) {
2384
- throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
2627
+ throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.INVALID_REQUEST);
2385
2628
  }
2386
2629
  return readSourceContext({
2387
2630
  request: parsed.data,
@@ -2392,9 +2635,9 @@ async function handleSourceContext(request, options) {
2392
2635
  });
2393
2636
  }
2394
2637
  async function handleOpenEditor(request, options) {
2395
- const parsed = import_shared10.openEditorRequestSchema.safeParse(await readJsonRequestBody(request));
2638
+ const parsed = import_shared11.openEditorRequestSchema.safeParse(await readJsonRequestBody(request));
2396
2639
  if (!parsed.success) {
2397
- throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
2640
+ throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.INVALID_REQUEST);
2398
2641
  }
2399
2642
  const body = parsed.data;
2400
2643
  const sourcePath = await resolveSourceFile({
@@ -2411,22 +2654,23 @@ async function handleOpenEditor(request, options) {
2411
2654
  options.logger?.warn(
2412
2655
  `[spotpatch:server] ${options.options.editor === "auto" ? "The detected editor" : options.options.editor} rejected an editor request.`
2413
2656
  );
2414
- throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.EDITOR_OPEN_FAILED, void 0, {
2657
+ throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.EDITOR_OPEN_FAILED, void 0, {
2415
2658
  cause: error
2416
2659
  });
2417
2660
  }
2418
2661
  }
2419
2662
  function createSpotPatchMiddleware(options) {
2420
2663
  const bootstrap = options.bootstrap === void 0 ? void 0 : resolveRuntimeBootstrapOptions(options.bootstrap);
2664
+ const dataFlowAnalyzer = createDataFlowAnalyzer(options);
2421
2665
  return (request, response, next) => {
2422
2666
  const path8 = requestPath(request);
2423
2667
  const agentRoute = matchAgentRequestPath(path8);
2424
- if (path8 !== import_shared10.SPOTPATCH_ENDPOINTS.sourceContext && path8 !== import_shared10.SPOTPATCH_ENDPOINTS.openEditor && agentRoute === void 0 && !path8.startsWith(`${import_shared10.SPOTPATCH_API_BASE}/`)) {
2668
+ if (path8 !== import_shared11.SPOTPATCH_ENDPOINTS.sourceContext && path8 !== import_shared11.SPOTPATCH_ENDPOINTS.openEditor && path8 !== import_shared11.SPOTPATCH_ENDPOINTS.dataFlowComponentReport && path8 !== import_shared11.SPOTPATCH_ENDPOINTS.dataFlowPageReport && agentRoute === void 0 && !path8.startsWith(`${import_shared11.SPOTPATCH_API_BASE}/`)) {
2425
2669
  next();
2426
2670
  return;
2427
2671
  }
2428
2672
  const handle = async () => {
2429
- if (path8 === import_shared10.SPOTPATCH_ENDPOINTS.bootstrap && bootstrap !== void 0) {
2673
+ if (path8 === import_shared11.SPOTPATCH_ENDPOINTS.bootstrap && bootstrap !== void 0) {
2430
2674
  const data = await readRuntimeBootstrap(
2431
2675
  request,
2432
2676
  bootstrap
@@ -2438,24 +2682,44 @@ function createSpotPatchMiddleware(options) {
2438
2682
  allowLan: options.options.allowLan,
2439
2683
  sessionToken: options.session.token
2440
2684
  });
2441
- if (path8 === import_shared10.SPOTPATCH_ENDPOINTS.sourceContext) {
2685
+ if (path8 === import_shared11.SPOTPATCH_ENDPOINTS.sourceContext) {
2442
2686
  if (request.method !== "POST") {
2443
- throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
2687
+ throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.INVALID_REQUEST);
2444
2688
  }
2445
2689
  const data = await handleSourceContext(request, options);
2446
2690
  writeJson(response, 200, { ok: true, data });
2447
2691
  return;
2448
2692
  }
2449
- if (path8 === import_shared10.SPOTPATCH_ENDPOINTS.openEditor) {
2693
+ if (path8 === import_shared11.SPOTPATCH_ENDPOINTS.openEditor) {
2450
2694
  if (request.method !== "POST") {
2451
- throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
2695
+ throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.INVALID_REQUEST);
2452
2696
  }
2453
2697
  const data = await handleOpenEditor(request, options);
2454
2698
  writeJson(response, 200, { ok: true, data });
2455
2699
  return;
2456
2700
  }
2701
+ if (path8 === import_shared11.SPOTPATCH_ENDPOINTS.dataFlowComponentReport) {
2702
+ if (request.method !== "POST") {
2703
+ throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.INVALID_REQUEST);
2704
+ }
2705
+ const data = await handleComponentDataFlowReport(
2706
+ request,
2707
+ dataFlowAnalyzer,
2708
+ options
2709
+ );
2710
+ writeJson(response, 200, { ok: true, data });
2711
+ return;
2712
+ }
2713
+ if (path8 === import_shared11.SPOTPATCH_ENDPOINTS.dataFlowPageReport) {
2714
+ if (request.method !== "POST") {
2715
+ throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.INVALID_REQUEST);
2716
+ }
2717
+ const data = await handlePageDataFlowReport(request, dataFlowAnalyzer, options);
2718
+ writeJson(response, 200, { ok: true, data });
2719
+ return;
2720
+ }
2457
2721
  if (agentRoute === void 0) {
2458
- throw new import_shared10.SpotPatchError(import_shared10.ERROR_CODES.INVALID_REQUEST);
2722
+ throw new import_shared11.SpotPatchError(import_shared11.ERROR_CODES.INVALID_REQUEST);
2459
2723
  }
2460
2724
  await handleAgentRequest(
2461
2725
  request,
@@ -2474,7 +2738,7 @@ function createSpotPatchMiddleware(options) {
2474
2738
  }
2475
2739
 
2476
2740
  // src/server/source-registration.ts
2477
- var import_node_crypto5 = require("crypto");
2741
+ var import_node_crypto6 = require("crypto");
2478
2742
  var import_promises6 = require("fs/promises");
2479
2743
  var import_node_path7 = __toESM(require("path"), 1);
2480
2744
  var import_compiler = require("@spotpatch/compiler");
@@ -2497,7 +2761,7 @@ function identitiesMatch(actual, expected) {
2497
2761
  }
2498
2762
  const actualBytes = Buffer.from(actual);
2499
2763
  const expectedBytes = Buffer.from(expected);
2500
- return actualBytes.byteLength === expectedBytes.byteLength && (0, import_node_crypto5.timingSafeEqual)(actualBytes, expectedBytes);
2764
+ return actualBytes.byteLength === expectedBytes.byteLength && (0, import_node_crypto6.timingSafeEqual)(actualBytes, expectedBytes);
2501
2765
  }
2502
2766
  function isWithinRoot(root, candidate) {
2503
2767
  const relative = import_node_path7.default.relative(root, candidate);
@@ -2592,11 +2856,11 @@ async function createSourceRegistrationService(input) {
2592
2856
  }
2593
2857
 
2594
2858
  // src/session/session.ts
2595
- var import_node_crypto6 = require("crypto");
2859
+ var import_node_crypto7 = require("crypto");
2596
2860
  function createSession() {
2597
2861
  return Object.freeze({
2598
- id: (0, import_node_crypto6.randomBytes)(16).toString("base64url"),
2599
- token: (0, import_node_crypto6.randomBytes)(16).toString("base64url")
2862
+ id: (0, import_node_crypto7.randomBytes)(16).toString("base64url"),
2863
+ token: (0, import_node_crypto7.randomBytes)(16).toString("base64url")
2600
2864
  });
2601
2865
  }
2602
2866
 
@@ -2606,6 +2870,7 @@ var OPTION_KEYS = Object.freeze([
2606
2870
  "allowLan",
2607
2871
  "budget",
2608
2872
  "debug",
2873
+ "dataFlow",
2609
2874
  "editor",
2610
2875
  "enabled",
2611
2876
  "exclude",
@@ -2693,6 +2958,9 @@ function serializeResolvedSpotPatchOptions(options) {
2693
2958
  allowLan: options.allowLan,
2694
2959
  budget: options.budget,
2695
2960
  debug: options.debug,
2961
+ dataFlow: options.dataFlow.enabled ? Object.freeze({
2962
+ runtime: options.dataFlow.runtime
2963
+ }) : false,
2696
2964
  editor: options.editor,
2697
2965
  enabled: options.enabled,
2698
2966
  exclude: Object.freeze(options.exclude.map(serializeFilter)),
@@ -2735,6 +3003,15 @@ function parseBudget(value) {
2735
3003
  );
2736
3004
  return Object.freeze(budget);
2737
3005
  }
3006
+ function parseDataFlow(value) {
3007
+ if (value === false) return false;
3008
+ if (!isRecord2(value) || !hasExactKeys(value, ["runtime"]) || value.runtime !== "dispatch") {
3009
+ throw new TypeError("The SpotPatch data-flow transport is invalid.");
3010
+ }
3011
+ return Object.freeze({
3012
+ runtime: value.runtime
3013
+ });
3014
+ }
2738
3015
  function parseSerializedSpotPatchOptions(value) {
2739
3016
  if (!isRecord2(value) || !hasExactKeys(value, OPTION_KEYS)) {
2740
3017
  throw new TypeError("The SpotPatch options transport is invalid.");
@@ -2748,6 +3025,7 @@ function parseSerializedSpotPatchOptions(value) {
2748
3025
  allowLan: value.allowLan,
2749
3026
  budget: parseBudget(value.budget),
2750
3027
  debug: value.debug,
3028
+ dataFlow: parseDataFlow(value.dataFlow),
2751
3029
  editor: value.editor,
2752
3030
  enabled: value.enabled,
2753
3031
  exclude: parseFilterList(value.exclude),
@@ -2771,6 +3049,7 @@ function parseSerializedSpotPatchOptions(value) {
2771
3049
  createAgentJobManager,
2772
3050
  createIntegrationFileChange,
2773
3051
  createRuntimeAiConfig,
3052
+ createRuntimeDataFlowConfig,
2774
3053
  createSession,
2775
3054
  createSourceRegistrationService,
2776
3055
  createSourceRegistry,