@undefineds.co/xpod 0.3.53 → 0.3.54
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/config/cli.json +72 -0
- package/config/components-ignore.json +21 -0
- package/config/extensions.local.initializer.json +77 -8
- package/config/resolver.json +72 -4
- package/dist/api/ApiServer.d.ts +12 -0
- package/dist/api/ApiServer.js +14 -3
- package/dist/api/ApiServer.js.map +1 -1
- package/dist/api/auth/NodeTokenAuthenticator.d.ts +0 -8
- package/dist/api/auth/NodeTokenAuthenticator.js +3 -46
- package/dist/api/auth/NodeTokenAuthenticator.js.map +1 -1
- package/dist/api/container/local.js +5 -36
- package/dist/api/container/local.js.map +1 -1
- package/dist/api/container/routes.js +6 -0
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/handlers/EdgeNodeSignalHandler.js +25 -3
- package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
- package/dist/api/handlers/ReachabilityHandler.d.ts +13 -0
- package/dist/api/handlers/ReachabilityHandler.js +388 -0
- package/dist/api/handlers/ReachabilityHandler.js.map +1 -0
- package/dist/api/runs/InngestRunExecutionBackend.d.ts +2 -2
- package/dist/api/tasks/InngestTaskScheduler.d.ts +4 -4
- package/dist/components/components.jsonld +14 -2
- package/dist/components/context.jsonld +439 -3
- package/dist/edge/EdgeNodeAgent.d.ts +43 -0
- package/dist/edge/EdgeNodeAgent.js +208 -1
- package/dist/edge/EdgeNodeAgent.js.map +1 -1
- package/dist/edge/EdgeNodeAgent.jsonld +166 -0
- package/dist/edge/EdgeNodeAgentInitializer.d.ts +20 -2
- package/dist/edge/EdgeNodeAgentInitializer.js +53 -2
- package/dist/edge/EdgeNodeAgentInitializer.js.map +1 -1
- package/dist/edge/EdgeNodeAgentInitializer.jsonld +409 -0
- package/dist/edge/reachability/CanonicalFetch.d.ts +7 -0
- package/dist/edge/reachability/CanonicalFetch.js +49 -0
- package/dist/edge/reachability/CanonicalFetch.js.map +1 -0
- package/dist/edge/reachability/CanonicalFetch.jsonld +25 -0
- package/dist/edge/reachability/ManagedClientFetch.d.ts +26 -0
- package/dist/edge/reachability/ManagedClientFetch.js +155 -0
- package/dist/edge/reachability/ManagedClientFetch.js.map +1 -0
- package/dist/edge/reachability/ManagedClientFetch.jsonld +83 -0
- package/dist/edge/reachability/ManagedClientP2PSmoke.d.ts +16 -0
- package/dist/edge/reachability/ManagedClientP2PSmoke.js +31 -0
- package/dist/edge/reachability/ManagedClientP2PSmoke.js.map +1 -0
- package/dist/edge/reachability/ManagedClientP2PSmoke.jsonld +65 -0
- package/dist/edge/reachability/ManagedRouteSelector.d.ts +7 -0
- package/dist/edge/reachability/ManagedRouteSelector.js +43 -0
- package/dist/edge/reachability/ManagedRouteSelector.js.map +1 -0
- package/dist/edge/reachability/ManagedRouteSelector.jsonld +29 -0
- package/dist/edge/reachability/P2PDataPlane.d.ts +37 -0
- package/dist/edge/reachability/P2PDataPlane.js +160 -0
- package/dist/edge/reachability/P2PDataPlane.js.map +1 -0
- package/dist/edge/reachability/P2PDataPlane.jsonld +134 -0
- package/dist/edge/reachability/P2PRealnetAcceptance.d.ts +74 -0
- package/dist/edge/reachability/P2PRealnetAcceptance.js +240 -0
- package/dist/edge/reachability/P2PRealnetAcceptance.js.map +1 -0
- package/dist/edge/reachability/P2PRealnetAcceptance.jsonld +283 -0
- package/dist/edge/reachability/P2PSignalingClient.d.ts +24 -0
- package/dist/edge/reachability/P2PSignalingClient.js +138 -0
- package/dist/edge/reachability/P2PSignalingClient.js.map +1 -0
- package/dist/edge/reachability/P2PSignalingClient.jsonld +79 -0
- package/dist/edge/reachability/ReachabilitySessionService.d.ts +55 -0
- package/dist/edge/reachability/ReachabilitySessionService.js +439 -0
- package/dist/edge/reachability/ReachabilitySessionService.js.map +1 -0
- package/dist/edge/reachability/ReachabilitySessionService.jsonld +196 -0
- package/dist/edge/reachability/RouteSetBuilder.d.ts +2 -0
- package/dist/edge/reachability/RouteSetBuilder.js +205 -0
- package/dist/edge/reachability/RouteSetBuilder.js.map +1 -0
- package/dist/edge/reachability/TcpP2PDataPlaneTransport.d.ts +47 -0
- package/dist/edge/reachability/TcpP2PDataPlaneTransport.js +281 -0
- package/dist/edge/reachability/TcpP2PDataPlaneTransport.js.map +1 -0
- package/dist/edge/reachability/TcpP2PDataPlaneTransport.jsonld +183 -0
- package/dist/edge/reachability/TcpP2PSignalingSession.d.ts +149 -0
- package/dist/edge/reachability/TcpP2PSignalingSession.js +699 -0
- package/dist/edge/reachability/TcpP2PSignalingSession.js.map +1 -0
- package/dist/edge/reachability/TcpP2PSignalingSession.jsonld +474 -0
- package/dist/edge/reachability/index.d.ts +12 -0
- package/dist/edge/reachability/index.js +29 -0
- package/dist/edge/reachability/index.js.map +1 -0
- package/dist/edge/reachability/types.d.ts +114 -0
- package/dist/edge/reachability/types.js +3 -0
- package/dist/edge/reachability/types.js.map +1 -0
- package/dist/edge/reachability/types.jsonld +457 -0
- package/dist/http/EdgeNodeProxyHttpHandler.d.ts +2 -0
- package/dist/http/EdgeNodeProxyHttpHandler.js +19 -1
- package/dist/http/EdgeNodeProxyHttpHandler.js.map +1 -1
- package/dist/http/EdgeNodeProxyHttpHandler.jsonld +8 -0
- package/dist/identity/drizzle/EdgeNodeRepository.js +1 -1
- package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime/bootstrap.js +8 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/service/EdgeNodeSignalClient.js +5 -1
- package/dist/service/EdgeNodeSignalClient.js.map +1 -1
- package/dist/storage/rdf/PostgresRdfEngine.d.ts +1 -0
- package/dist/storage/rdf/PostgresRdfEngine.js +53 -37
- package/dist/storage/rdf/PostgresRdfEngine.js.map +1 -1
- package/dist/storage/rdf/PostgresRdfEngine.jsonld +4 -0
- package/dist/test-utils/index.d.ts +2 -0
- package/dist/test-utils/index.js +3 -1
- package/dist/test-utils/index.js.map +1 -1
- package/dist/test-utils/local-managed-client-p2p-e2e-smoke.d.ts +63 -0
- package/dist/test-utils/local-managed-client-p2p-e2e-smoke.js +478 -0
- package/dist/test-utils/local-managed-client-p2p-e2e-smoke.js.map +1 -0
- package/package.json +11 -4
- package/static/app/assets/_commonjsHelpers-B-UnjaXt.js +1 -0
- package/static/app/assets/index-AaQ1qxhy.js +171 -0
- package/static/app/assets/inrupt-smoke.js +131 -0
- package/static/app/assets/main.css +1 -0
- package/static/app/assets/main.js +6 -6
- package/static/app/index.html +2 -1
- package/static/app/inrupt-smoke.html +14 -0
- package/static/app/reachability.html +221 -0
- package/static/app/reachability.svg +7 -0
- package/static/app/reachability.webmanifest +18 -0
- package/static/app/signal-pod.html +293 -0
- package/static/app/assets/index.css +0 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSignaledManagedClientFetch = createSignaledManagedClientFetch;
|
|
4
|
+
exports.createManagedClientFetch = createManagedClientFetch;
|
|
5
|
+
const CanonicalFetch_1 = require("./CanonicalFetch");
|
|
6
|
+
const P2PDataPlane_1 = require("./P2PDataPlane");
|
|
7
|
+
const P2PSignalingClient_1 = require("./P2PSignalingClient");
|
|
8
|
+
const TcpP2PSignalingSession_1 = require("./TcpP2PSignalingSession");
|
|
9
|
+
async function createSignaledManagedClientFetch(options) {
|
|
10
|
+
const { apiBaseUrl, nodeId, token, clientId, fetchImpl, probe, probeTimeoutMs, ...p2pOptions } = options;
|
|
11
|
+
const routeSet = await fetchManagedRouteSet({
|
|
12
|
+
apiBaseUrl,
|
|
13
|
+
nodeId,
|
|
14
|
+
token,
|
|
15
|
+
fetchImpl,
|
|
16
|
+
});
|
|
17
|
+
const signaling = (0, P2PSignalingClient_1.createP2PSignalingClient)({
|
|
18
|
+
apiBaseUrl,
|
|
19
|
+
nodeId,
|
|
20
|
+
token,
|
|
21
|
+
fetchImpl,
|
|
22
|
+
});
|
|
23
|
+
return createManagedClientFetch({
|
|
24
|
+
routeSet,
|
|
25
|
+
fetchImpl,
|
|
26
|
+
probe,
|
|
27
|
+
probeTimeoutMs,
|
|
28
|
+
p2p: {
|
|
29
|
+
...p2pOptions,
|
|
30
|
+
signaling,
|
|
31
|
+
clientId,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async function createManagedClientFetch(options) {
|
|
36
|
+
const errors = [];
|
|
37
|
+
for (const route of candidateRoutes(options.routeSet)) {
|
|
38
|
+
if (!await routeProbeSucceeds(route, options)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (route.kind === 'p2p') {
|
|
42
|
+
if (!options.p2p) {
|
|
43
|
+
errors.push(`Route ${route.id} requires p2p options`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const connected = await (0, TcpP2PSignalingSession_1.connectSignaledRawTcpP2PTransport)(options.p2p);
|
|
48
|
+
const fetchViaP2P = (0, P2PDataPlane_1.createP2PDataPlaneFetch)({
|
|
49
|
+
route: connected.rawTcpRoute ?? route,
|
|
50
|
+
transport: connected.transport,
|
|
51
|
+
});
|
|
52
|
+
return managedResult(route, fetchViaP2P, connected.transport);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
errors.push(`Route ${route.id} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return managedResult(route, (0, CanonicalFetch_1.createCanonicalFetch)({ route, fetchImpl: options.fetchImpl }));
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`No managed client route could be opened${errors.length > 0 ? `: ${errors.join('; ')}` : ''}`);
|
|
62
|
+
}
|
|
63
|
+
function candidateRoutes(routeSet) {
|
|
64
|
+
return routeSet.routes
|
|
65
|
+
.filter((route) => route.health !== 'unreachable')
|
|
66
|
+
.slice()
|
|
67
|
+
.sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id));
|
|
68
|
+
}
|
|
69
|
+
async function routeProbeSucceeds(route, options) {
|
|
70
|
+
const probe = options.probe ?? defaultManagedProbe;
|
|
71
|
+
const controller = new AbortController();
|
|
72
|
+
const timeout = setTimeout(() => controller.abort(), options.probeTimeoutMs ?? 1_000);
|
|
73
|
+
try {
|
|
74
|
+
return await Promise.resolve(probe(route, controller.signal));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
clearTimeout(timeout);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function defaultManagedProbe(route, signal) {
|
|
84
|
+
if (route.kind === 'p2p') {
|
|
85
|
+
return route.health !== 'unreachable';
|
|
86
|
+
}
|
|
87
|
+
if (!route.targetUrl.startsWith('http://') && !route.targetUrl.startsWith('https://')) {
|
|
88
|
+
return route.health === 'healthy';
|
|
89
|
+
}
|
|
90
|
+
const response = await fetch(new URL('/.well-known/solid', route.targetUrl), {
|
|
91
|
+
method: 'HEAD',
|
|
92
|
+
signal,
|
|
93
|
+
});
|
|
94
|
+
return response.ok || response.status === 401 || response.status === 403;
|
|
95
|
+
}
|
|
96
|
+
function managedResult(route, fetch, transport) {
|
|
97
|
+
return {
|
|
98
|
+
route,
|
|
99
|
+
fetch,
|
|
100
|
+
close() {
|
|
101
|
+
transport?.close();
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async function fetchManagedRouteSet(options) {
|
|
106
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
107
|
+
const headers = new Headers({ accept: 'application/json' });
|
|
108
|
+
if (options.token) {
|
|
109
|
+
headers.set('authorization', `Bearer ${options.token}`);
|
|
110
|
+
}
|
|
111
|
+
const response = await fetchImpl(new URL(`/v1/signal/nodes/${encodeURIComponent(options.nodeId)}/routes`, options.apiBaseUrl).toString(), {
|
|
112
|
+
method: 'GET',
|
|
113
|
+
headers,
|
|
114
|
+
});
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
throw new Error(`Route set request failed with ${response.status}: ${await safeReadText(response)}`);
|
|
117
|
+
}
|
|
118
|
+
const body = await response.json();
|
|
119
|
+
if (!isRouteSet(body)) {
|
|
120
|
+
throw new Error('Route set response is not a valid route set');
|
|
121
|
+
}
|
|
122
|
+
return body;
|
|
123
|
+
}
|
|
124
|
+
async function safeReadText(response) {
|
|
125
|
+
try {
|
|
126
|
+
return await response.text();
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return '';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function isRouteSet(value) {
|
|
133
|
+
return isRecord(value)
|
|
134
|
+
&& typeof value.nodeId === 'string'
|
|
135
|
+
&& typeof value.canonicalUrl === 'string'
|
|
136
|
+
&& typeof value.generatedAt === 'string'
|
|
137
|
+
&& Array.isArray(value.routes)
|
|
138
|
+
&& value.routes.every(isAccessRoute);
|
|
139
|
+
}
|
|
140
|
+
function isAccessRoute(value) {
|
|
141
|
+
return isRecord(value)
|
|
142
|
+
&& typeof value.id === 'string'
|
|
143
|
+
&& typeof value.nodeId === 'string'
|
|
144
|
+
&& typeof value.canonicalUrl === 'string'
|
|
145
|
+
&& typeof value.kind === 'string'
|
|
146
|
+
&& typeof value.targetUrl === 'string'
|
|
147
|
+
&& typeof value.priority === 'number'
|
|
148
|
+
&& typeof value.requiresManagedClient === 'boolean'
|
|
149
|
+
&& typeof value.visibility === 'string'
|
|
150
|
+
&& typeof value.health === 'string';
|
|
151
|
+
}
|
|
152
|
+
function isRecord(value) {
|
|
153
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=ManagedClientFetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ManagedClientFetch.js","sourceRoot":"","sources":["../../../src/edge/reachability/ManagedClientFetch.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCanonicalFetch, type CanonicalFetch } from './CanonicalFetch';\nimport { createP2PDataPlaneFetch } from './P2PDataPlane';\nimport { createP2PSignalingClient } from './P2PSignalingClient';\nimport {\n connectSignaledRawTcpP2PTransport,\n type ConnectSignaledRawTcpP2PTransportOptions,\n} from './TcpP2PSignalingSession';\nimport type { TcpP2PDataPlaneTransport } from './TcpP2PDataPlaneTransport';\nimport type { AccessRoute, RouteSet } from './types';\n\ntype ManagedClientP2POptions =\n Omit<ConnectSignaledRawTcpP2PTransportOptions, 'signaling' | 'clientId'>\n & Pick<ConnectSignaledRawTcpP2PTransportOptions, 'signaling' | 'clientId'>;\n\ntype SignaledManagedClientP2POptions = Omit<ManagedClientP2POptions, 'signaling' | 'clientId'>;\n\nexport interface ManagedClientFetchOptions {\n routeSet: RouteSet;\n fetchImpl?: typeof fetch;\n probe?: (route: AccessRoute, signal: AbortSignal) => Promise<boolean> | boolean;\n probeTimeoutMs?: number;\n p2p?: ManagedClientP2POptions;\n}\n\nexport interface ManagedClientFetch {\n route: AccessRoute;\n fetch: CanonicalFetch;\n close(): void;\n}\n\nexport interface SignaledManagedClientFetchOptions\n extends Omit<ManagedClientFetchOptions, 'routeSet' | 'p2p'>, SignaledManagedClientP2POptions {\n apiBaseUrl: string;\n nodeId: string;\n token?: string;\n clientId: string;\n}\n\nexport async function createSignaledManagedClientFetch(options: SignaledManagedClientFetchOptions): Promise<ManagedClientFetch> {\n const {\n apiBaseUrl,\n nodeId,\n token,\n clientId,\n fetchImpl,\n probe,\n probeTimeoutMs,\n ...p2pOptions\n } = options;\n const routeSet = await fetchManagedRouteSet({\n apiBaseUrl,\n nodeId,\n token,\n fetchImpl,\n });\n const signaling = createP2PSignalingClient({\n apiBaseUrl,\n nodeId,\n token,\n fetchImpl,\n });\n return createManagedClientFetch({\n routeSet,\n fetchImpl,\n probe,\n probeTimeoutMs,\n p2p: {\n ...p2pOptions,\n signaling,\n clientId,\n },\n });\n}\n\nexport async function createManagedClientFetch(options: ManagedClientFetchOptions): Promise<ManagedClientFetch> {\n const errors: string[] = [];\n for (const route of candidateRoutes(options.routeSet)) {\n if (!await routeProbeSucceeds(route, options)) {\n continue;\n }\n if (route.kind === 'p2p') {\n if (!options.p2p) {\n errors.push(`Route ${route.id} requires p2p options`);\n continue;\n }\n try {\n const connected = await connectSignaledRawTcpP2PTransport(options.p2p);\n const fetchViaP2P = createP2PDataPlaneFetch({\n route: connected.rawTcpRoute ?? route,\n transport: connected.transport,\n });\n return managedResult(route, fetchViaP2P, connected.transport);\n } catch (error) {\n errors.push(`Route ${route.id} failed: ${error instanceof Error ? error.message : String(error)}`);\n continue;\n }\n }\n\n return managedResult(route, createCanonicalFetch({ route, fetchImpl: options.fetchImpl }));\n }\n\n throw new Error(`No managed client route could be opened${errors.length > 0 ? `: ${errors.join('; ')}` : ''}`);\n}\n\nfunction candidateRoutes(routeSet: RouteSet): AccessRoute[] {\n return routeSet.routes\n .filter((route) => route.health !== 'unreachable')\n .slice()\n .sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id));\n}\n\nasync function routeProbeSucceeds(route: AccessRoute, options: ManagedClientFetchOptions): Promise<boolean> {\n const probe = options.probe ?? defaultManagedProbe;\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), options.probeTimeoutMs ?? 1_000);\n try {\n return await Promise.resolve(probe(route, controller.signal));\n } catch {\n return false;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nasync function defaultManagedProbe(route: AccessRoute, signal: AbortSignal): Promise<boolean> {\n if (route.kind === 'p2p') {\n return route.health !== 'unreachable';\n }\n if (!route.targetUrl.startsWith('http://') && !route.targetUrl.startsWith('https://')) {\n return route.health === 'healthy';\n }\n const response = await fetch(new URL('/.well-known/solid', route.targetUrl), {\n method: 'HEAD',\n signal,\n });\n return response.ok || response.status === 401 || response.status === 403;\n}\n\nfunction managedResult(\n route: AccessRoute,\n fetch: CanonicalFetch,\n transport?: TcpP2PDataPlaneTransport,\n): ManagedClientFetch {\n return {\n route,\n fetch,\n close(): void {\n transport?.close();\n },\n };\n}\n\nasync function fetchManagedRouteSet(options: {\n apiBaseUrl: string;\n nodeId: string;\n token?: string;\n fetchImpl?: typeof fetch;\n}): Promise<RouteSet> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const headers = new Headers({ accept: 'application/json' });\n if (options.token) {\n headers.set('authorization', `Bearer ${options.token}`);\n }\n const response = await fetchImpl(new URL(`/v1/signal/nodes/${encodeURIComponent(options.nodeId)}/routes`, options.apiBaseUrl).toString(), {\n method: 'GET',\n headers,\n });\n if (!response.ok) {\n throw new Error(`Route set request failed with ${response.status}: ${await safeReadText(response)}`);\n }\n const body = await response.json() as unknown;\n if (!isRouteSet(body)) {\n throw new Error('Route set response is not a valid route set');\n }\n return body;\n}\n\nasync function safeReadText(response: Response): Promise<string> {\n try {\n return await response.text();\n } catch {\n return '';\n }\n}\n\nfunction isRouteSet(value: unknown): value is RouteSet {\n return isRecord(value)\n && typeof value.nodeId === 'string'\n && typeof value.canonicalUrl === 'string'\n && typeof value.generatedAt === 'string'\n && Array.isArray(value.routes)\n && value.routes.every(isAccessRoute);\n}\n\nfunction isAccessRoute(value: unknown): value is AccessRoute {\n return isRecord(value)\n && typeof value.id === 'string'\n && typeof value.nodeId === 'string'\n && typeof value.canonicalUrl === 'string'\n && typeof value.kind === 'string'\n && typeof value.targetUrl === 'string'\n && typeof value.priority === 'number'\n && typeof value.requiresManagedClient === 'boolean'\n && typeof value.visibility === 'string'\n && typeof value.health === 'string';\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://linkedsoftwaredependencies.org/bundles/npm/@undefineds.co/xpod/^0.0.0/components/context.jsonld"
|
|
4
|
+
],
|
|
5
|
+
"@id": "npmd:@undefineds.co/xpod",
|
|
6
|
+
"components": [
|
|
7
|
+
{
|
|
8
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetchOptions",
|
|
9
|
+
"@type": "AbstractClass",
|
|
10
|
+
"requireElement": "ManagedClientFetchOptions",
|
|
11
|
+
"parameters": [],
|
|
12
|
+
"memberFields": [
|
|
13
|
+
{
|
|
14
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetchOptions__member_routeSet",
|
|
15
|
+
"memberFieldName": "routeSet"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetchOptions__member_fetchImpl",
|
|
19
|
+
"memberFieldName": "fetchImpl"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetchOptions__member_probe",
|
|
23
|
+
"memberFieldName": "probe"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetchOptions__member_probeTimeoutMs",
|
|
27
|
+
"memberFieldName": "probeTimeoutMs"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetchOptions__member_p2p",
|
|
31
|
+
"memberFieldName": "p2p"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"constructorArguments": []
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetch",
|
|
38
|
+
"@type": "AbstractClass",
|
|
39
|
+
"requireElement": "ManagedClientFetch",
|
|
40
|
+
"parameters": [],
|
|
41
|
+
"memberFields": [
|
|
42
|
+
{
|
|
43
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetch__member_route",
|
|
44
|
+
"memberFieldName": "route"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetch__member_fetch",
|
|
48
|
+
"memberFieldName": "fetch"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#ManagedClientFetch__member_close",
|
|
52
|
+
"memberFieldName": "close"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"constructorArguments": []
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#SignaledManagedClientFetchOptions",
|
|
59
|
+
"@type": "AbstractClass",
|
|
60
|
+
"requireElement": "SignaledManagedClientFetchOptions",
|
|
61
|
+
"parameters": [],
|
|
62
|
+
"memberFields": [
|
|
63
|
+
{
|
|
64
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#SignaledManagedClientFetchOptions__member_apiBaseUrl",
|
|
65
|
+
"memberFieldName": "apiBaseUrl"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#SignaledManagedClientFetchOptions__member_nodeId",
|
|
69
|
+
"memberFieldName": "nodeId"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#SignaledManagedClientFetchOptions__member_token",
|
|
73
|
+
"memberFieldName": "token"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#SignaledManagedClientFetchOptions__member_clientId",
|
|
77
|
+
"memberFieldName": "clientId"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"constructorArguments": []
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type SignaledManagedClientFetchOptions } from './ManagedClientFetch';
|
|
2
|
+
import type { AccessRoute } from './types';
|
|
3
|
+
export interface ManagedClientP2PSmokeOptions extends SignaledManagedClientFetchOptions {
|
|
4
|
+
resourceUrl: string;
|
|
5
|
+
requestInit?: RequestInit;
|
|
6
|
+
}
|
|
7
|
+
export interface ManagedClientP2PSmokeResult {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
route: AccessRoute;
|
|
10
|
+
resourceUrl: string;
|
|
11
|
+
status: number;
|
|
12
|
+
statusText: string;
|
|
13
|
+
headers: Record<string, string>;
|
|
14
|
+
body: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function runManagedClientP2PSmoke(options: ManagedClientP2PSmokeOptions): Promise<ManagedClientP2PSmokeResult>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runManagedClientP2PSmoke = runManagedClientP2PSmoke;
|
|
4
|
+
const ManagedClientFetch_1 = require("./ManagedClientFetch");
|
|
5
|
+
async function runManagedClientP2PSmoke(options) {
|
|
6
|
+
const { resourceUrl, requestInit, ...fetchOptions } = options;
|
|
7
|
+
const managed = await (0, ManagedClientFetch_1.createSignaledManagedClientFetch)(fetchOptions);
|
|
8
|
+
try {
|
|
9
|
+
const response = await managed.fetch(resourceUrl, requestInit);
|
|
10
|
+
return {
|
|
11
|
+
ok: response.ok,
|
|
12
|
+
route: managed.route,
|
|
13
|
+
resourceUrl,
|
|
14
|
+
status: response.status,
|
|
15
|
+
statusText: response.statusText,
|
|
16
|
+
headers: headersToRecord(response.headers),
|
|
17
|
+
body: await response.text(),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
finally {
|
|
21
|
+
managed.close();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function headersToRecord(headers) {
|
|
25
|
+
const result = {};
|
|
26
|
+
headers.forEach((value, key) => {
|
|
27
|
+
result[key] = value;
|
|
28
|
+
});
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=ManagedClientP2PSmoke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ManagedClientP2PSmoke.js","sourceRoot":"","sources":["../../../src/edge/reachability/ManagedClientP2PSmoke.ts"],"names":[],"mappings":";;AAqBA,4DAmBC;AAxCD,6DAG8B;AAkBvB,KAAK,UAAU,wBAAwB,CAC5C,OAAqC;IAErC,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC;IAC9D,MAAM,OAAO,GAAG,MAAM,IAAA,qDAAgC,EAAC,YAAY,CAAC,CAAC;IACrE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QAC/D,OAAO;YACL,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,WAAW;YACX,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC1C,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE;SAC5B,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,OAAgB;IACvC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import {\n createSignaledManagedClientFetch,\n type SignaledManagedClientFetchOptions,\n} from './ManagedClientFetch';\nimport type { AccessRoute } from './types';\n\nexport interface ManagedClientP2PSmokeOptions extends SignaledManagedClientFetchOptions {\n resourceUrl: string;\n requestInit?: RequestInit;\n}\n\nexport interface ManagedClientP2PSmokeResult {\n ok: boolean;\n route: AccessRoute;\n resourceUrl: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n body: string;\n}\n\nexport async function runManagedClientP2PSmoke(\n options: ManagedClientP2PSmokeOptions,\n): Promise<ManagedClientP2PSmokeResult> {\n const { resourceUrl, requestInit, ...fetchOptions } = options;\n const managed = await createSignaledManagedClientFetch(fetchOptions);\n try {\n const response = await managed.fetch(resourceUrl, requestInit);\n return {\n ok: response.ok,\n route: managed.route,\n resourceUrl,\n status: response.status,\n statusText: response.statusText,\n headers: headersToRecord(response.headers),\n body: await response.text(),\n };\n } finally {\n managed.close();\n }\n}\n\nfunction headersToRecord(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n}\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://linkedsoftwaredependencies.org/bundles/npm/@undefineds.co/xpod/^0.0.0/components/context.jsonld"
|
|
4
|
+
],
|
|
5
|
+
"@id": "npmd:@undefineds.co/xpod",
|
|
6
|
+
"components": [
|
|
7
|
+
{
|
|
8
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeOptions",
|
|
9
|
+
"@type": "AbstractClass",
|
|
10
|
+
"requireElement": "ManagedClientP2PSmokeOptions",
|
|
11
|
+
"extends": [
|
|
12
|
+
"undefineds:dist/edge/reachability/ManagedClientFetch.jsonld#SignaledManagedClientFetchOptions"
|
|
13
|
+
],
|
|
14
|
+
"parameters": [],
|
|
15
|
+
"memberFields": [
|
|
16
|
+
{
|
|
17
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeOptions__member_resourceUrl",
|
|
18
|
+
"memberFieldName": "resourceUrl"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeOptions__member_requestInit",
|
|
22
|
+
"memberFieldName": "requestInit"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"constructorArguments": []
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeResult",
|
|
29
|
+
"@type": "AbstractClass",
|
|
30
|
+
"requireElement": "ManagedClientP2PSmokeResult",
|
|
31
|
+
"parameters": [],
|
|
32
|
+
"memberFields": [
|
|
33
|
+
{
|
|
34
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeResult__member_ok",
|
|
35
|
+
"memberFieldName": "ok"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeResult__member_route",
|
|
39
|
+
"memberFieldName": "route"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeResult__member_resourceUrl",
|
|
43
|
+
"memberFieldName": "resourceUrl"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeResult__member_status",
|
|
47
|
+
"memberFieldName": "status"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeResult__member_statusText",
|
|
51
|
+
"memberFieldName": "statusText"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeResult__member_headers",
|
|
55
|
+
"memberFieldName": "headers"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"@id": "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld#ManagedClientP2PSmokeResult__member_body",
|
|
59
|
+
"memberFieldName": "body"
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"constructorArguments": []
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AccessRoute, RouteSet } from './types';
|
|
2
|
+
export interface ChooseAccessRouteOptions {
|
|
3
|
+
managedClient: boolean;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
probe?: (route: AccessRoute, signal: AbortSignal) => Promise<boolean> | boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function chooseAccessRoute(routeSet: RouteSet, options: ChooseAccessRouteOptions): Promise<AccessRoute | null>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.chooseAccessRoute = chooseAccessRoute;
|
|
4
|
+
async function chooseAccessRoute(routeSet, options) {
|
|
5
|
+
const candidates = routeSet.routes
|
|
6
|
+
.filter((route) => route.health !== 'unreachable')
|
|
7
|
+
.filter((route) => options.managedClient || !route.requiresManagedClient)
|
|
8
|
+
.slice()
|
|
9
|
+
.sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id));
|
|
10
|
+
if (candidates.length === 0) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const probe = options.probe ?? defaultProbe;
|
|
14
|
+
const results = await Promise.all(candidates.map(async (route) => ({
|
|
15
|
+
route,
|
|
16
|
+
ok: await probeWithTimeout(route, probe, options.timeoutMs ?? 1_000),
|
|
17
|
+
})));
|
|
18
|
+
return results.find((result) => result.ok)?.route ?? null;
|
|
19
|
+
}
|
|
20
|
+
async function probeWithTimeout(route, probe, timeoutMs) {
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
23
|
+
try {
|
|
24
|
+
return await Promise.resolve(probe(route, controller.signal));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
clearTimeout(timeout);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function defaultProbe(route, signal) {
|
|
34
|
+
if (!route.targetUrl.startsWith('http://') && !route.targetUrl.startsWith('https://')) {
|
|
35
|
+
return route.health === 'healthy';
|
|
36
|
+
}
|
|
37
|
+
const response = await fetch(new URL('/.well-known/solid', route.targetUrl), {
|
|
38
|
+
method: 'HEAD',
|
|
39
|
+
signal,
|
|
40
|
+
});
|
|
41
|
+
return response.ok || response.status === 401 || response.status === 403;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=ManagedRouteSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ManagedRouteSelector.js","sourceRoot":"","sources":["../../../src/edge/reachability/ManagedRouteSelector.ts"],"names":[],"mappings":";;AAQA,8CAqBC;AArBM,KAAK,UAAU,iBAAiB,CACrC,QAAkB,EAClB,OAAiC;IAEjC,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM;SAC/B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,aAAa,CAAC;SACjD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC;SACxE,KAAK,EAAE;SACP,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5F,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC;IAC5C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACjE,KAAK;QACL,EAAE,EAAE,MAAM,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;KACrE,CAAC,CAAC,CAAC,CAAC;IAEL,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;AAC5D,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,KAAkB,EAClB,KAA8E,EAC9E,SAAiB;IAEjB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAChE,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,KAAkB,EAAE,MAAmB;IACjE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACtF,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC;IACpC,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE;QAC3E,MAAM,EAAE,MAAM;QACd,MAAM;KACP,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC;AAC3E,CAAC","sourcesContent":["import type { AccessRoute, RouteSet } from './types';\n\nexport interface ChooseAccessRouteOptions {\n managedClient: boolean;\n timeoutMs?: number;\n probe?: (route: AccessRoute, signal: AbortSignal) => Promise<boolean> | boolean;\n}\n\nexport async function chooseAccessRoute(\n routeSet: RouteSet,\n options: ChooseAccessRouteOptions,\n): Promise<AccessRoute | null> {\n const candidates = routeSet.routes\n .filter((route) => route.health !== 'unreachable')\n .filter((route) => options.managedClient || !route.requiresManagedClient)\n .slice()\n .sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id));\n\n if (candidates.length === 0) {\n return null;\n }\n\n const probe = options.probe ?? defaultProbe;\n const results = await Promise.all(candidates.map(async (route) => ({\n route,\n ok: await probeWithTimeout(route, probe, options.timeoutMs ?? 1_000),\n })));\n\n return results.find((result) => result.ok)?.route ?? null;\n}\n\nasync function probeWithTimeout(\n route: AccessRoute,\n probe: (route: AccessRoute, signal: AbortSignal) => Promise<boolean> | boolean,\n timeoutMs: number,\n): Promise<boolean> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await Promise.resolve(probe(route, controller.signal));\n } catch {\n return false;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nasync function defaultProbe(route: AccessRoute, signal: AbortSignal): Promise<boolean> {\n if (!route.targetUrl.startsWith('http://') && !route.targetUrl.startsWith('https://')) {\n return route.health === 'healthy';\n }\n const response = await fetch(new URL('/.well-known/solid', route.targetUrl), {\n method: 'HEAD',\n signal,\n });\n return response.ok || response.status === 401 || response.status === 403;\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://linkedsoftwaredependencies.org/bundles/npm/@undefineds.co/xpod/^0.0.0/components/context.jsonld"
|
|
4
|
+
],
|
|
5
|
+
"@id": "npmd:@undefineds.co/xpod",
|
|
6
|
+
"components": [
|
|
7
|
+
{
|
|
8
|
+
"@id": "undefineds:dist/edge/reachability/ManagedRouteSelector.jsonld#ChooseAccessRouteOptions",
|
|
9
|
+
"@type": "AbstractClass",
|
|
10
|
+
"requireElement": "ChooseAccessRouteOptions",
|
|
11
|
+
"parameters": [],
|
|
12
|
+
"memberFields": [
|
|
13
|
+
{
|
|
14
|
+
"@id": "undefineds:dist/edge/reachability/ManagedRouteSelector.jsonld#ChooseAccessRouteOptions__member_managedClient",
|
|
15
|
+
"memberFieldName": "managedClient"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"@id": "undefineds:dist/edge/reachability/ManagedRouteSelector.jsonld#ChooseAccessRouteOptions__member_timeoutMs",
|
|
19
|
+
"memberFieldName": "timeoutMs"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"@id": "undefineds:dist/edge/reachability/ManagedRouteSelector.jsonld#ChooseAccessRouteOptions__member_probe",
|
|
23
|
+
"memberFieldName": "probe"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"constructorArguments": []
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AccessRoute } from './types';
|
|
2
|
+
export declare const XPOD_P2P_HTTP_PROTOCOL: "xpod-p2p-http/1";
|
|
3
|
+
export type P2PHttpProtocol = typeof XPOD_P2P_HTTP_PROTOCOL;
|
|
4
|
+
export type P2PHttpHeaderList = [string, string][];
|
|
5
|
+
export interface P2PHttpRequestFrame {
|
|
6
|
+
protocol: P2PHttpProtocol;
|
|
7
|
+
requestId?: string;
|
|
8
|
+
method: string;
|
|
9
|
+
url: string;
|
|
10
|
+
headers?: P2PHttpHeaderList;
|
|
11
|
+
bodyBase64?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface P2PHttpResponseFrame {
|
|
14
|
+
protocol: P2PHttpProtocol;
|
|
15
|
+
requestId?: string;
|
|
16
|
+
status: number;
|
|
17
|
+
statusText?: string;
|
|
18
|
+
headers?: P2PHttpHeaderList;
|
|
19
|
+
bodyBase64?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface P2PDataPlaneTransport {
|
|
22
|
+
request(frame: P2PHttpRequestFrame): Promise<P2PHttpResponseFrame>;
|
|
23
|
+
}
|
|
24
|
+
export interface P2PDataPlaneFetchOptions {
|
|
25
|
+
route: AccessRoute;
|
|
26
|
+
transport: P2PDataPlaneTransport;
|
|
27
|
+
}
|
|
28
|
+
export interface P2PDataPlaneHandlerOptions {
|
|
29
|
+
targetBaseUrl: string | URL;
|
|
30
|
+
fetchImpl?: typeof fetch;
|
|
31
|
+
}
|
|
32
|
+
export interface P2PDataPlaneHandler {
|
|
33
|
+
handleRequest(frame: P2PHttpRequestFrame): Promise<P2PHttpResponseFrame>;
|
|
34
|
+
}
|
|
35
|
+
export type P2PDataPlaneFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
36
|
+
export declare function createP2PDataPlaneFetch(options: P2PDataPlaneFetchOptions): P2PDataPlaneFetch;
|
|
37
|
+
export declare function createP2PDataPlaneHandler(options: P2PDataPlaneHandlerOptions): P2PDataPlaneHandler;
|