everything-dev 1.43.0 → 1.44.1
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/api-contract.cjs +35 -39
- package/dist/api-contract.cjs.map +1 -1
- package/dist/api-contract.mjs +35 -39
- package/dist/api-contract.mjs.map +1 -1
- package/dist/cli/catalog.cjs +2 -2
- package/dist/cli/catalog.cjs.map +1 -1
- package/dist/cli/catalog.mjs +2 -2
- package/dist/cli/catalog.mjs.map +1 -1
- package/dist/cli/infra.cjs +14 -4
- package/dist/cli/infra.cjs.map +1 -1
- package/dist/cli/infra.mjs +14 -4
- package/dist/cli/infra.mjs.map +1 -1
- package/dist/cli/init.cjs +9 -4
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +1 -1
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +1 -1
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +9 -4
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/status.cjs +13 -29
- package/dist/cli/status.cjs.map +1 -1
- package/dist/cli/status.mjs +13 -29
- package/dist/cli/status.mjs.map +1 -1
- package/dist/cli.cjs +9 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +9 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/config.cjs +27 -23
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +27 -23
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +6 -5
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +12 -4
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +12 -4
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +6 -5
- package/dist/contract.mjs.map +1 -1
- package/dist/dev-session.cjs +3 -1
- package/dist/dev-session.cjs.map +1 -1
- package/dist/dev-session.mjs +3 -1
- package/dist/dev-session.mjs.map +1 -1
- package/dist/fastkv.cjs +13 -29
- package/dist/fastkv.cjs.map +1 -1
- package/dist/fastkv.mjs +14 -29
- package/dist/fastkv.mjs.map +1 -1
- package/dist/http-client.cjs +102 -0
- package/dist/http-client.cjs.map +1 -0
- package/dist/http-client.mjs +100 -0
- package/dist/http-client.mjs.map +1 -0
- package/dist/integrity.cjs +3 -2
- package/dist/integrity.cjs.map +1 -1
- package/dist/integrity.d.cts.map +1 -1
- package/dist/integrity.d.mts.map +1 -1
- package/dist/integrity.mjs +3 -2
- package/dist/integrity.mjs.map +1 -1
- package/dist/mf.cjs +10 -2
- package/dist/mf.cjs.map +1 -1
- package/dist/mf.d.cts.map +1 -1
- package/dist/mf.d.mts.map +1 -1
- package/dist/mf.mjs +10 -2
- package/dist/mf.mjs.map +1 -1
- package/dist/near-cli.cjs +1 -1
- package/dist/near-cli.mjs +1 -1
- package/dist/orchestrator.cjs +39 -23
- package/dist/orchestrator.cjs.map +1 -1
- package/dist/orchestrator.mjs +39 -23
- package/dist/orchestrator.mjs.map +1 -1
- package/dist/plugin.cjs +57 -65
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +4 -0
- package/dist/plugin.d.mts +4 -0
- package/dist/plugin.mjs +57 -65
- package/dist/plugin.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.cjs","names":["Data","Duration","Effect","Schedule"],"sources":["../src/http-client.ts"],"sourcesContent":["import { Data, Duration, Effect, Schedule } from \"effect\";\n\n// --- Tagged errors ---\n\nexport class FetchNetworkError extends Data.TaggedError(\"FetchNetworkError\")<{\n url: string;\n cause: unknown;\n}> {}\n\nexport class FetchTimeoutError extends Data.TaggedError(\"FetchTimeoutError\")<{\n url: string;\n}> {}\n\nexport class FetchHttpError extends Data.TaggedError(\"FetchHttpError\")<{\n url: string;\n status: number;\n statusText: string;\n}> {}\n\nexport type FetchError = FetchNetworkError | FetchTimeoutError | FetchHttpError;\n\n// --- Options ---\n\nexport interface FetchOptions {\n method?: string;\n headers?: Record<string, string>;\n body?: BodyInit;\n redirect?: RequestRedirect;\n timeout?: Duration.DurationInput;\n}\n\nexport interface FetchWithRetryOptions extends FetchOptions {\n retries?: number;\n}\n\n// --- Defaults ---\n\nconst DEFAULT_TIMEOUT = \"10 seconds\";\nconst DEFAULT_RETRIES = 3;\n\nconst EXPONENTIAL_BASE = Duration.seconds(1);\nconst EXPONENTIAL_CAP = Duration.seconds(15);\n\n// --- Helpers ---\n\nconst isRetryable = (error: FetchError): boolean => {\n if (error instanceof FetchNetworkError) return true;\n if (error instanceof FetchTimeoutError) return true;\n if (error instanceof FetchHttpError) return error.status >= 500;\n return false;\n};\n\n// --- Low-level: just timeout + error classification, returns Response regardless of HTTP status ---\n\nconst fetchRawEff = (\n url: string,\n options?: FetchOptions,\n): Effect.Effect<Response, FetchNetworkError | FetchTimeoutError> =>\n Effect.tryPromise({\n try: async () => {\n const timeoutMs = Duration.toMillis(Duration.decode(options?.timeout ?? DEFAULT_TIMEOUT));\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, {\n method: options?.method ?? \"GET\",\n headers: options?.headers,\n body: options?.body,\n redirect: options?.redirect,\n signal: controller.signal,\n });\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new FetchTimeoutError({ url });\n }\n throw new FetchNetworkError({ url, cause: error });\n } finally {\n clearTimeout(timer);\n }\n },\n catch: (error) => {\n if (error instanceof FetchNetworkError || error instanceof FetchTimeoutError) {\n return error;\n }\n return new FetchNetworkError({ url, cause: error });\n },\n });\n\n// --- Adds HTTP status checking: non-2xx becomes FetchHttpError ---\n\nconst fetchEff = (url: string, options?: FetchOptions): Effect.Effect<Response, FetchError> =>\n fetchRawEff(url, options).pipe(\n Effect.flatMap((response) => {\n if (response.ok) return Effect.succeed(response);\n return Effect.fail(\n new FetchHttpError({ url, status: response.status, statusText: response.statusText }),\n );\n }),\n );\n\n// --- With retry ---\n\nconst retrySchedule = Schedule.exponential(EXPONENTIAL_BASE).pipe(\n Schedule.upTo(EXPONENTIAL_CAP),\n Schedule.intersect(Schedule.recurs(DEFAULT_RETRIES)),\n);\n\nexport const fetchWithRetryEff = (\n url: string,\n options?: FetchWithRetryOptions,\n): Effect.Effect<Response, FetchError> => {\n const retries = options?.retries ?? DEFAULT_RETRIES;\n\n if (retries <= 0) return fetchEff(url, options);\n\n const schedule =\n options?.retries !== undefined\n ? Schedule.exponential(EXPONENTIAL_BASE).pipe(\n Schedule.upTo(EXPONENTIAL_CAP),\n Schedule.intersect(Schedule.recurs(retries)),\n )\n : retrySchedule;\n\n return fetchEff(url, options).pipe(\n Effect.retry({\n schedule,\n while: isRetryable,\n }),\n );\n};\n\n// --- In-memory GET cache (per-process, prevents duplicate round trips) ---\n\nconst getCache = new Map<string, { data: unknown; expiresAt: number }>();\nconst GET_CACHE_TTL_MS = 30_000;\n\nfunction isCacheable(_url: string, options?: FetchWithRetryOptions): boolean {\n if (options?.method && options.method !== \"GET\") return false;\n if (options?.body) return false;\n return true;\n}\n\n// --- Promise wrappers (bridge for non-Effect code) ---\n\nexport const fetchResponse = (url: string, options?: FetchOptions): Promise<Response> =>\n Effect.runPromise(fetchRawEff(url, options));\n\nexport const fetchJsonOrNull = async <T>(\n url: string,\n options?: FetchWithRetryOptions,\n): Promise<T | null> => {\n if (isCacheable(url, options)) {\n const cached = getCache.get(url);\n if (cached && cached.expiresAt > Date.now()) {\n return cached.data as T;\n }\n }\n\n const retries = options?.retries ?? DEFAULT_RETRIES;\n const eff =\n retries > 0 ? fetchWithRetryEff(url, { ...options, retries }) : fetchEff(url, options);\n\n try {\n const res = await Effect.runPromise(eff);\n const data = (await res.json()) as T;\n if (isCacheable(url, options)) {\n getCache.set(url, { data, expiresAt: Date.now() + GET_CACHE_TTL_MS });\n }\n return data;\n } catch (error) {\n const msg =\n error instanceof FetchNetworkError\n ? `[http] Network error: ${error.url} — ${String(error.cause)}`\n : error instanceof FetchTimeoutError\n ? `[http] Timeout: ${error.url}`\n : error instanceof FetchHttpError\n ? `[http] HTTP ${error.status}: ${error.url}`\n : `[http] Unknown error while fetching ${url}`;\n console.error(msg);\n return null;\n }\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Data, Duration, Effect, Schedule } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/http-client.ts
|
|
4
|
+
var FetchNetworkError = class extends Data.TaggedError("FetchNetworkError") {};
|
|
5
|
+
var FetchTimeoutError = class extends Data.TaggedError("FetchTimeoutError") {};
|
|
6
|
+
var FetchHttpError = class extends Data.TaggedError("FetchHttpError") {};
|
|
7
|
+
const DEFAULT_TIMEOUT = "10 seconds";
|
|
8
|
+
const DEFAULT_RETRIES = 3;
|
|
9
|
+
const EXPONENTIAL_BASE = Duration.seconds(1);
|
|
10
|
+
const EXPONENTIAL_CAP = Duration.seconds(15);
|
|
11
|
+
const isRetryable = (error) => {
|
|
12
|
+
if (error instanceof FetchNetworkError) return true;
|
|
13
|
+
if (error instanceof FetchTimeoutError) return true;
|
|
14
|
+
if (error instanceof FetchHttpError) return error.status >= 500;
|
|
15
|
+
return false;
|
|
16
|
+
};
|
|
17
|
+
const fetchRawEff = (url, options) => Effect.tryPromise({
|
|
18
|
+
try: async () => {
|
|
19
|
+
const timeoutMs = Duration.toMillis(Duration.decode(options?.timeout ?? DEFAULT_TIMEOUT));
|
|
20
|
+
const controller = new AbortController();
|
|
21
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
22
|
+
try {
|
|
23
|
+
return await fetch(url, {
|
|
24
|
+
method: options?.method ?? "GET",
|
|
25
|
+
headers: options?.headers,
|
|
26
|
+
body: options?.body,
|
|
27
|
+
redirect: options?.redirect,
|
|
28
|
+
signal: controller.signal
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error instanceof Error && error.name === "AbortError") throw new FetchTimeoutError({ url });
|
|
32
|
+
throw new FetchNetworkError({
|
|
33
|
+
url,
|
|
34
|
+
cause: error
|
|
35
|
+
});
|
|
36
|
+
} finally {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
catch: (error) => {
|
|
41
|
+
if (error instanceof FetchNetworkError || error instanceof FetchTimeoutError) return error;
|
|
42
|
+
return new FetchNetworkError({
|
|
43
|
+
url,
|
|
44
|
+
cause: error
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const fetchEff = (url, options) => fetchRawEff(url, options).pipe(Effect.flatMap((response) => {
|
|
49
|
+
if (response.ok) return Effect.succeed(response);
|
|
50
|
+
return Effect.fail(new FetchHttpError({
|
|
51
|
+
url,
|
|
52
|
+
status: response.status,
|
|
53
|
+
statusText: response.statusText
|
|
54
|
+
}));
|
|
55
|
+
}));
|
|
56
|
+
const retrySchedule = Schedule.exponential(EXPONENTIAL_BASE).pipe(Schedule.upTo(EXPONENTIAL_CAP), Schedule.intersect(Schedule.recurs(DEFAULT_RETRIES)));
|
|
57
|
+
const fetchWithRetryEff = (url, options) => {
|
|
58
|
+
const retries = options?.retries ?? DEFAULT_RETRIES;
|
|
59
|
+
if (retries <= 0) return fetchEff(url, options);
|
|
60
|
+
const schedule = options?.retries !== void 0 ? Schedule.exponential(EXPONENTIAL_BASE).pipe(Schedule.upTo(EXPONENTIAL_CAP), Schedule.intersect(Schedule.recurs(retries))) : retrySchedule;
|
|
61
|
+
return fetchEff(url, options).pipe(Effect.retry({
|
|
62
|
+
schedule,
|
|
63
|
+
while: isRetryable
|
|
64
|
+
}));
|
|
65
|
+
};
|
|
66
|
+
const getCache = /* @__PURE__ */ new Map();
|
|
67
|
+
const GET_CACHE_TTL_MS = 3e4;
|
|
68
|
+
function isCacheable(_url, options) {
|
|
69
|
+
if (options?.method && options.method !== "GET") return false;
|
|
70
|
+
if (options?.body) return false;
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
const fetchResponse = (url, options) => Effect.runPromise(fetchRawEff(url, options));
|
|
74
|
+
const fetchJsonOrNull = async (url, options) => {
|
|
75
|
+
if (isCacheable(url, options)) {
|
|
76
|
+
const cached = getCache.get(url);
|
|
77
|
+
if (cached && cached.expiresAt > Date.now()) return cached.data;
|
|
78
|
+
}
|
|
79
|
+
const retries = options?.retries ?? DEFAULT_RETRIES;
|
|
80
|
+
const eff = retries > 0 ? fetchWithRetryEff(url, {
|
|
81
|
+
...options,
|
|
82
|
+
retries
|
|
83
|
+
}) : fetchEff(url, options);
|
|
84
|
+
try {
|
|
85
|
+
const data = await (await Effect.runPromise(eff)).json();
|
|
86
|
+
if (isCacheable(url, options)) getCache.set(url, {
|
|
87
|
+
data,
|
|
88
|
+
expiresAt: Date.now() + GET_CACHE_TTL_MS
|
|
89
|
+
});
|
|
90
|
+
return data;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
const msg = error instanceof FetchNetworkError ? `[http] Network error: ${error.url} — ${String(error.cause)}` : error instanceof FetchTimeoutError ? `[http] Timeout: ${error.url}` : error instanceof FetchHttpError ? `[http] HTTP ${error.status}: ${error.url}` : `[http] Unknown error while fetching ${url}`;
|
|
93
|
+
console.error(msg);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
//#endregion
|
|
99
|
+
export { fetchJsonOrNull, fetchResponse };
|
|
100
|
+
//# sourceMappingURL=http-client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.mjs","names":[],"sources":["../src/http-client.ts"],"sourcesContent":["import { Data, Duration, Effect, Schedule } from \"effect\";\n\n// --- Tagged errors ---\n\nexport class FetchNetworkError extends Data.TaggedError(\"FetchNetworkError\")<{\n url: string;\n cause: unknown;\n}> {}\n\nexport class FetchTimeoutError extends Data.TaggedError(\"FetchTimeoutError\")<{\n url: string;\n}> {}\n\nexport class FetchHttpError extends Data.TaggedError(\"FetchHttpError\")<{\n url: string;\n status: number;\n statusText: string;\n}> {}\n\nexport type FetchError = FetchNetworkError | FetchTimeoutError | FetchHttpError;\n\n// --- Options ---\n\nexport interface FetchOptions {\n method?: string;\n headers?: Record<string, string>;\n body?: BodyInit;\n redirect?: RequestRedirect;\n timeout?: Duration.DurationInput;\n}\n\nexport interface FetchWithRetryOptions extends FetchOptions {\n retries?: number;\n}\n\n// --- Defaults ---\n\nconst DEFAULT_TIMEOUT = \"10 seconds\";\nconst DEFAULT_RETRIES = 3;\n\nconst EXPONENTIAL_BASE = Duration.seconds(1);\nconst EXPONENTIAL_CAP = Duration.seconds(15);\n\n// --- Helpers ---\n\nconst isRetryable = (error: FetchError): boolean => {\n if (error instanceof FetchNetworkError) return true;\n if (error instanceof FetchTimeoutError) return true;\n if (error instanceof FetchHttpError) return error.status >= 500;\n return false;\n};\n\n// --- Low-level: just timeout + error classification, returns Response regardless of HTTP status ---\n\nconst fetchRawEff = (\n url: string,\n options?: FetchOptions,\n): Effect.Effect<Response, FetchNetworkError | FetchTimeoutError> =>\n Effect.tryPromise({\n try: async () => {\n const timeoutMs = Duration.toMillis(Duration.decode(options?.timeout ?? DEFAULT_TIMEOUT));\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, {\n method: options?.method ?? \"GET\",\n headers: options?.headers,\n body: options?.body,\n redirect: options?.redirect,\n signal: controller.signal,\n });\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new FetchTimeoutError({ url });\n }\n throw new FetchNetworkError({ url, cause: error });\n } finally {\n clearTimeout(timer);\n }\n },\n catch: (error) => {\n if (error instanceof FetchNetworkError || error instanceof FetchTimeoutError) {\n return error;\n }\n return new FetchNetworkError({ url, cause: error });\n },\n });\n\n// --- Adds HTTP status checking: non-2xx becomes FetchHttpError ---\n\nconst fetchEff = (url: string, options?: FetchOptions): Effect.Effect<Response, FetchError> =>\n fetchRawEff(url, options).pipe(\n Effect.flatMap((response) => {\n if (response.ok) return Effect.succeed(response);\n return Effect.fail(\n new FetchHttpError({ url, status: response.status, statusText: response.statusText }),\n );\n }),\n );\n\n// --- With retry ---\n\nconst retrySchedule = Schedule.exponential(EXPONENTIAL_BASE).pipe(\n Schedule.upTo(EXPONENTIAL_CAP),\n Schedule.intersect(Schedule.recurs(DEFAULT_RETRIES)),\n);\n\nexport const fetchWithRetryEff = (\n url: string,\n options?: FetchWithRetryOptions,\n): Effect.Effect<Response, FetchError> => {\n const retries = options?.retries ?? DEFAULT_RETRIES;\n\n if (retries <= 0) return fetchEff(url, options);\n\n const schedule =\n options?.retries !== undefined\n ? Schedule.exponential(EXPONENTIAL_BASE).pipe(\n Schedule.upTo(EXPONENTIAL_CAP),\n Schedule.intersect(Schedule.recurs(retries)),\n )\n : retrySchedule;\n\n return fetchEff(url, options).pipe(\n Effect.retry({\n schedule,\n while: isRetryable,\n }),\n );\n};\n\n// --- In-memory GET cache (per-process, prevents duplicate round trips) ---\n\nconst getCache = new Map<string, { data: unknown; expiresAt: number }>();\nconst GET_CACHE_TTL_MS = 30_000;\n\nfunction isCacheable(_url: string, options?: FetchWithRetryOptions): boolean {\n if (options?.method && options.method !== \"GET\") return false;\n if (options?.body) return false;\n return true;\n}\n\n// --- Promise wrappers (bridge for non-Effect code) ---\n\nexport const fetchResponse = (url: string, options?: FetchOptions): Promise<Response> =>\n Effect.runPromise(fetchRawEff(url, options));\n\nexport const fetchJsonOrNull = async <T>(\n url: string,\n options?: FetchWithRetryOptions,\n): Promise<T | null> => {\n if (isCacheable(url, options)) {\n const cached = getCache.get(url);\n if (cached && cached.expiresAt > Date.now()) {\n return cached.data as T;\n }\n }\n\n const retries = options?.retries ?? DEFAULT_RETRIES;\n const eff =\n retries > 0 ? fetchWithRetryEff(url, { ...options, retries }) : fetchEff(url, options);\n\n try {\n const res = await Effect.runPromise(eff);\n const data = (await res.json()) as T;\n if (isCacheable(url, options)) {\n getCache.set(url, { data, expiresAt: Date.now() + GET_CACHE_TTL_MS });\n }\n return data;\n } catch (error) {\n const msg =\n error instanceof FetchNetworkError\n ? `[http] Network error: ${error.url} — ${String(error.cause)}`\n : error instanceof FetchTimeoutError\n ? `[http] Timeout: ${error.url}`\n : error instanceof FetchHttpError\n ? `[http] HTTP ${error.status}: ${error.url}`\n : `[http] Unknown error while fetching ${url}`;\n console.error(msg);\n return null;\n }\n};\n"],"mappings":"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"}
|
package/dist/integrity.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
const require_http_client = require('./http-client.cjs');
|
|
3
4
|
const require_fastkv = require('./fastkv.cjs');
|
|
4
5
|
let node_crypto = require("node:crypto");
|
|
5
6
|
|
|
@@ -40,7 +41,7 @@ async function computeSriHashFromResponse(response, url, options) {
|
|
|
40
41
|
async function computeSriHashForUrl(url, options) {
|
|
41
42
|
try {
|
|
42
43
|
const entryUrl = resolveSriTargetUrl(url, options);
|
|
43
|
-
const response = await
|
|
44
|
+
const response = await require_http_client.fetchResponse(entryUrl, { timeout: "30 seconds" });
|
|
44
45
|
if (!response.ok) {
|
|
45
46
|
console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);
|
|
46
47
|
return null;
|
|
@@ -58,7 +59,7 @@ function resolveEntryUrl(url) {
|
|
|
58
59
|
}
|
|
59
60
|
async function verifySriForUrl(url, expectedIntegrity, options) {
|
|
60
61
|
const entryUrl = resolveSriTargetUrl(url, options);
|
|
61
|
-
const response = await
|
|
62
|
+
const response = await require_http_client.fetchResponse(entryUrl, { timeout: "30 seconds" });
|
|
62
63
|
if (!response.ok) {
|
|
63
64
|
console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);
|
|
64
65
|
return;
|
package/dist/integrity.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity.cjs","names":["fetchBosConfigFromFastKv"],"sources":["../src/integrity.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { fetchBosConfigFromFastKv } from \"./fastkv\";\n\nconst DEFAULT_MAX_SRI_RESPONSE_BYTES = 20 * 1024 * 1024;\n\ninterface SriUrlOptions {\n resolveEntryUrl?: boolean;\n maxBytes?: number;\n}\n\nexport function computeSriHash(content: string | Buffer): string {\n return `sha384-${createHash(\"sha384\").update(content).digest(\"base64\")}`;\n}\n\nfunction resolveSriTargetUrl(url: string, options?: SriUrlOptions): string {\n return options?.resolveEntryUrl === false ? url : resolveEntryUrl(url);\n}\n\nfunction getMaxSriResponseBytes(options?: SriUrlOptions): number {\n return options?.maxBytes ?? DEFAULT_MAX_SRI_RESPONSE_BYTES;\n}\n\nasync function computeSriHashFromResponse(\n response: Response,\n url: string,\n options?: SriUrlOptions,\n): Promise<string> {\n const maxBytes = getMaxSriResponseBytes(options);\n const contentLengthHeader = response.headers.get(\"content-length\");\n\n if (contentLengthHeader) {\n const contentLength = Number(contentLengthHeader);\n if (Number.isFinite(contentLength) && contentLength > maxBytes) {\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${contentLength})`,\n );\n }\n }\n\n if (!response.body) {\n throw new Error(`[SRI] Missing response body for ${url}`);\n }\n\n const hash = createHash(\"sha384\");\n const reader = response.body.getReader();\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${totalBytes})`,\n );\n }\n\n hash.update(value);\n }\n\n return `sha384-${hash.digest(\"base64\")}`;\n}\n\nexport async function computeSriHashForUrl(\n url: string,\n options?: SriUrlOptions,\n): Promise<string | null> {\n try {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await
|
|
1
|
+
{"version":3,"file":"integrity.cjs","names":["fetchResponse","fetchBosConfigFromFastKv"],"sources":["../src/integrity.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { fetchBosConfigFromFastKv } from \"./fastkv\";\nimport { fetchResponse } from \"./http-client\";\n\nconst DEFAULT_MAX_SRI_RESPONSE_BYTES = 20 * 1024 * 1024;\n\ninterface SriUrlOptions {\n resolveEntryUrl?: boolean;\n maxBytes?: number;\n}\n\nexport function computeSriHash(content: string | Buffer): string {\n return `sha384-${createHash(\"sha384\").update(content).digest(\"base64\")}`;\n}\n\nfunction resolveSriTargetUrl(url: string, options?: SriUrlOptions): string {\n return options?.resolveEntryUrl === false ? url : resolveEntryUrl(url);\n}\n\nfunction getMaxSriResponseBytes(options?: SriUrlOptions): number {\n return options?.maxBytes ?? DEFAULT_MAX_SRI_RESPONSE_BYTES;\n}\n\nasync function computeSriHashFromResponse(\n response: Response,\n url: string,\n options?: SriUrlOptions,\n): Promise<string> {\n const maxBytes = getMaxSriResponseBytes(options);\n const contentLengthHeader = response.headers.get(\"content-length\");\n\n if (contentLengthHeader) {\n const contentLength = Number(contentLengthHeader);\n if (Number.isFinite(contentLength) && contentLength > maxBytes) {\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${contentLength})`,\n );\n }\n }\n\n if (!response.body) {\n throw new Error(`[SRI] Missing response body for ${url}`);\n }\n\n const hash = createHash(\"sha384\");\n const reader = response.body.getReader();\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${totalBytes})`,\n );\n }\n\n hash.update(value);\n }\n\n return `sha384-${hash.digest(\"base64\")}`;\n}\n\nexport async function computeSriHashForUrl(\n url: string,\n options?: SriUrlOptions,\n): Promise<string | null> {\n try {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);\n return null;\n }\n return await computeSriHashFromResponse(response, entryUrl, options);\n } catch (error) {\n console.warn(\n `[SRI] Error computing integrity for ${url}:`,\n error instanceof Error ? error.message : error,\n );\n return null;\n }\n}\n\nexport function resolveEntryUrl(url: string): string {\n if (url.endsWith(\"/remoteEntry.js\")) return url;\n if (url.endsWith(\"/mf-manifest.json\"))\n return `${url.replace(/\\/mf-manifest\\.json$/, \"\")}/remoteEntry.js`;\n return `${url.replace(/\\/$/, \"\")}/remoteEntry.js`;\n}\n\nexport async function verifySriForUrl(\n url: string,\n expectedIntegrity: string,\n options?: SriUrlOptions,\n): Promise<void> {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);\n return;\n }\n\n const computed = await computeSriHashFromResponse(response, entryUrl, options);\n\n if (computed !== expectedIntegrity) {\n throw new Error(\n `[SRI] Integrity check failed for ${entryUrl}\\n Expected: ${expectedIntegrity}\\n Computed: ${computed}`,\n );\n }\n}\n\nexport class IntegrityRegistry {\n private hashes = new Map<string, string>();\n\n register(url: string, integrity: string): void {\n this.hashes.set(url, integrity);\n }\n\n registerEntry(baseUrl: string, integrity: string): void {\n this.hashes.set(resolveEntryUrl(baseUrl), integrity);\n }\n\n get(url: string): string | undefined {\n return this.hashes.get(url);\n }\n\n has(url: string): boolean {\n return this.hashes.has(url);\n }\n\n entries(): IterableIterator<[string, string]> {\n return this.hashes.entries();\n }\n}\n\nfunction extractIntegrityHashes(config: Record<string, unknown>): Map<string, string> {\n const hashes = new Map<string, string>();\n const app = config.app as Record<string, Record<string, unknown>> | undefined;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n\n if (app) {\n for (const [, entry] of Object.entries(app)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n if (plugins) {\n for (const [, entry] of Object.entries(plugins)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n return hashes;\n}\n\nexport async function verifyConfigAgainstChain(\n localConfig: Record<string, unknown>,\n bosUrl: string,\n): Promise<{ verified: boolean; mismatches: string[] }> {\n const mismatches: string[] = [];\n\n let chainConfig: Record<string, unknown>;\n try {\n chainConfig = await fetchBosConfigFromFastKv<Record<string, unknown>>(bosUrl);\n } catch (error) {\n console.warn(\n `[Attestation] Failed to fetch on-chain config: ${error instanceof Error ? error.message : String(error)}`,\n );\n return { verified: false, mismatches: [\"chain-fetch-failed\"] };\n }\n\n const localHashes = extractIntegrityHashes(localConfig);\n const chainHashes = extractIntegrityHashes(chainConfig);\n\n for (const [url, chainHash] of chainHashes) {\n const localHash = localHashes.get(url);\n if (localHash && localHash !== chainHash) {\n mismatches.push(url);\n console.error(\n `[Attestation] Integrity mismatch for ${url}\\n Local: ${localHash}\\n Chain: ${chainHash}`,\n );\n }\n }\n\n if (mismatches.length === 0 && localHashes.size > 0) {\n console.log(\n `[Attestation] Local config verified against on-chain anchor (${localHashes.size} entries checked)`,\n );\n }\n\n return { verified: mismatches.length === 0, mismatches };\n}\n"],"mappings":"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"}
|
package/dist/integrity.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity.d.cts","names":[],"sources":["../src/integrity.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"integrity.d.cts","names":[],"sources":["../src/integrity.ts"],"mappings":";UAMU,aAAA;EACR,eAAA;EACA,QAAA;AAAA;AAAA,iBAGc,cAAA,CAAe,OAAA,WAAkB,MAAA;AAAA,iBAyD3B,oBAAA,CACpB,GAAA,UACA,OAAA,GAAU,aAAA,GACT,OAAA;AAAA,iBAmBa,eAAA,CAAgB,GAAA;AAAA,iBAOV,eAAA,CACpB,GAAA,UACA,iBAAA,UACA,OAAA,GAAU,aAAA,GACT,OAAA;AAAA,cAkBU,iBAAA;EAAA,QACH,MAAA;EAER,QAAA,CAAS,GAAA,UAAa,SAAA;EAItB,aAAA,CAAc,OAAA,UAAiB,SAAA;EAI/B,GAAA,CAAI,GAAA;EAIJ,GAAA,CAAI,GAAA;EAIJ,OAAA,CAAA,GAAW,gBAAA;AAAA;AAAA,iBA6BS,wBAAA,CACpB,WAAA,EAAa,MAAA,mBACb,MAAA,WACC,OAAA;EAAU,QAAA;EAAmB,UAAA;AAAA"}
|
package/dist/integrity.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity.d.mts","names":[],"sources":["../src/integrity.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"integrity.d.mts","names":[],"sources":["../src/integrity.ts"],"mappings":";UAMU,aAAA;EACR,eAAA;EACA,QAAA;AAAA;AAAA,iBAGc,cAAA,CAAe,OAAA,WAAkB,MAAA;AAAA,iBAyD3B,oBAAA,CACpB,GAAA,UACA,OAAA,GAAU,aAAA,GACT,OAAA;AAAA,iBAmBa,eAAA,CAAgB,GAAA;AAAA,iBAOV,eAAA,CACpB,GAAA,UACA,iBAAA,UACA,OAAA,GAAU,aAAA,GACT,OAAA;AAAA,cAkBU,iBAAA;EAAA,QACH,MAAA;EAER,QAAA,CAAS,GAAA,UAAa,SAAA;EAItB,aAAA,CAAc,OAAA,UAAiB,SAAA;EAI/B,GAAA,CAAI,GAAA;EAIJ,GAAA,CAAI,GAAA;EAIJ,OAAA,CAAA,GAAW,gBAAA;AAAA;AAAA,iBA6BS,wBAAA,CACpB,WAAA,EAAa,MAAA,mBACb,MAAA,WACC,OAAA;EAAU,QAAA;EAAmB,UAAA;AAAA"}
|
package/dist/integrity.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fetchResponse } from "./http-client.mjs";
|
|
1
2
|
import { fetchBosConfigFromFastKv } from "./fastkv.mjs";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
|
|
@@ -38,7 +39,7 @@ async function computeSriHashFromResponse(response, url, options) {
|
|
|
38
39
|
async function computeSriHashForUrl(url, options) {
|
|
39
40
|
try {
|
|
40
41
|
const entryUrl = resolveSriTargetUrl(url, options);
|
|
41
|
-
const response = await
|
|
42
|
+
const response = await fetchResponse(entryUrl, { timeout: "30 seconds" });
|
|
42
43
|
if (!response.ok) {
|
|
43
44
|
console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);
|
|
44
45
|
return null;
|
|
@@ -56,7 +57,7 @@ function resolveEntryUrl(url) {
|
|
|
56
57
|
}
|
|
57
58
|
async function verifySriForUrl(url, expectedIntegrity, options) {
|
|
58
59
|
const entryUrl = resolveSriTargetUrl(url, options);
|
|
59
|
-
const response = await
|
|
60
|
+
const response = await fetchResponse(entryUrl, { timeout: "30 seconds" });
|
|
60
61
|
if (!response.ok) {
|
|
61
62
|
console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);
|
|
62
63
|
return;
|
package/dist/integrity.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity.mjs","names":[],"sources":["../src/integrity.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { fetchBosConfigFromFastKv } from \"./fastkv\";\n\nconst DEFAULT_MAX_SRI_RESPONSE_BYTES = 20 * 1024 * 1024;\n\ninterface SriUrlOptions {\n resolveEntryUrl?: boolean;\n maxBytes?: number;\n}\n\nexport function computeSriHash(content: string | Buffer): string {\n return `sha384-${createHash(\"sha384\").update(content).digest(\"base64\")}`;\n}\n\nfunction resolveSriTargetUrl(url: string, options?: SriUrlOptions): string {\n return options?.resolveEntryUrl === false ? url : resolveEntryUrl(url);\n}\n\nfunction getMaxSriResponseBytes(options?: SriUrlOptions): number {\n return options?.maxBytes ?? DEFAULT_MAX_SRI_RESPONSE_BYTES;\n}\n\nasync function computeSriHashFromResponse(\n response: Response,\n url: string,\n options?: SriUrlOptions,\n): Promise<string> {\n const maxBytes = getMaxSriResponseBytes(options);\n const contentLengthHeader = response.headers.get(\"content-length\");\n\n if (contentLengthHeader) {\n const contentLength = Number(contentLengthHeader);\n if (Number.isFinite(contentLength) && contentLength > maxBytes) {\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${contentLength})`,\n );\n }\n }\n\n if (!response.body) {\n throw new Error(`[SRI] Missing response body for ${url}`);\n }\n\n const hash = createHash(\"sha384\");\n const reader = response.body.getReader();\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${totalBytes})`,\n );\n }\n\n hash.update(value);\n }\n\n return `sha384-${hash.digest(\"base64\")}`;\n}\n\nexport async function computeSriHashForUrl(\n url: string,\n options?: SriUrlOptions,\n): Promise<string | null> {\n try {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await
|
|
1
|
+
{"version":3,"file":"integrity.mjs","names":[],"sources":["../src/integrity.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { fetchBosConfigFromFastKv } from \"./fastkv\";\nimport { fetchResponse } from \"./http-client\";\n\nconst DEFAULT_MAX_SRI_RESPONSE_BYTES = 20 * 1024 * 1024;\n\ninterface SriUrlOptions {\n resolveEntryUrl?: boolean;\n maxBytes?: number;\n}\n\nexport function computeSriHash(content: string | Buffer): string {\n return `sha384-${createHash(\"sha384\").update(content).digest(\"base64\")}`;\n}\n\nfunction resolveSriTargetUrl(url: string, options?: SriUrlOptions): string {\n return options?.resolveEntryUrl === false ? url : resolveEntryUrl(url);\n}\n\nfunction getMaxSriResponseBytes(options?: SriUrlOptions): number {\n return options?.maxBytes ?? DEFAULT_MAX_SRI_RESPONSE_BYTES;\n}\n\nasync function computeSriHashFromResponse(\n response: Response,\n url: string,\n options?: SriUrlOptions,\n): Promise<string> {\n const maxBytes = getMaxSriResponseBytes(options);\n const contentLengthHeader = response.headers.get(\"content-length\");\n\n if (contentLengthHeader) {\n const contentLength = Number(contentLengthHeader);\n if (Number.isFinite(contentLength) && contentLength > maxBytes) {\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${contentLength})`,\n );\n }\n }\n\n if (!response.body) {\n throw new Error(`[SRI] Missing response body for ${url}`);\n }\n\n const hash = createHash(\"sha384\");\n const reader = response.body.getReader();\n let totalBytes = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n totalBytes += value.byteLength;\n if (totalBytes > maxBytes) {\n await reader.cancel();\n throw new Error(\n `[SRI] Response for ${url} exceeds max size of ${maxBytes} bytes (${totalBytes})`,\n );\n }\n\n hash.update(value);\n }\n\n return `sha384-${hash.digest(\"base64\")}`;\n}\n\nexport async function computeSriHashForUrl(\n url: string,\n options?: SriUrlOptions,\n): Promise<string | null> {\n try {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl}: ${response.status} ${response.statusText}`);\n return null;\n }\n return await computeSriHashFromResponse(response, entryUrl, options);\n } catch (error) {\n console.warn(\n `[SRI] Error computing integrity for ${url}:`,\n error instanceof Error ? error.message : error,\n );\n return null;\n }\n}\n\nexport function resolveEntryUrl(url: string): string {\n if (url.endsWith(\"/remoteEntry.js\")) return url;\n if (url.endsWith(\"/mf-manifest.json\"))\n return `${url.replace(/\\/mf-manifest\\.json$/, \"\")}/remoteEntry.js`;\n return `${url.replace(/\\/$/, \"\")}/remoteEntry.js`;\n}\n\nexport async function verifySriForUrl(\n url: string,\n expectedIntegrity: string,\n options?: SriUrlOptions,\n): Promise<void> {\n const entryUrl = resolveSriTargetUrl(url, options);\n\n const response = await fetchResponse(entryUrl, { timeout: \"30 seconds\" });\n if (!response.ok) {\n console.warn(`[SRI] Failed to fetch ${entryUrl} for verification: ${response.status}`);\n return;\n }\n\n const computed = await computeSriHashFromResponse(response, entryUrl, options);\n\n if (computed !== expectedIntegrity) {\n throw new Error(\n `[SRI] Integrity check failed for ${entryUrl}\\n Expected: ${expectedIntegrity}\\n Computed: ${computed}`,\n );\n }\n}\n\nexport class IntegrityRegistry {\n private hashes = new Map<string, string>();\n\n register(url: string, integrity: string): void {\n this.hashes.set(url, integrity);\n }\n\n registerEntry(baseUrl: string, integrity: string): void {\n this.hashes.set(resolveEntryUrl(baseUrl), integrity);\n }\n\n get(url: string): string | undefined {\n return this.hashes.get(url);\n }\n\n has(url: string): boolean {\n return this.hashes.has(url);\n }\n\n entries(): IterableIterator<[string, string]> {\n return this.hashes.entries();\n }\n}\n\nfunction extractIntegrityHashes(config: Record<string, unknown>): Map<string, string> {\n const hashes = new Map<string, string>();\n const app = config.app as Record<string, Record<string, unknown>> | undefined;\n const plugins = config.plugins as Record<string, Record<string, unknown>> | undefined;\n\n if (app) {\n for (const [, entry] of Object.entries(app)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n if (plugins) {\n for (const [, entry] of Object.entries(plugins)) {\n if (entry?.integrity && entry?.production) {\n hashes.set(resolveEntryUrl(entry.production as string), entry.integrity as string);\n }\n }\n }\n\n return hashes;\n}\n\nexport async function verifyConfigAgainstChain(\n localConfig: Record<string, unknown>,\n bosUrl: string,\n): Promise<{ verified: boolean; mismatches: string[] }> {\n const mismatches: string[] = [];\n\n let chainConfig: Record<string, unknown>;\n try {\n chainConfig = await fetchBosConfigFromFastKv<Record<string, unknown>>(bosUrl);\n } catch (error) {\n console.warn(\n `[Attestation] Failed to fetch on-chain config: ${error instanceof Error ? error.message : String(error)}`,\n );\n return { verified: false, mismatches: [\"chain-fetch-failed\"] };\n }\n\n const localHashes = extractIntegrityHashes(localConfig);\n const chainHashes = extractIntegrityHashes(chainConfig);\n\n for (const [url, chainHash] of chainHashes) {\n const localHash = localHashes.get(url);\n if (localHash && localHash !== chainHash) {\n mismatches.push(url);\n console.error(\n `[Attestation] Integrity mismatch for ${url}\\n Local: ${localHash}\\n Chain: ${chainHash}`,\n );\n }\n }\n\n if (mismatches.length === 0 && localHashes.size > 0) {\n console.log(\n `[Attestation] Local config verified against on-chain anchor (${localHashes.size} entries checked)`,\n );\n }\n\n return { verified: mismatches.length === 0, mismatches };\n}\n"],"mappings":"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"}
|
package/dist/mf.cjs
CHANGED
|
@@ -6,13 +6,21 @@ let _module_federation_runtime_core = require("@module-federation/runtime-core")
|
|
|
6
6
|
|
|
7
7
|
//#region src/mf.ts
|
|
8
8
|
let mfInstance = null;
|
|
9
|
+
function fetchWithTimeout(url, init, timeoutMs) {
|
|
10
|
+
const controller = new AbortController();
|
|
11
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
12
|
+
return fetch(url, {
|
|
13
|
+
...init,
|
|
14
|
+
signal: controller.signal
|
|
15
|
+
}).finally(() => clearTimeout(timer));
|
|
16
|
+
}
|
|
9
17
|
function patchManifestFetchForSsrPublicPath(mf) {
|
|
10
18
|
if (!mf || !mf.loaderHook?.lifecycle?.fetch?.on) return;
|
|
11
19
|
if (mf.__everythingDevPatchedManifestFetch === true) return;
|
|
12
20
|
mf.__everythingDevPatchedManifestFetch = true;
|
|
13
21
|
mf.loaderHook.lifecycle.fetch.on((url, init) => {
|
|
14
22
|
if (typeof url !== "string" || !url.endsWith("/mf-manifest.json")) return;
|
|
15
|
-
return
|
|
23
|
+
return fetchWithTimeout(url, init, 15e3).then((res) => res.json()).then((json) => {
|
|
16
24
|
json.metaData = json.metaData ?? {};
|
|
17
25
|
json.metaData.ssrPublicPath = json.metaData.ssrPublicPath ?? url.replace(/\/mf-manifest\.json$/, "/");
|
|
18
26
|
if (json.metaData.publicPath === "auto" || json.metaData.publicPath === "") json.metaData.publicPath = "/";
|
|
@@ -31,7 +39,7 @@ function installIntegrityFetchHook(mf, registry) {
|
|
|
31
39
|
if (typeof url !== "string") return;
|
|
32
40
|
const expectedHash = registry.get(url);
|
|
33
41
|
if (!expectedHash) return;
|
|
34
|
-
return
|
|
42
|
+
return fetchWithTimeout(url, init, 15e3).then(async (res) => {
|
|
35
43
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
36
44
|
const computed = require_integrity.computeSriHash(buffer);
|
|
37
45
|
if (computed !== expectedHash) {
|
package/dist/mf.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mf.cjs","names":["computeSriHash"],"sources":["../src/mf.ts"],"sourcesContent":["import { createInstance, getInstance } from \"@module-federation/enhanced/runtime\";\nimport { setGlobalFederationInstance } from \"@module-federation/runtime-core\";\nimport { computeSriHash, type IntegrityRegistry } from \"./integrity\";\n\ntype FederationInstance = ReturnType<typeof createInstance>;\n\nlet mfInstance: FederationInstance | null = null;\n\nexport function patchManifestFetchForSsrPublicPath(mf: FederationInstance): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) return;\n if ((mf as any).__everythingDevPatchedManifestFetch === true) return;\n (mf as any).__everythingDevPatchedManifestFetch = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\" || !url.endsWith(\"/mf-manifest.json\")) {\n return;\n }\n return
|
|
1
|
+
{"version":3,"file":"mf.cjs","names":["computeSriHash"],"sources":["../src/mf.ts"],"sourcesContent":["import { createInstance, getInstance } from \"@module-federation/enhanced/runtime\";\nimport { setGlobalFederationInstance } from \"@module-federation/runtime-core\";\nimport { computeSriHash, type IntegrityRegistry } from \"./integrity\";\n\ntype FederationInstance = ReturnType<typeof createInstance>;\n\nlet mfInstance: FederationInstance | null = null;\n\nfunction fetchWithTimeout(url: string, init: RequestInit, timeoutMs: number): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n return fetch(url, { ...init, signal: controller.signal }).finally(() => clearTimeout(timer));\n}\n\nexport function patchManifestFetchForSsrPublicPath(mf: FederationInstance): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) return;\n if ((mf as any).__everythingDevPatchedManifestFetch === true) return;\n (mf as any).__everythingDevPatchedManifestFetch = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\" || !url.endsWith(\"/mf-manifest.json\")) {\n return;\n }\n return fetchWithTimeout(url as string, init as RequestInit, 15_000)\n .then((res) => res.json())\n .then((json: any) => {\n json.metaData = json.metaData ?? {};\n json.metaData.ssrPublicPath =\n json.metaData.ssrPublicPath ?? url.replace(/\\/mf-manifest\\.json$/, \"/\");\n if (json.metaData.publicPath === \"auto\" || json.metaData.publicPath === \"\") {\n json.metaData.publicPath = \"/\";\n }\n return new Response(JSON.stringify(json), {\n headers: { \"content-type\": \"application/json\" },\n });\n });\n });\n}\n\nexport function installIntegrityFetchHook(\n mf: FederationInstance,\n registry: IntegrityRegistry,\n): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) {\n console.warn(\"[SRI] MF lifecycle fetch hook not available, skipping integrity-in-pipeline\");\n return;\n }\n if ((mf as any).__everythingDevIntegrityHook === true) return;\n (mf as any).__everythingDevIntegrityHook = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\") return;\n\n const expectedHash = registry.get(url);\n if (!expectedHash) return;\n\n return fetchWithTimeout(url as string, init as RequestInit, 15_000).then(async (res) => {\n const buffer = Buffer.from(await res.arrayBuffer());\n const computed = computeSriHash(buffer);\n\n if (computed !== expectedHash) {\n console.error(\n `[SRI] Integrity check failed in MF fetch pipeline for ${url}\\n Expected: ${expectedHash}\\n Computed: ${computed}`,\n );\n return new Response(`Integrity check failed for ${url}`, {\n status: 500,\n statusText: \"Integrity Check Failed\",\n });\n }\n\n console.log(`[SRI] Integrity verified in pipeline for ${url}`);\n return new Response(buffer, {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n });\n });\n}\n\nexport function getFederationInstance(): FederationInstance {\n if (mfInstance) return mfInstance;\n\n const existing = getInstance();\n if (existing) {\n mfInstance = existing as FederationInstance;\n setGlobalFederationInstance(mfInstance as any);\n patchManifestFetchForSsrPublicPath(mfInstance);\n return mfInstance;\n }\n\n mfInstance = createInstance({\n name: \"host\",\n remotes: [],\n }) as FederationInstance;\n setGlobalFederationInstance(mfInstance as any);\n patchManifestFetchForSsrPublicPath(mfInstance);\n return mfInstance;\n}\n\nexport async function registerRemote(opts: {\n name: string;\n entry: string;\n type?: \"manifest\" | \"script\";\n}): Promise<void> {\n const instance = getFederationInstance();\n\n const inferType = (): \"manifest\" | \"script\" => {\n if (opts.type) return opts.type;\n if (opts.entry.endsWith(\"/mf-manifest.json\")) return \"manifest\";\n if (opts.entry.endsWith(\"/remoteEntry.js\")) return \"script\";\n return typeof window === \"undefined\" ? \"script\" : \"manifest\";\n };\n\n const remoteType = inferType();\n\n instance.registerRemotes([\n {\n name: opts.name,\n entry: opts.entry,\n type: remoteType,\n },\n ]);\n}\n\nexport async function loadRemoteModule<T>(\n specifier: string,\n options?: { loadFactory?: boolean; from?: \"build\" | \"runtime\" },\n): Promise<T> {\n const instance = getFederationInstance();\n\n const isServer = typeof window === \"undefined\";\n if (isServer) {\n await (instance as any).initializeSharing?.(\"default\");\n }\n\n const mod = await instance.loadRemote<T>(specifier, options as any);\n if (!mod) {\n throw new Error(`Failed to load remote module: ${specifier}`);\n }\n return mod;\n}\n\nexport async function ensureNodeRuntimePlugin(): Promise<void> {\n const instance = getFederationInstance();\n if (typeof window !== \"undefined\") return;\n if ((instance as any).__nodeRuntimePluginLoaded) return;\n\n const mod: any = await import(\"@module-federation/node/runtimePlugin\");\n const factory = mod?.default ?? mod;\n const plugin = typeof factory === \"function\" ? factory() : null;\n if (plugin) {\n instance.registerPlugins([plugin]);\n }\n (instance as any).__nodeRuntimePluginLoaded = true;\n}\n"],"mappings":"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"}
|
package/dist/mf.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mf.d.cts","names":[],"sources":["../src/mf.ts"],"mappings":";;;;KAIK,kBAAA,GAAqB,UAAA,QAAkB,cAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"mf.d.cts","names":[],"sources":["../src/mf.ts"],"mappings":";;;;KAIK,kBAAA,GAAqB,UAAA,QAAkB,cAAA;AAAA,iBAU5B,kCAAA,CAAmC,EAAA,EAAI,kBAAA;AAAA,iBAyBvC,yBAAA,CACd,EAAA,EAAI,kBAAA,EACJ,QAAA,EAAU,iBAAA;AAAA,iBAuCI,qBAAA,CAAA,GAAyB,kBAAA;AAAA,iBAoBnB,cAAA,CAAe,IAAA;EACnC,IAAA;EACA,KAAA;EACA,IAAA;AAAA,IACE,OAAA;AAAA,iBAqBkB,gBAAA,GAAA,CACpB,SAAA,UACA,OAAA;EAAY,WAAA;EAAuB,IAAA;AAAA,IAClC,OAAA,CAAQ,CAAA;AAAA,iBAeW,uBAAA,CAAA,GAA2B,OAAA"}
|
package/dist/mf.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mf.d.mts","names":[],"sources":["../src/mf.ts"],"mappings":";;;;KAIK,kBAAA,GAAqB,UAAA,QAAkB,cAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"mf.d.mts","names":[],"sources":["../src/mf.ts"],"mappings":";;;;KAIK,kBAAA,GAAqB,UAAA,QAAkB,cAAA;AAAA,iBAU5B,kCAAA,CAAmC,EAAA,EAAI,kBAAA;AAAA,iBAyBvC,yBAAA,CACd,EAAA,EAAI,kBAAA,EACJ,QAAA,EAAU,iBAAA;AAAA,iBAuCI,qBAAA,CAAA,GAAyB,kBAAA;AAAA,iBAoBnB,cAAA,CAAe,IAAA;EACnC,IAAA;EACA,KAAA;EACA,IAAA;AAAA,IACE,OAAA;AAAA,iBAqBkB,gBAAA,GAAA,CACpB,SAAA,UACA,OAAA;EAAY,WAAA;EAAuB,IAAA;AAAA,IAClC,OAAA,CAAQ,CAAA;AAAA,iBAeW,uBAAA,CAAA,GAA2B,OAAA"}
|
package/dist/mf.mjs
CHANGED
|
@@ -4,13 +4,21 @@ import { setGlobalFederationInstance } from "@module-federation/runtime-core";
|
|
|
4
4
|
|
|
5
5
|
//#region src/mf.ts
|
|
6
6
|
let mfInstance = null;
|
|
7
|
+
function fetchWithTimeout(url, init, timeoutMs) {
|
|
8
|
+
const controller = new AbortController();
|
|
9
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
10
|
+
return fetch(url, {
|
|
11
|
+
...init,
|
|
12
|
+
signal: controller.signal
|
|
13
|
+
}).finally(() => clearTimeout(timer));
|
|
14
|
+
}
|
|
7
15
|
function patchManifestFetchForSsrPublicPath(mf) {
|
|
8
16
|
if (!mf || !mf.loaderHook?.lifecycle?.fetch?.on) return;
|
|
9
17
|
if (mf.__everythingDevPatchedManifestFetch === true) return;
|
|
10
18
|
mf.__everythingDevPatchedManifestFetch = true;
|
|
11
19
|
mf.loaderHook.lifecycle.fetch.on((url, init) => {
|
|
12
20
|
if (typeof url !== "string" || !url.endsWith("/mf-manifest.json")) return;
|
|
13
|
-
return
|
|
21
|
+
return fetchWithTimeout(url, init, 15e3).then((res) => res.json()).then((json) => {
|
|
14
22
|
json.metaData = json.metaData ?? {};
|
|
15
23
|
json.metaData.ssrPublicPath = json.metaData.ssrPublicPath ?? url.replace(/\/mf-manifest\.json$/, "/");
|
|
16
24
|
if (json.metaData.publicPath === "auto" || json.metaData.publicPath === "") json.metaData.publicPath = "/";
|
|
@@ -29,7 +37,7 @@ function installIntegrityFetchHook(mf, registry) {
|
|
|
29
37
|
if (typeof url !== "string") return;
|
|
30
38
|
const expectedHash = registry.get(url);
|
|
31
39
|
if (!expectedHash) return;
|
|
32
|
-
return
|
|
40
|
+
return fetchWithTimeout(url, init, 15e3).then(async (res) => {
|
|
33
41
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
34
42
|
const computed = computeSriHash(buffer);
|
|
35
43
|
if (computed !== expectedHash) {
|
package/dist/mf.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mf.mjs","names":[],"sources":["../src/mf.ts"],"sourcesContent":["import { createInstance, getInstance } from \"@module-federation/enhanced/runtime\";\nimport { setGlobalFederationInstance } from \"@module-federation/runtime-core\";\nimport { computeSriHash, type IntegrityRegistry } from \"./integrity\";\n\ntype FederationInstance = ReturnType<typeof createInstance>;\n\nlet mfInstance: FederationInstance | null = null;\n\nexport function patchManifestFetchForSsrPublicPath(mf: FederationInstance): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) return;\n if ((mf as any).__everythingDevPatchedManifestFetch === true) return;\n (mf as any).__everythingDevPatchedManifestFetch = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\" || !url.endsWith(\"/mf-manifest.json\")) {\n return;\n }\n return
|
|
1
|
+
{"version":3,"file":"mf.mjs","names":[],"sources":["../src/mf.ts"],"sourcesContent":["import { createInstance, getInstance } from \"@module-federation/enhanced/runtime\";\nimport { setGlobalFederationInstance } from \"@module-federation/runtime-core\";\nimport { computeSriHash, type IntegrityRegistry } from \"./integrity\";\n\ntype FederationInstance = ReturnType<typeof createInstance>;\n\nlet mfInstance: FederationInstance | null = null;\n\nfunction fetchWithTimeout(url: string, init: RequestInit, timeoutMs: number): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n return fetch(url, { ...init, signal: controller.signal }).finally(() => clearTimeout(timer));\n}\n\nexport function patchManifestFetchForSsrPublicPath(mf: FederationInstance): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) return;\n if ((mf as any).__everythingDevPatchedManifestFetch === true) return;\n (mf as any).__everythingDevPatchedManifestFetch = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\" || !url.endsWith(\"/mf-manifest.json\")) {\n return;\n }\n return fetchWithTimeout(url as string, init as RequestInit, 15_000)\n .then((res) => res.json())\n .then((json: any) => {\n json.metaData = json.metaData ?? {};\n json.metaData.ssrPublicPath =\n json.metaData.ssrPublicPath ?? url.replace(/\\/mf-manifest\\.json$/, \"/\");\n if (json.metaData.publicPath === \"auto\" || json.metaData.publicPath === \"\") {\n json.metaData.publicPath = \"/\";\n }\n return new Response(JSON.stringify(json), {\n headers: { \"content-type\": \"application/json\" },\n });\n });\n });\n}\n\nexport function installIntegrityFetchHook(\n mf: FederationInstance,\n registry: IntegrityRegistry,\n): void {\n if (!mf || !(mf as any).loaderHook?.lifecycle?.fetch?.on) {\n console.warn(\"[SRI] MF lifecycle fetch hook not available, skipping integrity-in-pipeline\");\n return;\n }\n if ((mf as any).__everythingDevIntegrityHook === true) return;\n (mf as any).__everythingDevIntegrityHook = true;\n\n (mf as any).loaderHook.lifecycle.fetch.on((url: unknown, init: unknown) => {\n if (typeof url !== \"string\") return;\n\n const expectedHash = registry.get(url);\n if (!expectedHash) return;\n\n return fetchWithTimeout(url as string, init as RequestInit, 15_000).then(async (res) => {\n const buffer = Buffer.from(await res.arrayBuffer());\n const computed = computeSriHash(buffer);\n\n if (computed !== expectedHash) {\n console.error(\n `[SRI] Integrity check failed in MF fetch pipeline for ${url}\\n Expected: ${expectedHash}\\n Computed: ${computed}`,\n );\n return new Response(`Integrity check failed for ${url}`, {\n status: 500,\n statusText: \"Integrity Check Failed\",\n });\n }\n\n console.log(`[SRI] Integrity verified in pipeline for ${url}`);\n return new Response(buffer, {\n status: res.status,\n statusText: res.statusText,\n headers: res.headers,\n });\n });\n });\n}\n\nexport function getFederationInstance(): FederationInstance {\n if (mfInstance) return mfInstance;\n\n const existing = getInstance();\n if (existing) {\n mfInstance = existing as FederationInstance;\n setGlobalFederationInstance(mfInstance as any);\n patchManifestFetchForSsrPublicPath(mfInstance);\n return mfInstance;\n }\n\n mfInstance = createInstance({\n name: \"host\",\n remotes: [],\n }) as FederationInstance;\n setGlobalFederationInstance(mfInstance as any);\n patchManifestFetchForSsrPublicPath(mfInstance);\n return mfInstance;\n}\n\nexport async function registerRemote(opts: {\n name: string;\n entry: string;\n type?: \"manifest\" | \"script\";\n}): Promise<void> {\n const instance = getFederationInstance();\n\n const inferType = (): \"manifest\" | \"script\" => {\n if (opts.type) return opts.type;\n if (opts.entry.endsWith(\"/mf-manifest.json\")) return \"manifest\";\n if (opts.entry.endsWith(\"/remoteEntry.js\")) return \"script\";\n return typeof window === \"undefined\" ? \"script\" : \"manifest\";\n };\n\n const remoteType = inferType();\n\n instance.registerRemotes([\n {\n name: opts.name,\n entry: opts.entry,\n type: remoteType,\n },\n ]);\n}\n\nexport async function loadRemoteModule<T>(\n specifier: string,\n options?: { loadFactory?: boolean; from?: \"build\" | \"runtime\" },\n): Promise<T> {\n const instance = getFederationInstance();\n\n const isServer = typeof window === \"undefined\";\n if (isServer) {\n await (instance as any).initializeSharing?.(\"default\");\n }\n\n const mod = await instance.loadRemote<T>(specifier, options as any);\n if (!mod) {\n throw new Error(`Failed to load remote module: ${specifier}`);\n }\n return mod;\n}\n\nexport async function ensureNodeRuntimePlugin(): Promise<void> {\n const instance = getFederationInstance();\n if (typeof window !== \"undefined\") return;\n if ((instance as any).__nodeRuntimePluginLoaded) return;\n\n const mod: any = await import(\"@module-federation/node/runtimePlugin\");\n const factory = mod?.default ?? mod;\n const plugin = typeof factory === \"function\" ? factory() : null;\n if (plugin) {\n instance.registerPlugins([plugin]);\n }\n (instance as any).__nodeRuntimePluginLoaded = true;\n}\n"],"mappings":"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"}
|
package/dist/near-cli.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
2
|
const require_theme = require('./utils/theme.cjs');
|
|
3
|
-
let node_crypto = require("node:crypto");
|
|
4
3
|
let effect = require("effect");
|
|
4
|
+
let node_crypto = require("node:crypto");
|
|
5
5
|
let execa = require("execa");
|
|
6
6
|
|
|
7
7
|
//#region src/near-cli.ts
|
package/dist/near-cli.mjs
CHANGED
package/dist/orchestrator.cjs
CHANGED
|
@@ -30,6 +30,12 @@ const probeHttpOk = (url, timeoutMs = 400) => effect.Effect.tryPromise({
|
|
|
30
30
|
},
|
|
31
31
|
catch: () => false
|
|
32
32
|
});
|
|
33
|
+
const LOCAL_PROBE_DEADLINE_MS = 9e4;
|
|
34
|
+
const LOCAL_PROBE_INTERVAL_MS = 200;
|
|
35
|
+
const REMOTE_PROBE_TIMEOUT_MS = 5e3;
|
|
36
|
+
const REMOTE_PROBE_DEADLINE_MS = 6e4;
|
|
37
|
+
const REMOTE_PROBE_BACKOFF_INITIAL_MS = 1e3;
|
|
38
|
+
const REMOTE_PROBE_BACKOFF_MAX_MS = 15e3;
|
|
33
39
|
const detectStatus = (line, descriptor) => {
|
|
34
40
|
const cleanLine = stripAnsi(line);
|
|
35
41
|
const errorPatterns = descriptor.errorPatterns ?? [];
|
|
@@ -185,10 +191,17 @@ const spawnDevProcess = (descriptor, callbacks) => effect.Effect.gen(function* (
|
|
|
185
191
|
callbacks.onStatus(name, "ready");
|
|
186
192
|
yield* effect.Deferred.succeed(readyDeferred, void 0).pipe(effect.Effect.ignore);
|
|
187
193
|
});
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
194
|
+
const markError = (message) => effect.Effect.gen(function* () {
|
|
195
|
+
const currentStatus = yield* effect.Ref.get(statusRef);
|
|
196
|
+
if (currentStatus === "ready" || currentStatus === "error") return;
|
|
197
|
+
yield* effect.Ref.set(statusRef, "error");
|
|
198
|
+
callbacks.onStatus(name, "error");
|
|
199
|
+
yield* effect.Deferred.fail(readyDeferred, new Error(message)).pipe(effect.Effect.ignore);
|
|
200
|
+
});
|
|
201
|
+
yield* effect.Effect.forkScoped(effect.Effect.gen(function* () {
|
|
202
|
+
const deadline = Date.now() + LOCAL_PROBE_DEADLINE_MS;
|
|
203
|
+
if (port > 0) {
|
|
204
|
+
const url = `http://127.0.0.1:${port}${descriptor.readinessPath}`;
|
|
192
205
|
while (Date.now() < deadline) {
|
|
193
206
|
const status = yield* effect.Ref.get(statusRef);
|
|
194
207
|
if (status === "ready" || status === "error") return;
|
|
@@ -196,19 +209,26 @@ const spawnDevProcess = (descriptor, callbacks) => effect.Effect.gen(function* (
|
|
|
196
209
|
yield* markReady;
|
|
197
210
|
return;
|
|
198
211
|
}
|
|
199
|
-
yield* effect.Effect.sleep(
|
|
212
|
+
yield* effect.Effect.sleep(`${LOCAL_PROBE_INTERVAL_MS} millis`);
|
|
200
213
|
}
|
|
201
|
-
}))
|
|
202
|
-
|
|
214
|
+
} else while (Date.now() < deadline) {
|
|
215
|
+
const status = yield* effect.Ref.get(statusRef);
|
|
216
|
+
if (status === "ready" || status === "error") return;
|
|
217
|
+
yield* effect.Effect.sleep("500 millis");
|
|
218
|
+
}
|
|
219
|
+
const status = yield* effect.Ref.get(statusRef);
|
|
220
|
+
if (status !== "ready" && status !== "error") {
|
|
221
|
+
callbacks.onLog(name, "Probe deadline exceeded after 90s", true);
|
|
222
|
+
yield* markError(`Probe deadline exceeded: ${name}`);
|
|
223
|
+
}
|
|
224
|
+
}));
|
|
203
225
|
const pid = Number(proc.pid);
|
|
204
226
|
yield* effect.Effect.forkScoped(effect.Effect.gen(function* () {
|
|
205
227
|
const exitCode = yield* proc.exitCode;
|
|
206
228
|
const currentStatus = yield* effect.Ref.get(statusRef);
|
|
207
229
|
if (currentStatus === "ready" || currentStatus === "error") return;
|
|
208
230
|
callbacks.onLog(name, `Process exited before ready (exit code: ${exitCode})`, true);
|
|
209
|
-
yield*
|
|
210
|
-
callbacks.onStatus(name, "error");
|
|
211
|
-
yield* effect.Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Process exited before ready: ${name}`)).pipe(effect.Effect.ignore);
|
|
231
|
+
yield* markError(`Process exited before ready: ${name}`);
|
|
212
232
|
}));
|
|
213
233
|
const handleLine = (line, isStderr) => effect.Effect.gen(function* () {
|
|
214
234
|
if (!line.trim()) return;
|
|
@@ -218,12 +238,8 @@ const spawnDevProcess = (descriptor, callbacks) => effect.Effect.gen(function* (
|
|
|
218
238
|
const currentStatus = yield* effect.Ref.get(statusRef);
|
|
219
239
|
if (currentStatus === "ready" || currentStatus === "error") return;
|
|
220
240
|
const detected = detectStatus(line, descriptor);
|
|
221
|
-
if (detected)
|
|
222
|
-
|
|
223
|
-
callbacks.onStatus(name, detected.status);
|
|
224
|
-
if (detected.status === "ready" || detected.status === "error") if (detected.status === "ready") yield* effect.Deferred.succeed(readyDeferred, void 0).pipe(effect.Effect.ignore);
|
|
225
|
-
else yield* effect.Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Process failed: ${name}`)).pipe(effect.Effect.ignore);
|
|
226
|
-
}
|
|
241
|
+
if (detected) if (detected.status === "ready") yield* markReady;
|
|
242
|
+
else yield* markError(`Process failed: ${name}`);
|
|
227
243
|
});
|
|
228
244
|
yield* effect.Effect.forkScoped(effect.Stream.runForEach((line) => handleLine(line, false))(effect.Stream.splitLines(effect.Stream.decodeText(proc.stdout, "utf-8"))));
|
|
229
245
|
yield* effect.Effect.forkScoped(effect.Stream.runForEach((line) => handleLine(line, true))(effect.Stream.splitLines(effect.Stream.decodeText(proc.stderr, "utf-8"))));
|
|
@@ -247,25 +263,25 @@ const spawnRemoteProbe = (pkg, descriptor, callbacks) => effect.Effect.gen(funct
|
|
|
247
263
|
const readyDeferred = yield* effect.Deferred.make();
|
|
248
264
|
const statusRef = yield* effect.Ref.make("starting");
|
|
249
265
|
const markReady = effect.Effect.gen(function* () {
|
|
266
|
+
const currentStatus = yield* effect.Ref.get(statusRef);
|
|
267
|
+
if (currentStatus === "ready" || currentStatus === "error") return;
|
|
250
268
|
yield* effect.Ref.set(statusRef, "ready");
|
|
251
|
-
yield* effect.Deferred.succeed(readyDeferred, void 0);
|
|
269
|
+
yield* effect.Deferred.succeed(readyDeferred, void 0).pipe(effect.Effect.ignore);
|
|
252
270
|
callbacks.onStatus(pkg, "ready", "loaded");
|
|
253
271
|
});
|
|
254
272
|
const markError = effect.Effect.gen(function* () {
|
|
273
|
+
const currentStatus = yield* effect.Ref.get(statusRef);
|
|
274
|
+
if (currentStatus === "ready" || currentStatus === "error") return;
|
|
255
275
|
yield* effect.Ref.set(statusRef, "error");
|
|
256
|
-
yield* effect.Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Remote ${pkg} unreachable`));
|
|
276
|
+
yield* effect.Deferred.fail(readyDeferred, /* @__PURE__ */ new Error(`Remote ${pkg} unreachable`)).pipe(effect.Effect.ignore);
|
|
257
277
|
callbacks.onStatus(pkg, "error", "unreachable");
|
|
258
278
|
});
|
|
259
279
|
const baseUrl = descriptor.url.replace(/\/$/, "");
|
|
260
280
|
const manifestUrl = `${baseUrl}/mf-manifest.json`;
|
|
261
281
|
const entryUrl = `${baseUrl}${descriptor.readinessPath}`;
|
|
262
282
|
const probeUrl = descriptor.readinessPath === "/health" ? `${baseUrl}/health` : manifestUrl;
|
|
263
|
-
const REMOTE_PROBE_TIMEOUT_MS = 5e3;
|
|
264
|
-
const REMOTE_PROBE_BACKOFF_INITIAL_MS = 1e3;
|
|
265
|
-
const REMOTE_PROBE_BACKOFF_MAX_MS = 15e3;
|
|
266
283
|
yield* effect.Effect.forkScoped(effect.Effect.gen(function* () {
|
|
267
|
-
|
|
268
|
-
const deadline = Date.now() + 6e4;
|
|
284
|
+
const deadline = Date.now() + REMOTE_PROBE_DEADLINE_MS;
|
|
269
285
|
let delay = REMOTE_PROBE_BACKOFF_INITIAL_MS;
|
|
270
286
|
while (Date.now() < deadline) {
|
|
271
287
|
const status = yield* effect.Ref.get(statusRef);
|