veryfront 0.1.1004 → 0.1.1006
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/esm/deno.js +1 -1
- package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
- package/esm/src/internal-agents/run-stream.js +63 -2
- package/esm/src/platform/compat/http/types.d.ts +1 -0
- package/esm/src/platform/compat/http/types.d.ts.map +1 -1
- package/esm/src/platform/compat/http/websocket.d.ts +2 -0
- package/esm/src/platform/compat/http/websocket.d.ts.map +1 -1
- package/esm/src/platform/compat/http/websocket.js +9 -4
- package/esm/src/proxy/main.js +2 -2
- package/esm/src/proxy/websocket-bridge.d.ts +2 -0
- package/esm/src/proxy/websocket-bridge.d.ts.map +1 -1
- package/esm/src/proxy/websocket-bridge.js +5 -0
- package/esm/src/routing/api/module-loader/esbuild-plugin.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/esbuild-plugin.js +149 -29
- package/esm/src/routing/api/module-loader/loader.js +1 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
package/esm/deno.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,qBAAqB,CAAC;AAU7B,OAAO,EAAmB,KAAK,IAAI,EAAgB,MAAM,kBAAkB,CAAC;AAU5E,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAsCxD,MAAM,WAAW,+BAA+B;IAC9C,cAAc,EAAE,sBAAsB,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC;IAC5C,mBAAmB,CAAC,EAAE;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,cAAc,CAAC,EAAE,+BAA+B,CAAC,gBAAgB,CAAC,CAAC;IACnE,8BAA8B,CAAC,EAAE,CAC/B,KAAK,EAAE,+BAA+B,KACnC,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,aAAa,CAAC,EAAE,CACd,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAClC;QACH,MAAM,EAAE,CACN,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;YACV,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;SAC9C,EACD,aAAa,CAAC,EAAE,MAAM,EACtB,uBAAuB,CAAC,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,WAAW,KACtB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;KAC1C,CAAC;CACH;AAoCD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,oBAAoB,EAC3B,cAAc,EAAE,sBAAsB,EACtC,2BAA2B,CAAC,EAAE,MAAM,EAAE,EACtC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;;cAsFrD;AAyOD,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,QAAQ,CAAC,CAoVnB"}
|
|
@@ -7,6 +7,7 @@ import { createAgentServiceSandboxTools } from "../sandbox/index.js";
|
|
|
7
7
|
import { tryResolve } from "../extensions/contracts.js";
|
|
8
8
|
import { importFirstPartyExtensionModule } from "../extensions/first-party-import.js";
|
|
9
9
|
import { SandboxShellToolsProviderName, } from "../extensions/sandbox/index.js";
|
|
10
|
+
import { resolveHostedRuntimeAllowedToolNames } from "../agent/hosted/runtime-essential-tools.js";
|
|
10
11
|
import { SKILL_TOOL_IDS } from "../skill/types.js";
|
|
11
12
|
import { isToolVisibleTo, toolRegistry } from "../tool/index.js";
|
|
12
13
|
import { defineSchema, lazySchema } from "../schemas/index.js";
|
|
@@ -209,6 +210,54 @@ function getAllowedRemoteToolNames(forwardedProps) {
|
|
|
209
210
|
}
|
|
210
211
|
return allowedTools.every((toolName) => typeof toolName === "string") ? allowedTools : [];
|
|
211
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Reads the restrictive `runtimeOverrides.toolAllowlist` override.
|
|
215
|
+
*
|
|
216
|
+
* Unlike `runtimeOverrides.allowedTools` — which this path treats as an
|
|
217
|
+
* additive grant list (Studio forwards integration/studio tool names it wants
|
|
218
|
+
* attached on top of the agent's own surface) — `toolAllowlist` is a hard
|
|
219
|
+
* restriction: the model-visible tool set of the run is intersected with it.
|
|
220
|
+
* Scheduled automations declare it via schedule `input.runtimeOverrides` for
|
|
221
|
+
* defense in depth. Returns null when absent (no restriction); a present but
|
|
222
|
+
* malformed value fails closed to an empty allowlist.
|
|
223
|
+
*/
|
|
224
|
+
function getRuntimeToolAllowlist(forwardedProps) {
|
|
225
|
+
const runtimeOverrides = isRecord(forwardedProps?.runtimeOverrides)
|
|
226
|
+
? forwardedProps.runtimeOverrides
|
|
227
|
+
: null;
|
|
228
|
+
if (!runtimeOverrides || !Object.hasOwn(runtimeOverrides, "toolAllowlist")) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const toolAllowlist = runtimeOverrides.toolAllowlist;
|
|
232
|
+
if (!Array.isArray(toolAllowlist)) {
|
|
233
|
+
return new Set();
|
|
234
|
+
}
|
|
235
|
+
return new Set(toolAllowlist.filter((toolName) => typeof toolName === "string" && toolName.length > 0));
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Intersects the merged run tool set with the restrictive tool allowlist.
|
|
239
|
+
* Skill runtime/delegation tools are preserved for skill-enabled agents,
|
|
240
|
+
* mirroring the hosted chat runtime's allowlist semantics.
|
|
241
|
+
*/
|
|
242
|
+
function applyRuntimeToolAllowlist(mergedTools, toolAllowlist, agent) {
|
|
243
|
+
if (!toolAllowlist || !mergedTools || mergedTools === true) {
|
|
244
|
+
return mergedTools;
|
|
245
|
+
}
|
|
246
|
+
const availableSkillIds = agent.config.skills === true
|
|
247
|
+
? ["*"]
|
|
248
|
+
: Array.isArray(agent.config.skills)
|
|
249
|
+
? agent.config.skills
|
|
250
|
+
: undefined;
|
|
251
|
+
const allowedToolNames = resolveHostedRuntimeAllowedToolNames({
|
|
252
|
+
allowedToolNames: toolAllowlist,
|
|
253
|
+
localToolNames: Object.keys(mergedTools),
|
|
254
|
+
...(availableSkillIds ? { availableSkillIds } : {}),
|
|
255
|
+
});
|
|
256
|
+
if (!allowedToolNames) {
|
|
257
|
+
return mergedTools;
|
|
258
|
+
}
|
|
259
|
+
return Object.fromEntries(Object.entries(mergedTools).filter(([toolName]) => allowedToolNames.has(toolName)));
|
|
260
|
+
}
|
|
212
261
|
function getServerResolvedProjectToolNames(forwardedProps) {
|
|
213
262
|
const runtimeOverrides = isRecord(forwardedProps?.runtimeOverrides)
|
|
214
263
|
? forwardedProps.runtimeOverrides
|
|
@@ -257,9 +306,21 @@ export async function createRuntimeAgentStreamResponse(input, agent, deps) {
|
|
|
257
306
|
});
|
|
258
307
|
const forwardedAllowedRemoteToolNames = getAllowedRemoteToolNames(input.forwardedProps);
|
|
259
308
|
const sourceAllowedRemoteToolNames = getAgentAllowedRemoteToolNames(agent);
|
|
260
|
-
const
|
|
309
|
+
const grantedRemoteToolNames = forwardedAllowedRemoteToolNames === undefined
|
|
261
310
|
? undefined
|
|
262
311
|
: mergeRemoteToolNames(sourceAllowedRemoteToolNames, forwardedAllowedRemoteToolNames);
|
|
312
|
+
// A restrictive toolAllowlist caps remote exposure too: it intersects the
|
|
313
|
+
// tightest existing remote filter (forwarded grants, else the agent source's
|
|
314
|
+
// own __vfAllowedRemoteTools), and with neither present it becomes the
|
|
315
|
+
// remote filter directly. It can only narrow what the agent's sources
|
|
316
|
+
// already expose, never add.
|
|
317
|
+
const runtimeToolAllowlist = getRuntimeToolAllowlist(input.forwardedProps);
|
|
318
|
+
const sourceRemoteFilterBase = Object.hasOwn(agent.config, "__vfAllowedRemoteTools")
|
|
319
|
+
? sourceAllowedRemoteToolNames
|
|
320
|
+
: undefined;
|
|
321
|
+
const allowedRemoteToolNames = runtimeToolAllowlist === null
|
|
322
|
+
? grantedRemoteToolNames
|
|
323
|
+
: (grantedRemoteToolNames ?? sourceRemoteFilterBase ?? [...runtimeToolAllowlist]).filter((toolName) => runtimeToolAllowlist.has(toolName));
|
|
263
324
|
const forwardedIntegrationToolDefs = getForwardedIntegrationToolDefinitions(input.forwardedProps);
|
|
264
325
|
const availableForwardedToolNames = forwardedIntegrationToolDefs?.map((tool) => tool.name);
|
|
265
326
|
const sandboxTools = await buildProjectAgentSandboxTools({ agent, deps });
|
|
@@ -267,7 +328,7 @@ export async function createRuntimeAgentStreamResponse(input, agent, deps) {
|
|
|
267
328
|
...(deps.localTools ?? {}),
|
|
268
329
|
...(sandboxTools.tools ?? {}),
|
|
269
330
|
};
|
|
270
|
-
const mergedTools = buildMergedTools(agent, input, deps.sessionManager, availableForwardedToolNames, Object.keys(availableLocalTools).length > 0 ? availableLocalTools : undefined);
|
|
331
|
+
const mergedTools = applyRuntimeToolAllowlist(buildMergedTools(agent, input, deps.sessionManager, availableForwardedToolNames, Object.keys(availableLocalTools).length > 0 ? availableLocalTools : undefined), runtimeToolAllowlist, agent);
|
|
271
332
|
const runtimeAgent = {
|
|
272
333
|
...agent,
|
|
273
334
|
config: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACjE;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import * as dntShim from "../../../../_dnt.shims.js";
|
|
1
2
|
import type { WebSocketUpgradeOptions, WebSocketUpgradeResult } from "./types.js";
|
|
2
3
|
export declare function upgradeWebSocket(request: Request, options?: WebSocketUpgradeOptions): WebSocketUpgradeResult;
|
|
3
4
|
export declare function isWebSocketUpgrade(request: Request): boolean;
|
|
5
|
+
export declare function resolveDenoUpgradeWebSocketOptions(options?: WebSocketUpgradeOptions): dntShim.Deno.UpgradeWebSocketOptions | undefined;
|
|
4
6
|
//# sourceMappingURL=websocket.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/websocket.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/compat/http/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAErD,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAIlF,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,uBAAuB,GAChC,sBAAsB,CASxB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE5D;AAsBD,wBAAgB,kCAAkC,CAChD,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,IAAI,CAAC,uBAAuB,GAAG,SAAS,CAOlD"}
|
|
@@ -22,9 +22,14 @@ function upgradeWebSocketDeno(request, options) {
|
|
|
22
22
|
detail: "Deno.upgradeWebSocket() is not available in this runtime.",
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
-
const
|
|
26
|
-
? { protocol: options.protocol }
|
|
27
|
-
: undefined;
|
|
28
|
-
const { socket, response } = nativeDeno.upgradeWebSocket(request, denoOptions);
|
|
25
|
+
const { socket, response } = nativeDeno.upgradeWebSocket(request, resolveDenoUpgradeWebSocketOptions(options));
|
|
29
26
|
return { socket, response };
|
|
30
27
|
}
|
|
28
|
+
export function resolveDenoUpgradeWebSocketOptions(options) {
|
|
29
|
+
if (!options?.protocol && options?.idleTimeout === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
return {
|
|
32
|
+
...(options.protocol ? { protocol: options.protocol } : {}),
|
|
33
|
+
...(options.idleTimeout !== undefined ? { idleTimeout: options.idleTimeout } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
package/esm/src/proxy/main.js
CHANGED
|
@@ -23,7 +23,7 @@ import * as dntShim from "../../_dnt.shims.js";
|
|
|
23
23
|
import { createProxyHandler, INTERNAL_PROXY_HEADERS } from "./handler.js";
|
|
24
24
|
import { createCacheFromEnv } from "./cache/index.js";
|
|
25
25
|
import { isRetryableConnectionError } from "./retry.js";
|
|
26
|
-
import { closeBridgePeer, getServerWebSocketErrorLogLevel } from "./websocket-bridge.js";
|
|
26
|
+
import { closeBridgePeer, createProxyClientWebSocketUpgradeOptions, getServerWebSocketErrorLogLevel, } from "./websocket-bridge.js";
|
|
27
27
|
import { register } from "../extensions/contracts.js";
|
|
28
28
|
import { importFirstPartyExtensionModule } from "../extensions/first-party-import.js";
|
|
29
29
|
import { endSpan, extractContext, initializeOTLPWithApis, injectContext, ProxySpanNames, shutdownOTLP, startServerSpan, withContext, withSpan, } from "./tracing.js";
|
|
@@ -138,7 +138,7 @@ function handleWebSocketUpgrade(req, url) {
|
|
|
138
138
|
parsedEnvironment: parsed.environment,
|
|
139
139
|
targetUrl: targetUrl.toString(),
|
|
140
140
|
});
|
|
141
|
-
const { socket: clientSocket, response } = upgradeWebSocket(req);
|
|
141
|
+
const { socket: clientSocket, response } = upgradeWebSocket(req, createProxyClientWebSocketUpgradeOptions());
|
|
142
142
|
let serverSocket = null;
|
|
143
143
|
let connectTimeoutId = null;
|
|
144
144
|
let timedOut = false;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { WebSocketUpgradeOptions } from "../platform/compat/http/index.js";
|
|
1
2
|
type BridgePeer = Pick<WebSocket, "close" | "readyState">;
|
|
2
3
|
export type ServerWebSocketErrorLogLevel = "warn" | "error";
|
|
3
4
|
export declare function getServerWebSocketErrorLogLevel(message: string): ServerWebSocketErrorLogLevel;
|
|
4
5
|
export declare function closeBridgePeer(peer: BridgePeer | null, code: number, reason: string): void;
|
|
6
|
+
export declare function createProxyClientWebSocketUpgradeOptions(): WebSocketUpgradeOptions;
|
|
5
7
|
export {};
|
|
6
8
|
//# sourceMappingURL=websocket-bridge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-bridge.d.ts","sourceRoot":"","sources":["../../../src/src/proxy/websocket-bridge.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,YAAY,CAAC,CAAC;AAE1D,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG,OAAO,CAAC;AAS5D,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,GAAG,4BAA4B,CAI7F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAI3F"}
|
|
1
|
+
{"version":3,"file":"websocket-bridge.d.ts","sourceRoot":"","sources":["../../../src/src/proxy/websocket-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAEhF,KAAK,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,YAAY,CAAC,CAAC;AAE1D,MAAM,MAAM,4BAA4B,GAAG,MAAM,GAAG,OAAO,CAAC;AAS5D,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,MAAM,GAAG,4BAA4B,CAI7F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAI3F;AAED,wBAAgB,wCAAwC,IAAI,uBAAuB,CAIlF"}
|
|
@@ -16,3 +16,8 @@ export function closeBridgePeer(peer, code, reason) {
|
|
|
16
16
|
return;
|
|
17
17
|
peer.close(code, reason);
|
|
18
18
|
}
|
|
19
|
+
export function createProxyClientWebSocketUpgradeOptions() {
|
|
20
|
+
// Proxied project sockets use app-level heartbeats; Deno's transport idle timeout
|
|
21
|
+
// can close otherwise healthy bridges before the browser sends a data frame.
|
|
22
|
+
return { idleTimeout: 0 };
|
|
23
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esbuild-plugin.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/esbuild-plugin.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"esbuild-plugin.d.ts","sourceRoot":"","sources":["../../../../../src/src/routing/api/module-loader/esbuild-plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAW,MAAM,EAAE,MAAM,sCAAsC,CAAC;AAO5E,UAAU,iBAAiB;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA8GD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,EAAE,GAAG,MAAM,CAkP9E"}
|
|
@@ -1,11 +1,84 @@
|
|
|
1
1
|
import * as dntShim from "../../../../_dnt.shims.js";
|
|
2
|
-
import { computeIntegrity, createLockfileManager, HTTP_MODULE_FETCH_TIMEOUT_MS, HTTP_NETWORK_CONNECT_TIMEOUT, serverLogger, } from "../../../utils/index.js";
|
|
2
|
+
import { computeHash, computeIntegrity, createLockfileManager, HTTP_MODULE_FETCH_TIMEOUT_MS, HTTP_NETWORK_CONNECT_TIMEOUT, serverLogger, } from "../../../utils/index.js";
|
|
3
|
+
import { createFileSystem } from "../../../platform/compat/fs.js";
|
|
4
|
+
import * as pathHelper from "../../../platform/compat/path/index.js";
|
|
3
5
|
const logger = serverLogger.component("api");
|
|
6
|
+
const HTTP_MODULE_CACHE_DIR = ".veryfront/cache/api-http-imports";
|
|
7
|
+
const HTTP_MODULE_FETCH_MAX_ATTEMPTS = 3;
|
|
8
|
+
const HTTP_MODULE_FETCH_RETRY_DELAY_MS = 100;
|
|
9
|
+
function createHTTPModuleCache(projectDir) {
|
|
10
|
+
if (!projectDir)
|
|
11
|
+
return null;
|
|
12
|
+
const fs = createFileSystem();
|
|
13
|
+
const cacheDir = pathHelper.join(projectDir, HTTP_MODULE_CACHE_DIR);
|
|
14
|
+
async function cachePaths(url, integrity) {
|
|
15
|
+
const key = await computeHash(`${url}\n${integrity}`);
|
|
16
|
+
return {
|
|
17
|
+
sourcePath: pathHelper.join(cacheDir, `${key}.mjs`),
|
|
18
|
+
metadataPath: pathHelper.join(cacheDir, `${key}.json`),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async function readMetadata(metadataPath, fs) {
|
|
22
|
+
if (!await fs.exists(metadataPath))
|
|
23
|
+
return null;
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(await fs.readTextFile(metadataPath));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
logger.debug(`[http] ignoring unreadable module cache metadata: ${error}`);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
async read(url, expectedIntegrity) {
|
|
34
|
+
if (!expectedIntegrity)
|
|
35
|
+
return null;
|
|
36
|
+
try {
|
|
37
|
+
const { sourcePath, metadataPath } = await cachePaths(url, expectedIntegrity);
|
|
38
|
+
if (!await fs.exists(sourcePath))
|
|
39
|
+
return null;
|
|
40
|
+
const contents = await fs.readTextFile(sourcePath);
|
|
41
|
+
const integrity = await computeIntegrity(contents);
|
|
42
|
+
if (expectedIntegrity && integrity !== expectedIntegrity) {
|
|
43
|
+
logger.warn(`[http] cached module integrity mismatch: ${url}`);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const metadata = await readMetadata(metadataPath, fs);
|
|
47
|
+
if (metadata?.integrity && metadata.integrity !== integrity) {
|
|
48
|
+
logger.warn(`[http] cached module metadata integrity mismatch: ${url}`);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return contents;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
logger.debug(`[http] module cache read miss for ${url}: ${error}`);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
async write(url, contents, resolvedUrl, integrity) {
|
|
59
|
+
try {
|
|
60
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
61
|
+
const { sourcePath, metadataPath } = await cachePaths(url, integrity);
|
|
62
|
+
await fs.writeTextFile(sourcePath, contents);
|
|
63
|
+
await fs.writeTextFile(metadataPath, `${JSON.stringify({
|
|
64
|
+
url,
|
|
65
|
+
resolvedUrl,
|
|
66
|
+
integrity,
|
|
67
|
+
fetchedAt: new Date().toISOString(),
|
|
68
|
+
}, null, 2)}\n`);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
logger.debug(`[http] could not update module cache for ${url}: ${error}`);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
4
76
|
export function createHTTPPlugin(options) {
|
|
5
77
|
const opts = Array.isArray(options) ? { allowedHosts: options } : options;
|
|
6
78
|
const { allowedHosts, strict = false } = opts;
|
|
7
79
|
const lockfile = opts.lockfile ??
|
|
8
80
|
(opts.projectDir ? createLockfileManager(opts.projectDir) : null);
|
|
81
|
+
const moduleCache = createHTTPModuleCache(opts.projectDir);
|
|
9
82
|
return {
|
|
10
83
|
name: "vf-api-http-fetch",
|
|
11
84
|
setup(build) {
|
|
@@ -25,6 +98,27 @@ export function createHTTPPlugin(options) {
|
|
|
25
98
|
clearTimeout(timeout);
|
|
26
99
|
}
|
|
27
100
|
}
|
|
101
|
+
function shouldRetryFetch(status) {
|
|
102
|
+
return status === 429 || status >= 500 || status === HTTP_NETWORK_CONNECT_TIMEOUT;
|
|
103
|
+
}
|
|
104
|
+
function delay(ms) {
|
|
105
|
+
return new Promise((resolve) => dntShim.setTimeout(resolve, ms));
|
|
106
|
+
}
|
|
107
|
+
async function fetchRemoteModule(url) {
|
|
108
|
+
for (let attempt = 1; attempt <= HTTP_MODULE_FETCH_MAX_ATTEMPTS; attempt += 1) {
|
|
109
|
+
const response = await fetchWithTimeout(url).catch((error) => new Response(String(error?.message ?? error), {
|
|
110
|
+
status: HTTP_NETWORK_CONNECT_TIMEOUT,
|
|
111
|
+
}));
|
|
112
|
+
if (!shouldRetryFetch(response.status) || attempt === HTTP_MODULE_FETCH_MAX_ATTEMPTS) {
|
|
113
|
+
return response;
|
|
114
|
+
}
|
|
115
|
+
logger.warn(`[http] fetch attempt ${attempt} failed ${url} ${response.status}; retrying`);
|
|
116
|
+
await delay(HTTP_MODULE_FETCH_RETRY_DELAY_MS * attempt);
|
|
117
|
+
}
|
|
118
|
+
return new Response("Remote module fetch failed", {
|
|
119
|
+
status: HTTP_NETWORK_CONNECT_TIMEOUT,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
28
122
|
build.onResolve({ filter: /^(http|https):\/\// }, (args) => ({
|
|
29
123
|
path: args.path,
|
|
30
124
|
namespace: "http-url",
|
|
@@ -90,39 +184,62 @@ export function createHTTPPlugin(options) {
|
|
|
90
184
|
catch (e) {
|
|
91
185
|
logger.warn("API URL parse failed", e);
|
|
92
186
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
],
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
logger.warn(`[http] integrity mismatch, refetching: ${args.path}`);
|
|
187
|
+
const readCachedModule = async (url, expectedIntegrity) => {
|
|
188
|
+
if (!url || !moduleCache)
|
|
189
|
+
return null;
|
|
190
|
+
return await moduleCache.read(url, expectedIntegrity);
|
|
191
|
+
};
|
|
192
|
+
const lockfileEntry = lockfile ? await lockfile.get(args.path) : null;
|
|
193
|
+
if (lockfileEntry) {
|
|
194
|
+
let canUseLockfileCacheFallback = true;
|
|
195
|
+
logger.debug(`[http] lockfile hit: ${args.path}`);
|
|
196
|
+
try {
|
|
197
|
+
const res = await fetchRemoteModule(lockfileEntry.resolved);
|
|
198
|
+
if (res.ok) {
|
|
199
|
+
const text = await res.text();
|
|
200
|
+
const integrity = await computeIntegrity(text);
|
|
201
|
+
if (integrity === lockfileEntry.integrity) {
|
|
202
|
+
await moduleCache?.write(args.path, text, lockfileEntry.resolved, integrity);
|
|
203
|
+
await moduleCache?.write(lockfileEntry.resolved, text, lockfileEntry.resolved, integrity);
|
|
204
|
+
return { contents: text, loader: "js" };
|
|
115
205
|
}
|
|
206
|
+
if (strict) {
|
|
207
|
+
return {
|
|
208
|
+
errors: [
|
|
209
|
+
{
|
|
210
|
+
text: `Integrity mismatch for ${args.path}: expected ${lockfileEntry.integrity}, got ${integrity}`,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
canUseLockfileCacheFallback = false;
|
|
216
|
+
logger.warn(`[http] integrity mismatch, refetching: ${args.path}`);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
logger.warn(`[http] cached URL returned ${res.status}, trying module cache: ${args.path}`);
|
|
116
220
|
}
|
|
117
|
-
|
|
118
|
-
|
|
221
|
+
}
|
|
222
|
+
catch (_error) {
|
|
223
|
+
logger.warn(`[http] cached URL failed, trying module cache: ${args.path}`);
|
|
224
|
+
}
|
|
225
|
+
if (canUseLockfileCacheFallback) {
|
|
226
|
+
const cachedText = await readCachedModule(lockfileEntry.resolved, lockfileEntry.integrity) ??
|
|
227
|
+
await readCachedModule(args.path, lockfileEntry.integrity);
|
|
228
|
+
if (cachedText) {
|
|
229
|
+
logger.warn(`[http] serving cached remote import for ${args.path}`);
|
|
230
|
+
return { contents: cachedText, loader: "js" };
|
|
119
231
|
}
|
|
120
232
|
}
|
|
121
233
|
}
|
|
122
|
-
const res = await
|
|
123
|
-
return new Response(String(e?.message ?? e), { status: HTTP_NETWORK_CONNECT_TIMEOUT });
|
|
124
|
-
});
|
|
234
|
+
const res = await fetchRemoteModule(requestUrl);
|
|
125
235
|
if (!res.ok) {
|
|
236
|
+
const cachedText = await readCachedModule(lockfileEntry?.resolved, lockfileEntry?.integrity) ??
|
|
237
|
+
await readCachedModule(requestUrl, lockfileEntry?.integrity) ??
|
|
238
|
+
await readCachedModule(args.path, lockfileEntry?.integrity);
|
|
239
|
+
if (cachedText) {
|
|
240
|
+
logger.warn(`[http] serving cached remote import for ${args.path}`);
|
|
241
|
+
return { contents: cachedText, loader: "js" };
|
|
242
|
+
}
|
|
126
243
|
logger.error(`[http] fetch failed ${requestUrl} ${res.status}`);
|
|
127
244
|
return {
|
|
128
245
|
errors: [
|
|
@@ -134,8 +251,8 @@ export function createHTTPPlugin(options) {
|
|
|
134
251
|
}
|
|
135
252
|
const text = await res.text();
|
|
136
253
|
const resolvedUrl = res.url || requestUrl;
|
|
254
|
+
const integrity = await computeIntegrity(text);
|
|
137
255
|
if (lockfile) {
|
|
138
|
-
const integrity = await computeIntegrity(text);
|
|
139
256
|
await lockfile.set(args.path, {
|
|
140
257
|
resolved: resolvedUrl,
|
|
141
258
|
integrity,
|
|
@@ -144,6 +261,9 @@ export function createHTTPPlugin(options) {
|
|
|
144
261
|
await lockfile.flush();
|
|
145
262
|
logger.debug(`[http] lockfile updated: ${args.path} -> ${resolvedUrl}`);
|
|
146
263
|
}
|
|
264
|
+
await moduleCache?.write(args.path, text, resolvedUrl, integrity);
|
|
265
|
+
await moduleCache?.write(requestUrl, text, resolvedUrl, integrity);
|
|
266
|
+
await moduleCache?.write(resolvedUrl, text, resolvedUrl, integrity);
|
|
147
267
|
return { contents: text, loader: "js" };
|
|
148
268
|
});
|
|
149
269
|
logger.debug(`[API][http] resolvedUrls: ${resolvedUrls.length}, nodeMapped: ${nodeMapped.length}`);
|
|
@@ -253,7 +253,7 @@ function loadAndTranspileModule(modulePath, projectDir, adapter, fs, config) {
|
|
|
253
253
|
plugins: [
|
|
254
254
|
createImportMapPlugin(projectDir, adapter, config),
|
|
255
255
|
createAdapterResolvePlugin(adapter, projectDir),
|
|
256
|
-
createHTTPPlugin(allowedHosts),
|
|
256
|
+
createHTTPPlugin({ allowedHosts, projectDir }),
|
|
257
257
|
],
|
|
258
258
|
});
|
|
259
259
|
if (result.errors?.length) {
|