every-plugin 3.0.0-rc.3 → 3.0.0-rc.4
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/client.cjs +69 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +15 -0
- package/dist/client.d.cts.map +1 -0
- package/dist/client.d.mts +15 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +68 -0
- package/dist/client.mjs.map +1 -0
- package/dist/dev/host.cjs +149 -0
- package/dist/dev/host.cjs.map +1 -0
- package/dist/dev/host.d.cts +32 -0
- package/dist/dev/host.d.cts.map +1 -0
- package/dist/dev/host.d.mts +32 -0
- package/dist/dev/host.d.mts.map +1 -0
- package/dist/dev/host.mjs +148 -0
- package/dist/dev/host.mjs.map +1 -0
- package/dist/dev/serve.cjs +14 -109
- package/dist/dev/serve.cjs.map +1 -1
- package/dist/dev/serve.d.cts.map +1 -1
- package/dist/dev/serve.d.mts.map +1 -1
- package/dist/dev/serve.mjs +14 -109
- package/dist/dev/serve.mjs.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +3 -2
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +2 -1
- package/dist/plugin.d.cts +1 -1
- package/dist/runtime/mf-config.cjs +1 -1
- package/dist/runtime/mf-config.mjs +1 -1
- package/dist/runtime/services/plugin-loader.service.d.cts +2 -2
- package/dist/types.d.cts +23 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +22 -1
- package/dist/types.d.mts.map +1 -1
- package/package.json +19 -2
- package/src/client.ts +111 -0
- package/src/dev/host.ts +235 -0
- package/src/dev/serve.ts +14 -135
- package/src/index.ts +9 -0
- package/src/types.ts +36 -2
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let _orpc_client = require("@orpc/client");
|
|
3
|
+
let _orpc_client_fetch = require("@orpc/client/fetch");
|
|
4
|
+
|
|
5
|
+
//#region src/client.ts
|
|
6
|
+
const RATE_LIMIT_MAX_RETRIES = 3;
|
|
7
|
+
const RATE_LIMIT_RETRY_CAP_MS = 2e3;
|
|
8
|
+
const browserClients = /* @__PURE__ */ new Map();
|
|
9
|
+
async function fetchWithRateLimitRetry(fetchUrl, options) {
|
|
10
|
+
let response = await fetch(fetchUrl, options);
|
|
11
|
+
for (let attempt = 1; attempt <= RATE_LIMIT_MAX_RETRIES && response.status === 429; attempt++) {
|
|
12
|
+
const retryAfterSeconds = Number(response.headers.get("retry-after"));
|
|
13
|
+
const delay = Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0 ? Math.min(retryAfterSeconds * 1e3, RATE_LIMIT_RETRY_CAP_MS) : 100 * attempt;
|
|
14
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
15
|
+
response = await fetch(fetchUrl, options);
|
|
16
|
+
}
|
|
17
|
+
return response;
|
|
18
|
+
}
|
|
19
|
+
function createRpcLink(config, headers) {
|
|
20
|
+
return new _orpc_client_fetch.RPCLink({
|
|
21
|
+
origin: config.hostUrl,
|
|
22
|
+
url: config.path,
|
|
23
|
+
interceptors: [(0, _orpc_client.onError)((error) => {
|
|
24
|
+
if (typeof globalThis.window === "undefined") return;
|
|
25
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
26
|
+
const message = String(error.message).toLowerCase();
|
|
27
|
+
if (message.includes("fetch") || message.includes("network") || message.includes("failed to fetch")) import("sonner").then(({ toast }) => {
|
|
28
|
+
const message = config.connectionError?.() ?? {
|
|
29
|
+
title: "Unable to connect to API",
|
|
30
|
+
description: "The API is currently unavailable. Please try again later."
|
|
31
|
+
};
|
|
32
|
+
toast.error(message.title, {
|
|
33
|
+
id: "api-connection-error",
|
|
34
|
+
description: message.description
|
|
35
|
+
});
|
|
36
|
+
}).catch(() => {});
|
|
37
|
+
}
|
|
38
|
+
})],
|
|
39
|
+
fetch(fetchUrl, options) {
|
|
40
|
+
return fetchWithRateLimitRetry(fetchUrl, {
|
|
41
|
+
...options,
|
|
42
|
+
credentials: "include",
|
|
43
|
+
headers: headers ? {
|
|
44
|
+
...Object.fromEntries(headers),
|
|
45
|
+
...options?.headers
|
|
46
|
+
} : options?.headers
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function buildRpcClient(config, headers) {
|
|
52
|
+
if (!config.hostUrl) throw new Error("Missing runtime host URL");
|
|
53
|
+
return (0, _orpc_client.createORPCClient)(createRpcLink(config, headers));
|
|
54
|
+
}
|
|
55
|
+
function createRpcClient(config, headers) {
|
|
56
|
+
if (typeof globalThis.window !== "undefined" && !headers) {
|
|
57
|
+
const key = `${config.hostUrl}${config.path}`;
|
|
58
|
+
const memoized = browserClients.get(key);
|
|
59
|
+
if (memoized) return memoized;
|
|
60
|
+
const client = buildRpcClient(config, headers);
|
|
61
|
+
browserClients.set(key, client);
|
|
62
|
+
return client;
|
|
63
|
+
}
|
|
64
|
+
return buildRpcClient(config, headers);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//#endregion
|
|
68
|
+
exports.createRpcClient = createRpcClient;
|
|
69
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.cjs","names":["RPCLink","onError","createORPCClient"],"sources":["../src/client.ts"],"sourcesContent":["import { createORPCClient, onError } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nimport type { ContractRouterClient, RouterContract } from \"@orpc/contract\";\n\nexport type RpcConnectionErrorCopy = () => { title: string; description: string };\n\nexport interface RpcClientConfig {\n hostUrl: string;\n path: `/${string}`;\n connectionError?: RpcConnectionErrorCopy;\n}\n\nconst RATE_LIMIT_MAX_RETRIES = 3;\nconst RATE_LIMIT_RETRY_CAP_MS = 2_000;\nconst browserClients = new Map<string, unknown>();\ntype FetchInput = Parameters<typeof fetch>[0];\n\nasync function fetchWithRateLimitRetry(\n fetchUrl: FetchInput,\n options?: RequestInit,\n): Promise<Response> {\n let response = await fetch(fetchUrl, options);\n for (let attempt = 1; attempt <= RATE_LIMIT_MAX_RETRIES && response.status === 429; attempt++) {\n const retryAfterSeconds = Number(response.headers.get(\"retry-after\"));\n const delay =\n Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0\n ? Math.min(retryAfterSeconds * 1000, RATE_LIMIT_RETRY_CAP_MS)\n : 100 * attempt;\n await new Promise((resolve) => setTimeout(resolve, delay));\n response = await fetch(fetchUrl, options);\n }\n\n return response;\n}\n\nfunction createRpcLink(config: RpcClientConfig, headers?: Headers) {\n return new RPCLink({\n origin: config.hostUrl,\n url: config.path,\n interceptors: [\n onError((error: unknown) => {\n const browserWindow = (globalThis as { window?: unknown }).window;\n if (typeof browserWindow === \"undefined\") {\n return;\n }\n\n if (error && typeof error === \"object\" && \"message\" in error) {\n const message = String(error.message).toLowerCase();\n if (\n message.includes(\"fetch\") ||\n message.includes(\"network\") ||\n message.includes(\"failed to fetch\")\n ) {\n void import(\"sonner\")\n .then(({ toast }) => {\n const message = config.connectionError?.() ?? {\n title: \"Unable to connect to API\",\n description: \"The API is currently unavailable. Please try again later.\",\n };\n toast.error(message.title, {\n id: \"api-connection-error\",\n description: message.description,\n });\n })\n .catch(() => {});\n }\n }\n }),\n ],\n fetch(fetchUrl: FetchInput, options?: RequestInit) {\n return fetchWithRateLimitRetry(fetchUrl, {\n ...options,\n credentials: \"include\",\n headers: headers\n ? { ...Object.fromEntries(headers), ...(options?.headers as Record<string, string>) }\n : options?.headers,\n });\n },\n });\n}\n\nfunction buildRpcClient<T extends RouterContract>(\n config: RpcClientConfig,\n headers?: Headers,\n): ContractRouterClient<T> {\n if (!config.hostUrl) {\n throw new Error(\"Missing runtime host URL\");\n }\n\n return createORPCClient(createRpcLink(config, headers)) as ContractRouterClient<T>;\n}\n\nexport function createRpcClient<T extends RouterContract = RouterContract>(\n config: RpcClientConfig,\n headers?: Headers,\n): ContractRouterClient<T> {\n const browserWindow = (globalThis as { window?: unknown }).window;\n if (typeof browserWindow !== \"undefined\" && !headers) {\n const key = `${config.hostUrl}${config.path}`;\n const memoized = browserClients.get(key);\n if (memoized) {\n return memoized as ContractRouterClient<T>;\n }\n\n const client = buildRpcClient<T>(config, headers);\n browserClients.set(key, client);\n return client;\n }\n\n return buildRpcClient<T>(config, headers);\n}\n"],"mappings":";;;;;AAYA,MAAM,yBAAyB;AAC/B,MAAM,0BAA0B;AAChC,MAAM,iCAAiB,IAAI,IAAqB;AAGhD,eAAe,wBACb,UACA,SACmB;CACnB,IAAI,WAAW,MAAM,MAAM,UAAU,OAAO;CAC5C,KAAK,IAAI,UAAU,GAAG,WAAW,0BAA0B,SAAS,WAAW,KAAK,WAAW;EAC7F,MAAM,oBAAoB,OAAO,SAAS,QAAQ,IAAI,aAAa,CAAC;EACpE,MAAM,QACJ,OAAO,SAAS,iBAAiB,KAAK,oBAAoB,IACtD,KAAK,IAAI,oBAAoB,KAAM,uBAAuB,IAC1D,MAAM;EACZ,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,KAAK,CAAC;EACzD,WAAW,MAAM,MAAM,UAAU,OAAO;CAC1C;CAEA,OAAO;AACT;AAEA,SAAS,cAAc,QAAyB,SAAmB;CACjE,OAAO,IAAIA,2BAAQ;EACjB,QAAQ,OAAO;EACf,KAAK,OAAO;EACZ,cAAc,KACZC,uBAAS,UAAmB;GAE1B,IAAI,OADmB,WAAoC,WAC9B,aAC3B;GAGF,IAAI,SAAS,OAAO,UAAU,YAAY,aAAa,OAAO;IAC5D,MAAM,UAAU,OAAO,MAAM,OAAO,CAAC,CAAC,YAAY;IAClD,IACE,QAAQ,SAAS,OAAO,KACxB,QAAQ,SAAS,SAAS,KAC1B,QAAQ,SAAS,iBAAiB,GAElC,AAAK,OAAO,SAAS,CAClB,MAAM,EAAE,YAAY;KACnB,MAAM,UAAU,OAAO,kBAAkB,KAAK;MAC5C,OAAO;MACP,aAAa;KACf;KACA,MAAM,MAAM,QAAQ,OAAO;MACzB,IAAI;MACJ,aAAa,QAAQ;KACvB,CAAC;IACH,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;GAErB;EACF,CAAC,CACH;EACA,MAAM,UAAsB,SAAuB;GACjD,OAAO,wBAAwB,UAAU;IACvC,GAAG;IACH,aAAa;IACb,SAAS,UACL;KAAE,GAAG,OAAO,YAAY,OAAO;KAAG,GAAI,SAAS;IAAmC,IAClF,SAAS;GACf,CAAC;EACH;CACF,CAAC;AACH;AAEA,SAAS,eACP,QACA,SACyB;CACzB,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MAAM,0BAA0B;CAG5C,WAAOC,+BAAiB,cAAc,QAAQ,OAAO,CAAC;AACxD;AAEA,SAAgB,gBACd,QACA,SACyB;CAEzB,IAAI,OADmB,WAAoC,WAC9B,eAAe,CAAC,SAAS;EACpD,MAAM,MAAM,GAAG,OAAO,UAAU,OAAO;EACvC,MAAM,WAAW,eAAe,IAAI,GAAG;EACvC,IAAI,UACF,OAAO;EAGT,MAAM,SAAS,eAAkB,QAAQ,OAAO;EAChD,eAAe,IAAI,KAAK,MAAM;EAC9B,OAAO;CACT;CAEA,OAAO,eAAkB,QAAQ,OAAO;AAC1C"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ContractRouterClient, RouterContract } from "@orpc/contract";
|
|
2
|
+
//#region src/client.d.ts
|
|
3
|
+
type RpcConnectionErrorCopy = () => {
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
};
|
|
7
|
+
interface RpcClientConfig {
|
|
8
|
+
hostUrl: string;
|
|
9
|
+
path: `/${string}`;
|
|
10
|
+
connectionError?: RpcConnectionErrorCopy;
|
|
11
|
+
}
|
|
12
|
+
declare function createRpcClient<T extends RouterContract = RouterContract>(config: RpcClientConfig, headers?: Headers): ContractRouterClient<T>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { RpcClientConfig, RpcConnectionErrorCopy, createRpcClient };
|
|
15
|
+
//# sourceMappingURL=client.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.cts","names":[],"sources":["../src/client.ts"],"mappings":";;KAIY;EAAiC;EAAe;;UAE3C;EACf;EACA;EACA,kBAAkB;;iBAmFJ,gBAAgB,UAAU,iBAAiB,gBACzD,QAAQ,iBACR,UAAU,UACT,qBAAqB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ContractRouterClient, RouterContract } from "@orpc/contract";
|
|
2
|
+
//#region src/client.d.ts
|
|
3
|
+
type RpcConnectionErrorCopy = () => {
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
};
|
|
7
|
+
interface RpcClientConfig {
|
|
8
|
+
hostUrl: string;
|
|
9
|
+
path: `/${string}`;
|
|
10
|
+
connectionError?: RpcConnectionErrorCopy;
|
|
11
|
+
}
|
|
12
|
+
declare function createRpcClient<T extends RouterContract = RouterContract>(config: RpcClientConfig, headers?: Headers): ContractRouterClient<T>;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { RpcClientConfig, RpcConnectionErrorCopy, createRpcClient };
|
|
15
|
+
//# sourceMappingURL=client.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;KAIY;EAAiC;EAAe;;UAE3C;EACf;EACA;EACA,kBAAkB;;iBAmFJ,gBAAgB,UAAU,iBAAiB,gBACzD,QAAQ,iBACR,UAAU,UACT,qBAAqB"}
|
package/dist/client.mjs
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createORPCClient, onError } from "@orpc/client";
|
|
2
|
+
import { RPCLink } from "@orpc/client/fetch";
|
|
3
|
+
|
|
4
|
+
//#region src/client.ts
|
|
5
|
+
const RATE_LIMIT_MAX_RETRIES = 3;
|
|
6
|
+
const RATE_LIMIT_RETRY_CAP_MS = 2e3;
|
|
7
|
+
const browserClients = /* @__PURE__ */ new Map();
|
|
8
|
+
async function fetchWithRateLimitRetry(fetchUrl, options) {
|
|
9
|
+
let response = await fetch(fetchUrl, options);
|
|
10
|
+
for (let attempt = 1; attempt <= RATE_LIMIT_MAX_RETRIES && response.status === 429; attempt++) {
|
|
11
|
+
const retryAfterSeconds = Number(response.headers.get("retry-after"));
|
|
12
|
+
const delay = Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0 ? Math.min(retryAfterSeconds * 1e3, RATE_LIMIT_RETRY_CAP_MS) : 100 * attempt;
|
|
13
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
14
|
+
response = await fetch(fetchUrl, options);
|
|
15
|
+
}
|
|
16
|
+
return response;
|
|
17
|
+
}
|
|
18
|
+
function createRpcLink(config, headers) {
|
|
19
|
+
return new RPCLink({
|
|
20
|
+
origin: config.hostUrl,
|
|
21
|
+
url: config.path,
|
|
22
|
+
interceptors: [onError((error) => {
|
|
23
|
+
if (typeof globalThis.window === "undefined") return;
|
|
24
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
25
|
+
const message = String(error.message).toLowerCase();
|
|
26
|
+
if (message.includes("fetch") || message.includes("network") || message.includes("failed to fetch")) import("sonner").then(({ toast }) => {
|
|
27
|
+
const message = config.connectionError?.() ?? {
|
|
28
|
+
title: "Unable to connect to API",
|
|
29
|
+
description: "The API is currently unavailable. Please try again later."
|
|
30
|
+
};
|
|
31
|
+
toast.error(message.title, {
|
|
32
|
+
id: "api-connection-error",
|
|
33
|
+
description: message.description
|
|
34
|
+
});
|
|
35
|
+
}).catch(() => {});
|
|
36
|
+
}
|
|
37
|
+
})],
|
|
38
|
+
fetch(fetchUrl, options) {
|
|
39
|
+
return fetchWithRateLimitRetry(fetchUrl, {
|
|
40
|
+
...options,
|
|
41
|
+
credentials: "include",
|
|
42
|
+
headers: headers ? {
|
|
43
|
+
...Object.fromEntries(headers),
|
|
44
|
+
...options?.headers
|
|
45
|
+
} : options?.headers
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function buildRpcClient(config, headers) {
|
|
51
|
+
if (!config.hostUrl) throw new Error("Missing runtime host URL");
|
|
52
|
+
return createORPCClient(createRpcLink(config, headers));
|
|
53
|
+
}
|
|
54
|
+
function createRpcClient(config, headers) {
|
|
55
|
+
if (typeof globalThis.window !== "undefined" && !headers) {
|
|
56
|
+
const key = `${config.hostUrl}${config.path}`;
|
|
57
|
+
const memoized = browserClients.get(key);
|
|
58
|
+
if (memoized) return memoized;
|
|
59
|
+
const client = buildRpcClient(config, headers);
|
|
60
|
+
browserClients.set(key, client);
|
|
61
|
+
return client;
|
|
62
|
+
}
|
|
63
|
+
return buildRpcClient(config, headers);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { createRpcClient };
|
|
68
|
+
//# sourceMappingURL=client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import { createORPCClient, onError } from \"@orpc/client\";\nimport { RPCLink } from \"@orpc/client/fetch\";\nimport type { ContractRouterClient, RouterContract } from \"@orpc/contract\";\n\nexport type RpcConnectionErrorCopy = () => { title: string; description: string };\n\nexport interface RpcClientConfig {\n hostUrl: string;\n path: `/${string}`;\n connectionError?: RpcConnectionErrorCopy;\n}\n\nconst RATE_LIMIT_MAX_RETRIES = 3;\nconst RATE_LIMIT_RETRY_CAP_MS = 2_000;\nconst browserClients = new Map<string, unknown>();\ntype FetchInput = Parameters<typeof fetch>[0];\n\nasync function fetchWithRateLimitRetry(\n fetchUrl: FetchInput,\n options?: RequestInit,\n): Promise<Response> {\n let response = await fetch(fetchUrl, options);\n for (let attempt = 1; attempt <= RATE_LIMIT_MAX_RETRIES && response.status === 429; attempt++) {\n const retryAfterSeconds = Number(response.headers.get(\"retry-after\"));\n const delay =\n Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0\n ? Math.min(retryAfterSeconds * 1000, RATE_LIMIT_RETRY_CAP_MS)\n : 100 * attempt;\n await new Promise((resolve) => setTimeout(resolve, delay));\n response = await fetch(fetchUrl, options);\n }\n\n return response;\n}\n\nfunction createRpcLink(config: RpcClientConfig, headers?: Headers) {\n return new RPCLink({\n origin: config.hostUrl,\n url: config.path,\n interceptors: [\n onError((error: unknown) => {\n const browserWindow = (globalThis as { window?: unknown }).window;\n if (typeof browserWindow === \"undefined\") {\n return;\n }\n\n if (error && typeof error === \"object\" && \"message\" in error) {\n const message = String(error.message).toLowerCase();\n if (\n message.includes(\"fetch\") ||\n message.includes(\"network\") ||\n message.includes(\"failed to fetch\")\n ) {\n void import(\"sonner\")\n .then(({ toast }) => {\n const message = config.connectionError?.() ?? {\n title: \"Unable to connect to API\",\n description: \"The API is currently unavailable. Please try again later.\",\n };\n toast.error(message.title, {\n id: \"api-connection-error\",\n description: message.description,\n });\n })\n .catch(() => {});\n }\n }\n }),\n ],\n fetch(fetchUrl: FetchInput, options?: RequestInit) {\n return fetchWithRateLimitRetry(fetchUrl, {\n ...options,\n credentials: \"include\",\n headers: headers\n ? { ...Object.fromEntries(headers), ...(options?.headers as Record<string, string>) }\n : options?.headers,\n });\n },\n });\n}\n\nfunction buildRpcClient<T extends RouterContract>(\n config: RpcClientConfig,\n headers?: Headers,\n): ContractRouterClient<T> {\n if (!config.hostUrl) {\n throw new Error(\"Missing runtime host URL\");\n }\n\n return createORPCClient(createRpcLink(config, headers)) as ContractRouterClient<T>;\n}\n\nexport function createRpcClient<T extends RouterContract = RouterContract>(\n config: RpcClientConfig,\n headers?: Headers,\n): ContractRouterClient<T> {\n const browserWindow = (globalThis as { window?: unknown }).window;\n if (typeof browserWindow !== \"undefined\" && !headers) {\n const key = `${config.hostUrl}${config.path}`;\n const memoized = browserClients.get(key);\n if (memoized) {\n return memoized as ContractRouterClient<T>;\n }\n\n const client = buildRpcClient<T>(config, headers);\n browserClients.set(key, client);\n return client;\n }\n\n return buildRpcClient<T>(config, headers);\n}\n"],"mappings":";;;;AAYA,MAAM,yBAAyB;AAC/B,MAAM,0BAA0B;AAChC,MAAM,iCAAiB,IAAI,IAAqB;AAGhD,eAAe,wBACb,UACA,SACmB;CACnB,IAAI,WAAW,MAAM,MAAM,UAAU,OAAO;CAC5C,KAAK,IAAI,UAAU,GAAG,WAAW,0BAA0B,SAAS,WAAW,KAAK,WAAW;EAC7F,MAAM,oBAAoB,OAAO,SAAS,QAAQ,IAAI,aAAa,CAAC;EACpE,MAAM,QACJ,OAAO,SAAS,iBAAiB,KAAK,oBAAoB,IACtD,KAAK,IAAI,oBAAoB,KAAM,uBAAuB,IAC1D,MAAM;EACZ,MAAM,IAAI,SAAS,YAAY,WAAW,SAAS,KAAK,CAAC;EACzD,WAAW,MAAM,MAAM,UAAU,OAAO;CAC1C;CAEA,OAAO;AACT;AAEA,SAAS,cAAc,QAAyB,SAAmB;CACjE,OAAO,IAAI,QAAQ;EACjB,QAAQ,OAAO;EACf,KAAK,OAAO;EACZ,cAAc,CACZ,SAAS,UAAmB;GAE1B,IAAI,OADmB,WAAoC,WAC9B,aAC3B;GAGF,IAAI,SAAS,OAAO,UAAU,YAAY,aAAa,OAAO;IAC5D,MAAM,UAAU,OAAO,MAAM,OAAO,CAAC,CAAC,YAAY;IAClD,IACE,QAAQ,SAAS,OAAO,KACxB,QAAQ,SAAS,SAAS,KAC1B,QAAQ,SAAS,iBAAiB,GAElC,AAAK,OAAO,SAAS,CAClB,MAAM,EAAE,YAAY;KACnB,MAAM,UAAU,OAAO,kBAAkB,KAAK;MAC5C,OAAO;MACP,aAAa;KACf;KACA,MAAM,MAAM,QAAQ,OAAO;MACzB,IAAI;MACJ,aAAa,QAAQ;KACvB,CAAC;IACH,CAAC,CAAC,CACD,YAAY,CAAC,CAAC;GAErB;EACF,CAAC,CACH;EACA,MAAM,UAAsB,SAAuB;GACjD,OAAO,wBAAwB,UAAU;IACvC,GAAG;IACH,aAAa;IACb,SAAS,UACL;KAAE,GAAG,OAAO,YAAY,OAAO;KAAG,GAAI,SAAS;IAAmC,IAClF,SAAS;GACf,CAAC;EACH;CACF,CAAC;AACH;AAEA,SAAS,eACP,QACA,SACyB;CACzB,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MAAM,0BAA0B;CAG5C,OAAO,iBAAiB,cAAc,QAAQ,OAAO,CAAC;AACxD;AAEA,SAAgB,gBACd,QACA,SACyB;CAEzB,IAAI,OADmB,WAAoC,WAC9B,eAAe,CAAC,SAAS;EACpD,MAAM,MAAM,GAAG,OAAO,UAAU,OAAO;EACvC,MAAM,WAAW,eAAe,IAAI,GAAG;EACvC,IAAI,UACF,OAAO;EAGT,MAAM,SAAS,eAAkB,QAAQ,OAAO;EAChD,eAAe,IAAI,KAAK,MAAM;EAC9B,OAAO;CACT;CAEA,OAAO,eAAkB,QAAQ,OAAO;AAC1C"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let node_http = require("node:http");
|
|
3
|
+
|
|
4
|
+
//#region src/dev/host.ts
|
|
5
|
+
const corsHeaders = {
|
|
6
|
+
"Access-Control-Allow-Origin": "*",
|
|
7
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
8
|
+
"Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
|
|
9
|
+
};
|
|
10
|
+
const applyCorsHeaders = (res) => {
|
|
11
|
+
Object.entries(corsHeaders).forEach(([key, value]) => {
|
|
12
|
+
res.setHeader(key, value);
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const sendJson = (res, status, body) => {
|
|
16
|
+
res.statusCode = status;
|
|
17
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
18
|
+
res.end(JSON.stringify(body));
|
|
19
|
+
};
|
|
20
|
+
const sendText = (res, status, text) => {
|
|
21
|
+
res.statusCode = status;
|
|
22
|
+
res.setHeader("content-type", "text/plain; charset=utf-8");
|
|
23
|
+
res.end(text);
|
|
24
|
+
};
|
|
25
|
+
const pathnameOf = (req) => ((req.url ?? "/").split("?")[0] ?? "/").replace(/\/+$/, "") || "/";
|
|
26
|
+
const toWebRequest = (req, port) => {
|
|
27
|
+
const url = `http://localhost:${port}${req.url}`;
|
|
28
|
+
return new Request(url, {
|
|
29
|
+
method: req.method,
|
|
30
|
+
headers: req.headers,
|
|
31
|
+
body: req.method !== "GET" && req.method !== "HEAD" ? req : void 0,
|
|
32
|
+
duplex: req.method !== "GET" && req.method !== "HEAD" ? "half" : void 0
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
const buildHostContext = (effectContext, webRequest, cachedRawBodyRef) => {
|
|
36
|
+
const rawClone = webRequest.method === "GET" || webRequest.method === "HEAD" ? null : webRequest.clone();
|
|
37
|
+
return {
|
|
38
|
+
reqHeaders: webRequest.headers,
|
|
39
|
+
"effect/context": effectContext,
|
|
40
|
+
getRawBody: async () => {
|
|
41
|
+
if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;
|
|
42
|
+
if (!rawClone) {
|
|
43
|
+
cachedRawBodyRef.value = "";
|
|
44
|
+
return cachedRawBodyRef.value;
|
|
45
|
+
}
|
|
46
|
+
cachedRawBodyRef.value = await rawClone.text();
|
|
47
|
+
return cachedRawBodyRef.value;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const handleOrpc = async (req, res, handler, prefix, errorLabel, effectContext, port) => {
|
|
52
|
+
try {
|
|
53
|
+
const webRequest = toWebRequest(req, port);
|
|
54
|
+
const result = await handler.handle(webRequest, {
|
|
55
|
+
prefix,
|
|
56
|
+
context: buildHostContext(effectContext, webRequest, { value: null })
|
|
57
|
+
});
|
|
58
|
+
if (result.response) {
|
|
59
|
+
res.statusCode = result.response.status;
|
|
60
|
+
result.response.headers.forEach((value, key) => {
|
|
61
|
+
res.setHeader(key, value);
|
|
62
|
+
});
|
|
63
|
+
const text = await result.response.text();
|
|
64
|
+
if (!res.getHeader("content-length")) res.setHeader("content-length", Buffer.byteLength(text).toString());
|
|
65
|
+
res.end(text);
|
|
66
|
+
} else sendText(res, 404, "Not Found");
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error(`${errorLabel} error:`, error);
|
|
69
|
+
sendJson(res, 500, { error: error.message });
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
async function startDirectModuleHost(options) {
|
|
73
|
+
const port = options.port ?? 0;
|
|
74
|
+
let resolvedPort = port;
|
|
75
|
+
const rpcPrefix = options.rpcPrefix ?? "/api/rpc";
|
|
76
|
+
const apiPrefix = options.apiPrefix ?? "/api";
|
|
77
|
+
const staticHandler = options.staticHandler ?? null;
|
|
78
|
+
const resolveEffectContext = typeof options.effectContext === "function" ? options.effectContext : () => options.effectContext;
|
|
79
|
+
const server = (0, node_http.createServer)((req, res) => {
|
|
80
|
+
applyCorsHeaders(res);
|
|
81
|
+
const pathname = pathnameOf(req);
|
|
82
|
+
if (req.method === "OPTIONS") {
|
|
83
|
+
res.statusCode = 200;
|
|
84
|
+
res.end();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (pathname === "/" && (req.method === "GET" || req.method === "HEAD")) {
|
|
88
|
+
sendJson(res, 200, {
|
|
89
|
+
ok: true,
|
|
90
|
+
module: options.name,
|
|
91
|
+
version: options.version,
|
|
92
|
+
status: options.handlers.rpc ? "ready" : "loading",
|
|
93
|
+
endpoints: {
|
|
94
|
+
health: "/health",
|
|
95
|
+
docs: apiPrefix,
|
|
96
|
+
rpc: rpcPrefix
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (pathname === "/health" && (req.method === "GET" || req.method === "HEAD")) {
|
|
102
|
+
sendText(res, 200, "OK");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (pathname.startsWith(`${rpcPrefix}/`)) {
|
|
106
|
+
if (!options.handlers.rpc) {
|
|
107
|
+
sendJson(res, 503, { error: "Plugin still loading..." });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
handleOrpc(req, res, options.handlers.rpc, rpcPrefix, "RPC", resolveEffectContext(), resolvedPort).catch(() => {});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {
|
|
114
|
+
if (!options.handlers.api) {
|
|
115
|
+
sendJson(res, 503, { error: "Plugin still loading..." });
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
handleOrpc(req, res, options.handlers.api, apiPrefix, "OpenAPI", resolveEffectContext(), resolvedPort).catch(() => {});
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (staticHandler) {
|
|
122
|
+
staticHandler(req, res, () => {
|
|
123
|
+
sendText(res, 404, "Not Found");
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
sendText(res, 404, "Not Found");
|
|
128
|
+
});
|
|
129
|
+
await new Promise((resolve, reject) => {
|
|
130
|
+
server.once("error", reject);
|
|
131
|
+
server.listen(port, () => resolve());
|
|
132
|
+
});
|
|
133
|
+
const address = server.address();
|
|
134
|
+
if (!address || typeof address === "string") {
|
|
135
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
136
|
+
throw new Error("Failed to resolve host port");
|
|
137
|
+
}
|
|
138
|
+
resolvedPort = address.port;
|
|
139
|
+
return {
|
|
140
|
+
port: resolvedPort,
|
|
141
|
+
close: async () => {
|
|
142
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
exports.startDirectModuleHost = startDirectModuleHost;
|
|
149
|
+
//# sourceMappingURL=host.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.cjs","names":["createServer"],"sources":["../../src/dev/host.ts"],"sourcesContent":["import type http from \"node:http\";\nimport { createServer } from \"node:http\";\n\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, PATCH, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"X-Requested-With, content-type, Authorization\",\n};\n\nconst applyCorsHeaders = (res: http.ServerResponse) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst sendJson = (res: http.ServerResponse, status: number, body: unknown) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify(body));\n};\n\nconst sendText = (res: http.ServerResponse, status: number, text: string) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(text);\n};\n\nconst pathnameOf = (req: http.IncomingMessage): string =>\n ((req.url ?? \"/\").split(\"?\")[0] ?? \"/\").replace(/\\/+$/, \"\") || \"/\";\n\nconst toWebRequest = (req: http.IncomingMessage, port: number): Request => {\n const url = `http://localhost:${port}${req.url}`;\n return new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n};\n\nconst buildHostContext = (\n effectContext: unknown,\n webRequest: Request,\n cachedRawBodyRef: { value: string | null },\n) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n\n return {\n reqHeaders: webRequest.headers,\n \"effect/context\": effectContext,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;\n if (!rawClone) {\n cachedRawBodyRef.value = \"\";\n return cachedRawBodyRef.value;\n }\n cachedRawBodyRef.value = await rawClone.text();\n return cachedRawBodyRef.value;\n },\n };\n};\n\nexport interface DirectModuleHostHandler {\n handle(\n request: Request,\n options: { prefix: string; context: unknown },\n ): Promise<{ response?: Response }>;\n}\n\nconst handleOrpc = async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n handler: DirectModuleHostHandler,\n prefix: string,\n errorLabel: string,\n effectContext: unknown,\n port: number,\n) => {\n try {\n const webRequest = toWebRequest(req, port);\n const cachedRawBodyRef = { value: null as string | null };\n const result = await handler.handle(webRequest, {\n prefix,\n context: buildHostContext(effectContext, webRequest, cachedRawBodyRef),\n });\n\n if (result.response) {\n res.statusCode = result.response.status;\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n if (!res.getHeader(\"content-length\")) {\n res.setHeader(\"content-length\", Buffer.byteLength(text).toString());\n }\n res.end(text);\n } else {\n sendText(res, 404, \"Not Found\");\n }\n } catch (error) {\n console.error(`${errorLabel} error:`, error);\n sendJson(res, 500, { error: (error as Error).message });\n }\n};\n\nexport interface DirectModuleHostHandlers {\n rpc: DirectModuleHostHandler | null;\n api: DirectModuleHostHandler | null;\n}\n\nexport interface DirectModuleHostOptions {\n name: string;\n handlers: DirectModuleHostHandlers;\n effectContext: unknown;\n port?: number;\n version?: string;\n rpcPrefix?: `/${string}`;\n apiPrefix?: `/${string}`;\n staticHandler?:\n | ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void)\n | null;\n}\n\nexport interface DirectModuleHostHandle {\n port: number;\n close: () => Promise<void>;\n}\n\nexport async function startDirectModuleHost(\n options: DirectModuleHostOptions,\n): Promise<DirectModuleHostHandle> {\n const port = options.port ?? 0;\n let resolvedPort = port;\n const rpcPrefix = options.rpcPrefix ?? \"/api/rpc\";\n const apiPrefix = options.apiPrefix ?? \"/api\";\n const staticHandler = options.staticHandler ?? null;\n const resolveEffectContext: () => unknown =\n typeof options.effectContext === \"function\"\n ? (options.effectContext as () => unknown)\n : () => options.effectContext;\n\n const server = createServer((req, res) => {\n applyCorsHeaders(res);\n const pathname = pathnameOf(req);\n\n if (req.method === \"OPTIONS\") {\n res.statusCode = 200;\n res.end();\n return;\n }\n\n if (pathname === \"/\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendJson(res, 200, {\n ok: true,\n module: options.name,\n version: options.version,\n status: options.handlers.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/health\",\n docs: apiPrefix,\n rpc: rpcPrefix,\n },\n });\n return;\n }\n\n if (pathname === \"/health\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendText(res, 200, \"OK\");\n return;\n }\n\n if (pathname.startsWith(`${rpcPrefix}/`)) {\n if (!options.handlers.rpc) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.rpc,\n rpcPrefix,\n \"RPC\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {\n if (!options.handlers.api) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.api,\n apiPrefix,\n \"OpenAPI\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (staticHandler) {\n staticHandler(req, res, () => {\n sendText(res, 404, \"Not Found\");\n });\n return;\n }\n\n sendText(res, 404, \"Not Found\");\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, () => resolve());\n });\n\n const address = server.address();\n if (!address || typeof address === \"string\") {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n throw new Error(\"Failed to resolve host port\");\n }\n resolvedPort = address.port;\n\n return {\n port: resolvedPort,\n close: async () => {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n },\n };\n}\n"],"mappings":";;;;AAGA,MAAM,cAAc;CAClB,+BAA+B;CAC/B,gCAAgC;CAChC,gCAAgC;AAClC;AAEA,MAAM,oBAAoB,QAA6B;CACrD,OAAO,QAAQ,WAAW,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EACpD,IAAI,UAAU,KAAK,KAAK;CAC1B,CAAC;AACH;AAEA,MAAM,YAAY,KAA0B,QAAgB,SAAkB;CAC5E,IAAI,aAAa;CACjB,IAAI,UAAU,gBAAgB,iCAAiC;CAC/D,IAAI,IAAI,KAAK,UAAU,IAAI,CAAC;AAC9B;AAEA,MAAM,YAAY,KAA0B,QAAgB,SAAiB;CAC3E,IAAI,aAAa;CACjB,IAAI,UAAU,gBAAgB,2BAA2B;CACzD,IAAI,IAAI,IAAI;AACd;AAEA,MAAM,cAAc,UAChB,IAAI,OAAO,IAAG,CAAE,MAAM,GAAG,CAAC,CAAC,MAAM,IAAG,CAAE,QAAQ,QAAQ,EAAE,KAAK;AAEjE,MAAM,gBAAgB,KAA2B,SAA0B;CACzE,MAAM,MAAM,oBAAoB,OAAO,IAAI;CAC3C,OAAO,IAAI,QAAQ,KAAK;EACtB,QAAQ,IAAI;EACZ,SAAS,IAAI;EACb,MAAM,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,MAAM;EAC5D,QAAQ,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS,SAAS;CACnE,CAAgB;AAClB;AAEA,MAAM,oBACJ,eACA,YACA,qBACG;CACH,MAAM,WACJ,WAAW,WAAW,SAAS,WAAW,WAAW,SAAS,OAAO,WAAW,MAAM;CAExF,OAAO;EACL,YAAY,WAAW;EACvB,kBAAkB;EAClB,YAAY,YAA6B;GACvC,IAAI,iBAAiB,UAAU,MAAM,OAAO,iBAAiB;GAC7D,IAAI,CAAC,UAAU;IACb,iBAAiB,QAAQ;IACzB,OAAO,iBAAiB;GAC1B;GACA,iBAAiB,QAAQ,MAAM,SAAS,KAAK;GAC7C,OAAO,iBAAiB;EAC1B;CACF;AACF;AASA,MAAM,aAAa,OACjB,KACA,KACA,SACA,QACA,YACA,eACA,SACG;CACH,IAAI;EACF,MAAM,aAAa,aAAa,KAAK,IAAI;EAEzC,MAAM,SAAS,MAAM,QAAQ,OAAO,YAAY;GAC9C;GACA,SAAS,iBAAiB,eAAe,YAAY,EAH5B,OAAO,KAGoC,CAAC;EACvE,CAAC;EAED,IAAI,OAAO,UAAU;GACnB,IAAI,aAAa,OAAO,SAAS;GACjC,OAAO,SAAS,QAAQ,SAAS,OAAe,QAAgB;IAC9D,IAAI,UAAU,KAAK,KAAK;GAC1B,CAAC;GACD,MAAM,OAAO,MAAM,OAAO,SAAS,KAAK;GACxC,IAAI,CAAC,IAAI,UAAU,gBAAgB,GACjC,IAAI,UAAU,kBAAkB,OAAO,WAAW,IAAI,CAAC,CAAC,SAAS,CAAC;GAEpE,IAAI,IAAI,IAAI;EACd,OACE,SAAS,KAAK,KAAK,WAAW;CAElC,SAAS,OAAO;EACd,QAAQ,MAAM,GAAG,WAAW,UAAU,KAAK;EAC3C,SAAS,KAAK,KAAK,EAAE,OAAQ,MAAgB,QAAQ,CAAC;CACxD;AACF;AAyBA,eAAsB,sBACpB,SACiC;CACjC,MAAM,OAAO,QAAQ,QAAQ;CAC7B,IAAI,eAAe;CACnB,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,uBACJ,OAAO,QAAQ,kBAAkB,aAC5B,QAAQ,sBACH,QAAQ;CAEpB,MAAM,aAASA,yBAAc,KAAK,QAAQ;EACxC,iBAAiB,GAAG;EACpB,MAAM,WAAW,WAAW,GAAG;EAE/B,IAAI,IAAI,WAAW,WAAW;GAC5B,IAAI,aAAa;GACjB,IAAI,IAAI;GACR;EACF;EAEA,IAAI,aAAa,QAAQ,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS;GACvE,SAAS,KAAK,KAAK;IACjB,IAAI;IACJ,QAAQ,QAAQ;IAChB,SAAS,QAAQ;IACjB,QAAQ,QAAQ,SAAS,MAAM,UAAU;IACzC,WAAW;KACT,QAAQ;KACR,MAAM;KACN,KAAK;IACP;GACF,CAAC;GACD;EACF;EAEA,IAAI,aAAa,cAAc,IAAI,WAAW,SAAS,IAAI,WAAW,SAAS;GAC7E,SAAS,KAAK,KAAK,IAAI;GACvB;EACF;EAEA,IAAI,SAAS,WAAW,GAAG,UAAU,EAAE,GAAG;GACxC,IAAI,CAAC,QAAQ,SAAS,KAAK;IACzB,SAAS,KAAK,KAAK,EAAE,OAAO,0BAA0B,CAAC;IACvD;GACF;GACA,WACE,KACA,KACA,QAAQ,SAAS,KACjB,WACA,OACA,qBAAqB,GACrB,YACF,CAAC,CAAC,YAAY,CAAC,CAAC;GAChB;EACF;EAEA,IAAI,aAAa,aAAa,SAAS,WAAW,GAAG,UAAU,EAAE,GAAG;GAClE,IAAI,CAAC,QAAQ,SAAS,KAAK;IACzB,SAAS,KAAK,KAAK,EAAE,OAAO,0BAA0B,CAAC;IACvD;GACF;GACA,WACE,KACA,KACA,QAAQ,SAAS,KACjB,WACA,WACA,qBAAqB,GACrB,YACF,CAAC,CAAC,YAAY,CAAC,CAAC;GAChB;EACF;EAEA,IAAI,eAAe;GACjB,cAAc,KAAK,WAAW;IAC5B,SAAS,KAAK,KAAK,WAAW;GAChC,CAAC;GACD;EACF;EAEA,SAAS,KAAK,KAAK,WAAW;CAChC,CAAC;CAED,MAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,OAAO,KAAK,SAAS,MAAM;EAC3B,OAAO,OAAO,YAAY,QAAQ,CAAC;CACrC,CAAC;CAED,MAAM,UAAU,OAAO,QAAQ;CAC/B,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU;EAC3C,MAAM,IAAI,SAAe,YAAY,OAAO,YAAY,QAAQ,CAAC,CAAC;EAClE,MAAM,IAAI,MAAM,6BAA6B;CAC/C;CACA,eAAe,QAAQ;CAEvB,OAAO;EACL,MAAM;EACN,OAAO,YAAY;GACjB,MAAM,IAAI,SAAe,YAAY,OAAO,YAAY,QAAQ,CAAC,CAAC;EACpE;CACF;AACF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
//#region src/dev/host.d.ts
|
|
3
|
+
interface DirectModuleHostHandler {
|
|
4
|
+
handle(request: Request, options: {
|
|
5
|
+
prefix: string;
|
|
6
|
+
context: unknown;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
response?: Response;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
interface DirectModuleHostHandlers {
|
|
12
|
+
rpc: DirectModuleHostHandler | null;
|
|
13
|
+
api: DirectModuleHostHandler | null;
|
|
14
|
+
}
|
|
15
|
+
interface DirectModuleHostOptions {
|
|
16
|
+
name: string;
|
|
17
|
+
handlers: DirectModuleHostHandlers;
|
|
18
|
+
effectContext: unknown;
|
|
19
|
+
port?: number;
|
|
20
|
+
version?: string;
|
|
21
|
+
rpcPrefix?: `/${string}`;
|
|
22
|
+
apiPrefix?: `/${string}`;
|
|
23
|
+
staticHandler?: ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void) | null;
|
|
24
|
+
}
|
|
25
|
+
interface DirectModuleHostHandle {
|
|
26
|
+
port: number;
|
|
27
|
+
close: () => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
declare function startDirectModuleHost(options: DirectModuleHostOptions): Promise<DirectModuleHostHandle>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { DirectModuleHostHandle, DirectModuleHostHandler, DirectModuleHostHandlers, DirectModuleHostOptions, startDirectModuleHost };
|
|
32
|
+
//# sourceMappingURL=host.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.cts","names":[],"sources":["../../src/dev/host.ts"],"mappings":";;UA+DiB;EACf,OACE,SAAS,SACT;IAAW;IAAgB;MAC1B;IAAU,WAAW;;;UAuCT;EACf,KAAK;EACL,KAAK;;UAGU;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA,kBACM,KAAK,KAAK,iBAAiB,KAAK,KAAK,gBAAgB;;UAI5C;EACf;EACA,aAAa;;iBAGO,sBACpB,SAAS,0BACR,QAAQ"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
//#region src/dev/host.d.ts
|
|
3
|
+
interface DirectModuleHostHandler {
|
|
4
|
+
handle(request: Request, options: {
|
|
5
|
+
prefix: string;
|
|
6
|
+
context: unknown;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
response?: Response;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
interface DirectModuleHostHandlers {
|
|
12
|
+
rpc: DirectModuleHostHandler | null;
|
|
13
|
+
api: DirectModuleHostHandler | null;
|
|
14
|
+
}
|
|
15
|
+
interface DirectModuleHostOptions {
|
|
16
|
+
name: string;
|
|
17
|
+
handlers: DirectModuleHostHandlers;
|
|
18
|
+
effectContext: unknown;
|
|
19
|
+
port?: number;
|
|
20
|
+
version?: string;
|
|
21
|
+
rpcPrefix?: `/${string}`;
|
|
22
|
+
apiPrefix?: `/${string}`;
|
|
23
|
+
staticHandler?: ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void) | null;
|
|
24
|
+
}
|
|
25
|
+
interface DirectModuleHostHandle {
|
|
26
|
+
port: number;
|
|
27
|
+
close: () => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
declare function startDirectModuleHost(options: DirectModuleHostOptions): Promise<DirectModuleHostHandle>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { DirectModuleHostHandle, DirectModuleHostHandler, DirectModuleHostHandlers, DirectModuleHostOptions, startDirectModuleHost };
|
|
32
|
+
//# sourceMappingURL=host.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.mts","names":[],"sources":["../../src/dev/host.ts"],"mappings":";;UA+DiB;EACf,OACE,SAAS,SACT;IAAW;IAAgB;MAC1B;IAAU,WAAW;;;UAuCT;EACf,KAAK;EACL,KAAK;;UAGU;EACf;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA,kBACM,KAAK,KAAK,iBAAiB,KAAK,KAAK,gBAAgB;;UAI5C;EACf;EACA,aAAa;;iBAGO,sBACpB,SAAS,0BACR,QAAQ"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
|
|
3
|
+
//#region src/dev/host.ts
|
|
4
|
+
const corsHeaders = {
|
|
5
|
+
"Access-Control-Allow-Origin": "*",
|
|
6
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
7
|
+
"Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
|
|
8
|
+
};
|
|
9
|
+
const applyCorsHeaders = (res) => {
|
|
10
|
+
Object.entries(corsHeaders).forEach(([key, value]) => {
|
|
11
|
+
res.setHeader(key, value);
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
const sendJson = (res, status, body) => {
|
|
15
|
+
res.statusCode = status;
|
|
16
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
17
|
+
res.end(JSON.stringify(body));
|
|
18
|
+
};
|
|
19
|
+
const sendText = (res, status, text) => {
|
|
20
|
+
res.statusCode = status;
|
|
21
|
+
res.setHeader("content-type", "text/plain; charset=utf-8");
|
|
22
|
+
res.end(text);
|
|
23
|
+
};
|
|
24
|
+
const pathnameOf = (req) => ((req.url ?? "/").split("?")[0] ?? "/").replace(/\/+$/, "") || "/";
|
|
25
|
+
const toWebRequest = (req, port) => {
|
|
26
|
+
const url = `http://localhost:${port}${req.url}`;
|
|
27
|
+
return new Request(url, {
|
|
28
|
+
method: req.method,
|
|
29
|
+
headers: req.headers,
|
|
30
|
+
body: req.method !== "GET" && req.method !== "HEAD" ? req : void 0,
|
|
31
|
+
duplex: req.method !== "GET" && req.method !== "HEAD" ? "half" : void 0
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
const buildHostContext = (effectContext, webRequest, cachedRawBodyRef) => {
|
|
35
|
+
const rawClone = webRequest.method === "GET" || webRequest.method === "HEAD" ? null : webRequest.clone();
|
|
36
|
+
return {
|
|
37
|
+
reqHeaders: webRequest.headers,
|
|
38
|
+
"effect/context": effectContext,
|
|
39
|
+
getRawBody: async () => {
|
|
40
|
+
if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;
|
|
41
|
+
if (!rawClone) {
|
|
42
|
+
cachedRawBodyRef.value = "";
|
|
43
|
+
return cachedRawBodyRef.value;
|
|
44
|
+
}
|
|
45
|
+
cachedRawBodyRef.value = await rawClone.text();
|
|
46
|
+
return cachedRawBodyRef.value;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
const handleOrpc = async (req, res, handler, prefix, errorLabel, effectContext, port) => {
|
|
51
|
+
try {
|
|
52
|
+
const webRequest = toWebRequest(req, port);
|
|
53
|
+
const result = await handler.handle(webRequest, {
|
|
54
|
+
prefix,
|
|
55
|
+
context: buildHostContext(effectContext, webRequest, { value: null })
|
|
56
|
+
});
|
|
57
|
+
if (result.response) {
|
|
58
|
+
res.statusCode = result.response.status;
|
|
59
|
+
result.response.headers.forEach((value, key) => {
|
|
60
|
+
res.setHeader(key, value);
|
|
61
|
+
});
|
|
62
|
+
const text = await result.response.text();
|
|
63
|
+
if (!res.getHeader("content-length")) res.setHeader("content-length", Buffer.byteLength(text).toString());
|
|
64
|
+
res.end(text);
|
|
65
|
+
} else sendText(res, 404, "Not Found");
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(`${errorLabel} error:`, error);
|
|
68
|
+
sendJson(res, 500, { error: error.message });
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
async function startDirectModuleHost(options) {
|
|
72
|
+
const port = options.port ?? 0;
|
|
73
|
+
let resolvedPort = port;
|
|
74
|
+
const rpcPrefix = options.rpcPrefix ?? "/api/rpc";
|
|
75
|
+
const apiPrefix = options.apiPrefix ?? "/api";
|
|
76
|
+
const staticHandler = options.staticHandler ?? null;
|
|
77
|
+
const resolveEffectContext = typeof options.effectContext === "function" ? options.effectContext : () => options.effectContext;
|
|
78
|
+
const server = createServer((req, res) => {
|
|
79
|
+
applyCorsHeaders(res);
|
|
80
|
+
const pathname = pathnameOf(req);
|
|
81
|
+
if (req.method === "OPTIONS") {
|
|
82
|
+
res.statusCode = 200;
|
|
83
|
+
res.end();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (pathname === "/" && (req.method === "GET" || req.method === "HEAD")) {
|
|
87
|
+
sendJson(res, 200, {
|
|
88
|
+
ok: true,
|
|
89
|
+
module: options.name,
|
|
90
|
+
version: options.version,
|
|
91
|
+
status: options.handlers.rpc ? "ready" : "loading",
|
|
92
|
+
endpoints: {
|
|
93
|
+
health: "/health",
|
|
94
|
+
docs: apiPrefix,
|
|
95
|
+
rpc: rpcPrefix
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (pathname === "/health" && (req.method === "GET" || req.method === "HEAD")) {
|
|
101
|
+
sendText(res, 200, "OK");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (pathname.startsWith(`${rpcPrefix}/`)) {
|
|
105
|
+
if (!options.handlers.rpc) {
|
|
106
|
+
sendJson(res, 503, { error: "Plugin still loading..." });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
handleOrpc(req, res, options.handlers.rpc, rpcPrefix, "RPC", resolveEffectContext(), resolvedPort).catch(() => {});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {
|
|
113
|
+
if (!options.handlers.api) {
|
|
114
|
+
sendJson(res, 503, { error: "Plugin still loading..." });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
handleOrpc(req, res, options.handlers.api, apiPrefix, "OpenAPI", resolveEffectContext(), resolvedPort).catch(() => {});
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (staticHandler) {
|
|
121
|
+
staticHandler(req, res, () => {
|
|
122
|
+
sendText(res, 404, "Not Found");
|
|
123
|
+
});
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
sendText(res, 404, "Not Found");
|
|
127
|
+
});
|
|
128
|
+
await new Promise((resolve, reject) => {
|
|
129
|
+
server.once("error", reject);
|
|
130
|
+
server.listen(port, () => resolve());
|
|
131
|
+
});
|
|
132
|
+
const address = server.address();
|
|
133
|
+
if (!address || typeof address === "string") {
|
|
134
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
135
|
+
throw new Error("Failed to resolve host port");
|
|
136
|
+
}
|
|
137
|
+
resolvedPort = address.port;
|
|
138
|
+
return {
|
|
139
|
+
port: resolvedPort,
|
|
140
|
+
close: async () => {
|
|
141
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//#endregion
|
|
147
|
+
export { startDirectModuleHost };
|
|
148
|
+
//# sourceMappingURL=host.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.mjs","names":[],"sources":["../../src/dev/host.ts"],"sourcesContent":["import type http from \"node:http\";\nimport { createServer } from \"node:http\";\n\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, PATCH, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"X-Requested-With, content-type, Authorization\",\n};\n\nconst applyCorsHeaders = (res: http.ServerResponse) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst sendJson = (res: http.ServerResponse, status: number, body: unknown) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify(body));\n};\n\nconst sendText = (res: http.ServerResponse, status: number, text: string) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(text);\n};\n\nconst pathnameOf = (req: http.IncomingMessage): string =>\n ((req.url ?? \"/\").split(\"?\")[0] ?? \"/\").replace(/\\/+$/, \"\") || \"/\";\n\nconst toWebRequest = (req: http.IncomingMessage, port: number): Request => {\n const url = `http://localhost:${port}${req.url}`;\n return new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n};\n\nconst buildHostContext = (\n effectContext: unknown,\n webRequest: Request,\n cachedRawBodyRef: { value: string | null },\n) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n\n return {\n reqHeaders: webRequest.headers,\n \"effect/context\": effectContext,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBodyRef.value !== null) return cachedRawBodyRef.value;\n if (!rawClone) {\n cachedRawBodyRef.value = \"\";\n return cachedRawBodyRef.value;\n }\n cachedRawBodyRef.value = await rawClone.text();\n return cachedRawBodyRef.value;\n },\n };\n};\n\nexport interface DirectModuleHostHandler {\n handle(\n request: Request,\n options: { prefix: string; context: unknown },\n ): Promise<{ response?: Response }>;\n}\n\nconst handleOrpc = async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n handler: DirectModuleHostHandler,\n prefix: string,\n errorLabel: string,\n effectContext: unknown,\n port: number,\n) => {\n try {\n const webRequest = toWebRequest(req, port);\n const cachedRawBodyRef = { value: null as string | null };\n const result = await handler.handle(webRequest, {\n prefix,\n context: buildHostContext(effectContext, webRequest, cachedRawBodyRef),\n });\n\n if (result.response) {\n res.statusCode = result.response.status;\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n if (!res.getHeader(\"content-length\")) {\n res.setHeader(\"content-length\", Buffer.byteLength(text).toString());\n }\n res.end(text);\n } else {\n sendText(res, 404, \"Not Found\");\n }\n } catch (error) {\n console.error(`${errorLabel} error:`, error);\n sendJson(res, 500, { error: (error as Error).message });\n }\n};\n\nexport interface DirectModuleHostHandlers {\n rpc: DirectModuleHostHandler | null;\n api: DirectModuleHostHandler | null;\n}\n\nexport interface DirectModuleHostOptions {\n name: string;\n handlers: DirectModuleHostHandlers;\n effectContext: unknown;\n port?: number;\n version?: string;\n rpcPrefix?: `/${string}`;\n apiPrefix?: `/${string}`;\n staticHandler?:\n | ((req: http.IncomingMessage, res: http.ServerResponse, next: () => void) => void)\n | null;\n}\n\nexport interface DirectModuleHostHandle {\n port: number;\n close: () => Promise<void>;\n}\n\nexport async function startDirectModuleHost(\n options: DirectModuleHostOptions,\n): Promise<DirectModuleHostHandle> {\n const port = options.port ?? 0;\n let resolvedPort = port;\n const rpcPrefix = options.rpcPrefix ?? \"/api/rpc\";\n const apiPrefix = options.apiPrefix ?? \"/api\";\n const staticHandler = options.staticHandler ?? null;\n const resolveEffectContext: () => unknown =\n typeof options.effectContext === \"function\"\n ? (options.effectContext as () => unknown)\n : () => options.effectContext;\n\n const server = createServer((req, res) => {\n applyCorsHeaders(res);\n const pathname = pathnameOf(req);\n\n if (req.method === \"OPTIONS\") {\n res.statusCode = 200;\n res.end();\n return;\n }\n\n if (pathname === \"/\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendJson(res, 200, {\n ok: true,\n module: options.name,\n version: options.version,\n status: options.handlers.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/health\",\n docs: apiPrefix,\n rpc: rpcPrefix,\n },\n });\n return;\n }\n\n if (pathname === \"/health\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendText(res, 200, \"OK\");\n return;\n }\n\n if (pathname.startsWith(`${rpcPrefix}/`)) {\n if (!options.handlers.rpc) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.rpc,\n rpcPrefix,\n \"RPC\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (pathname === apiPrefix || pathname.startsWith(`${apiPrefix}/`)) {\n if (!options.handlers.api) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(\n req,\n res,\n options.handlers.api,\n apiPrefix,\n \"OpenAPI\",\n resolveEffectContext(),\n resolvedPort,\n ).catch(() => {});\n return;\n }\n\n if (staticHandler) {\n staticHandler(req, res, () => {\n sendText(res, 404, \"Not Found\");\n });\n return;\n }\n\n sendText(res, 404, \"Not Found\");\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, () => resolve());\n });\n\n const address = server.address();\n if (!address || typeof address === \"string\") {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n throw new Error(\"Failed to resolve host port\");\n }\n resolvedPort = address.port;\n\n return {\n port: resolvedPort,\n close: async () => {\n await new Promise<void>((resolve) => server.close(() => resolve()));\n },\n };\n}\n"],"mappings":"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"}
|