@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.
Files changed (117) hide show
  1. package/config/cli.json +72 -0
  2. package/config/components-ignore.json +21 -0
  3. package/config/extensions.local.initializer.json +77 -8
  4. package/config/resolver.json +72 -4
  5. package/dist/api/ApiServer.d.ts +12 -0
  6. package/dist/api/ApiServer.js +14 -3
  7. package/dist/api/ApiServer.js.map +1 -1
  8. package/dist/api/auth/NodeTokenAuthenticator.d.ts +0 -8
  9. package/dist/api/auth/NodeTokenAuthenticator.js +3 -46
  10. package/dist/api/auth/NodeTokenAuthenticator.js.map +1 -1
  11. package/dist/api/container/local.js +5 -36
  12. package/dist/api/container/local.js.map +1 -1
  13. package/dist/api/container/routes.js +6 -0
  14. package/dist/api/container/routes.js.map +1 -1
  15. package/dist/api/handlers/EdgeNodeSignalHandler.js +25 -3
  16. package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
  17. package/dist/api/handlers/ReachabilityHandler.d.ts +13 -0
  18. package/dist/api/handlers/ReachabilityHandler.js +388 -0
  19. package/dist/api/handlers/ReachabilityHandler.js.map +1 -0
  20. package/dist/api/runs/InngestRunExecutionBackend.d.ts +2 -2
  21. package/dist/api/tasks/InngestTaskScheduler.d.ts +4 -4
  22. package/dist/components/components.jsonld +14 -2
  23. package/dist/components/context.jsonld +439 -3
  24. package/dist/edge/EdgeNodeAgent.d.ts +43 -0
  25. package/dist/edge/EdgeNodeAgent.js +208 -1
  26. package/dist/edge/EdgeNodeAgent.js.map +1 -1
  27. package/dist/edge/EdgeNodeAgent.jsonld +166 -0
  28. package/dist/edge/EdgeNodeAgentInitializer.d.ts +20 -2
  29. package/dist/edge/EdgeNodeAgentInitializer.js +53 -2
  30. package/dist/edge/EdgeNodeAgentInitializer.js.map +1 -1
  31. package/dist/edge/EdgeNodeAgentInitializer.jsonld +409 -0
  32. package/dist/edge/reachability/CanonicalFetch.d.ts +7 -0
  33. package/dist/edge/reachability/CanonicalFetch.js +49 -0
  34. package/dist/edge/reachability/CanonicalFetch.js.map +1 -0
  35. package/dist/edge/reachability/CanonicalFetch.jsonld +25 -0
  36. package/dist/edge/reachability/ManagedClientFetch.d.ts +26 -0
  37. package/dist/edge/reachability/ManagedClientFetch.js +155 -0
  38. package/dist/edge/reachability/ManagedClientFetch.js.map +1 -0
  39. package/dist/edge/reachability/ManagedClientFetch.jsonld +83 -0
  40. package/dist/edge/reachability/ManagedClientP2PSmoke.d.ts +16 -0
  41. package/dist/edge/reachability/ManagedClientP2PSmoke.js +31 -0
  42. package/dist/edge/reachability/ManagedClientP2PSmoke.js.map +1 -0
  43. package/dist/edge/reachability/ManagedClientP2PSmoke.jsonld +65 -0
  44. package/dist/edge/reachability/ManagedRouteSelector.d.ts +7 -0
  45. package/dist/edge/reachability/ManagedRouteSelector.js +43 -0
  46. package/dist/edge/reachability/ManagedRouteSelector.js.map +1 -0
  47. package/dist/edge/reachability/ManagedRouteSelector.jsonld +29 -0
  48. package/dist/edge/reachability/P2PDataPlane.d.ts +37 -0
  49. package/dist/edge/reachability/P2PDataPlane.js +160 -0
  50. package/dist/edge/reachability/P2PDataPlane.js.map +1 -0
  51. package/dist/edge/reachability/P2PDataPlane.jsonld +134 -0
  52. package/dist/edge/reachability/P2PRealnetAcceptance.d.ts +74 -0
  53. package/dist/edge/reachability/P2PRealnetAcceptance.js +240 -0
  54. package/dist/edge/reachability/P2PRealnetAcceptance.js.map +1 -0
  55. package/dist/edge/reachability/P2PRealnetAcceptance.jsonld +283 -0
  56. package/dist/edge/reachability/P2PSignalingClient.d.ts +24 -0
  57. package/dist/edge/reachability/P2PSignalingClient.js +138 -0
  58. package/dist/edge/reachability/P2PSignalingClient.js.map +1 -0
  59. package/dist/edge/reachability/P2PSignalingClient.jsonld +79 -0
  60. package/dist/edge/reachability/ReachabilitySessionService.d.ts +55 -0
  61. package/dist/edge/reachability/ReachabilitySessionService.js +439 -0
  62. package/dist/edge/reachability/ReachabilitySessionService.js.map +1 -0
  63. package/dist/edge/reachability/ReachabilitySessionService.jsonld +196 -0
  64. package/dist/edge/reachability/RouteSetBuilder.d.ts +2 -0
  65. package/dist/edge/reachability/RouteSetBuilder.js +205 -0
  66. package/dist/edge/reachability/RouteSetBuilder.js.map +1 -0
  67. package/dist/edge/reachability/TcpP2PDataPlaneTransport.d.ts +47 -0
  68. package/dist/edge/reachability/TcpP2PDataPlaneTransport.js +281 -0
  69. package/dist/edge/reachability/TcpP2PDataPlaneTransport.js.map +1 -0
  70. package/dist/edge/reachability/TcpP2PDataPlaneTransport.jsonld +183 -0
  71. package/dist/edge/reachability/TcpP2PSignalingSession.d.ts +149 -0
  72. package/dist/edge/reachability/TcpP2PSignalingSession.js +699 -0
  73. package/dist/edge/reachability/TcpP2PSignalingSession.js.map +1 -0
  74. package/dist/edge/reachability/TcpP2PSignalingSession.jsonld +474 -0
  75. package/dist/edge/reachability/index.d.ts +12 -0
  76. package/dist/edge/reachability/index.js +29 -0
  77. package/dist/edge/reachability/index.js.map +1 -0
  78. package/dist/edge/reachability/types.d.ts +114 -0
  79. package/dist/edge/reachability/types.js +3 -0
  80. package/dist/edge/reachability/types.js.map +1 -0
  81. package/dist/edge/reachability/types.jsonld +457 -0
  82. package/dist/http/EdgeNodeProxyHttpHandler.d.ts +2 -0
  83. package/dist/http/EdgeNodeProxyHttpHandler.js +19 -1
  84. package/dist/http/EdgeNodeProxyHttpHandler.js.map +1 -1
  85. package/dist/http/EdgeNodeProxyHttpHandler.jsonld +8 -0
  86. package/dist/identity/drizzle/EdgeNodeRepository.js +1 -1
  87. package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
  88. package/dist/index.d.ts +4 -1
  89. package/dist/index.js +5 -2
  90. package/dist/index.js.map +1 -1
  91. package/dist/runtime/bootstrap.js +8 -0
  92. package/dist/runtime/bootstrap.js.map +1 -1
  93. package/dist/service/EdgeNodeSignalClient.js +5 -1
  94. package/dist/service/EdgeNodeSignalClient.js.map +1 -1
  95. package/dist/storage/rdf/PostgresRdfEngine.d.ts +1 -0
  96. package/dist/storage/rdf/PostgresRdfEngine.js +53 -37
  97. package/dist/storage/rdf/PostgresRdfEngine.js.map +1 -1
  98. package/dist/storage/rdf/PostgresRdfEngine.jsonld +4 -0
  99. package/dist/test-utils/index.d.ts +2 -0
  100. package/dist/test-utils/index.js +3 -1
  101. package/dist/test-utils/index.js.map +1 -1
  102. package/dist/test-utils/local-managed-client-p2p-e2e-smoke.d.ts +63 -0
  103. package/dist/test-utils/local-managed-client-p2p-e2e-smoke.js +478 -0
  104. package/dist/test-utils/local-managed-client-p2p-e2e-smoke.js.map +1 -0
  105. package/package.json +11 -4
  106. package/static/app/assets/_commonjsHelpers-B-UnjaXt.js +1 -0
  107. package/static/app/assets/index-AaQ1qxhy.js +171 -0
  108. package/static/app/assets/inrupt-smoke.js +131 -0
  109. package/static/app/assets/main.css +1 -0
  110. package/static/app/assets/main.js +6 -6
  111. package/static/app/index.html +2 -1
  112. package/static/app/inrupt-smoke.html +14 -0
  113. package/static/app/reachability.html +221 -0
  114. package/static/app/reachability.svg +7 -0
  115. package/static/app/reachability.webmanifest +18 -0
  116. package/static/app/signal-pod.html +293 -0
  117. package/static/app/assets/index.css +0 -1
@@ -0,0 +1,388 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerReachabilityRoutes = registerReachabilityRoutes;
4
+ const AuthContext_1 = require("../auth/AuthContext");
5
+ const RouteSetBuilder_1 = require("../../edge/reachability/RouteSetBuilder");
6
+ const ReachabilitySessionService_1 = require("../../edge/reachability/ReachabilitySessionService");
7
+ function registerReachabilityRoutes(server, options) {
8
+ const service = new ReachabilitySessionService_1.ReachabilitySessionService({
9
+ repository: options.repository,
10
+ baseStorageDomain: options.baseStorageDomain,
11
+ apiBaseUrl: options.apiBaseUrl ?? process.env.XPOD_CLOUD_API_ENDPOINT ?? process.env.CSS_BASE_URL ?? 'http://localhost/',
12
+ now: options.now,
13
+ randomId: options.randomId,
14
+ maxActiveP2PSessionsPerNode: options.maxActiveP2PSessionsPerNode
15
+ ?? parsePositiveInteger(process.env.XPOD_P2P_MAX_ACTIVE_SESSIONS_PER_NODE),
16
+ maxP2PCandidatesPerUpdate: options.maxP2PCandidatesPerUpdate
17
+ ?? parsePositiveInteger(process.env.XPOD_P2P_MAX_CANDIDATES_PER_UPDATE),
18
+ maxP2PCandidatesPerSession: options.maxP2PCandidatesPerSession
19
+ ?? parsePositiveInteger(process.env.XPOD_P2P_MAX_CANDIDATES_PER_SESSION),
20
+ });
21
+ server.get('/v1/signal/nodes/:nodeId/routes', async (request, response, params) => {
22
+ const access = resolveAccess(request, params.nodeId);
23
+ if (!access.allowed) {
24
+ sendJson(response, access.status, { error: access.error });
25
+ return;
26
+ }
27
+ try {
28
+ const source = await loadRouteSource(options.repository, params.nodeId, options.baseStorageDomain);
29
+ const routeSet = (0, RouteSetBuilder_1.buildRouteSet)(source, {
30
+ audience: access.audience,
31
+ baseStorageDomain: options.baseStorageDomain,
32
+ now: options.now?.() ?? new Date(),
33
+ });
34
+ sendJson(response, 200, routeSet);
35
+ }
36
+ catch (error) {
37
+ if (error instanceof ReachabilitySessionService_1.NodeRouteSourceNotFoundError) {
38
+ sendJson(response, 404, { error: 'Node not found' });
39
+ return;
40
+ }
41
+ sendJson(response, 500, { error: 'Failed to load routes' });
42
+ }
43
+ }, { optionalAuth: true });
44
+ server.post('/v1/signal/nodes/:nodeId/sessions', async (request, response, params) => {
45
+ const access = resolveSessionAccess(request, params.nodeId);
46
+ if (!access.allowed) {
47
+ sendJson(response, access.status, { error: access.error });
48
+ return;
49
+ }
50
+ const body = await readJsonBody(request);
51
+ if (!isRecord(body)) {
52
+ sendJson(response, 400, { error: 'kind must be p2p or relay' });
53
+ return;
54
+ }
55
+ if (body.kind === 'p2p') {
56
+ if (typeof body.clientId !== 'string' || body.clientId.trim().length === 0) {
57
+ sendJson(response, 400, { error: 'clientId is required' });
58
+ return;
59
+ }
60
+ try {
61
+ const session = await service.createP2PSession(params.nodeId, {
62
+ kind: 'p2p',
63
+ clientId: body.clientId.trim(),
64
+ ...(access.kind === 'solid' ? { owner: { type: 'solid', webId: access.webId } } : {}),
65
+ capabilities: Array.isArray(body.capabilities) ? body.capabilities.filter((entry) => typeof entry === 'string') : [],
66
+ candidates: enrichP2PCandidatesWithObservedAddress(Array.isArray(body.candidates) ? body.candidates : [], resolveObservedAddress(request)),
67
+ });
68
+ sendJson(response, 201, session);
69
+ }
70
+ catch (error) {
71
+ if (error instanceof ReachabilitySessionService_1.NodeRouteSourceNotFoundError) {
72
+ sendJson(response, 404, { error: 'Node not found' });
73
+ return;
74
+ }
75
+ if (error instanceof ReachabilitySessionService_1.P2PActiveSessionLimitExceededError) {
76
+ sendJson(response, 429, { error: 'P2P active session limit exceeded' });
77
+ return;
78
+ }
79
+ if (error instanceof ReachabilitySessionService_1.P2PCandidateUpdateLimitExceededError) {
80
+ sendJson(response, 429, { error: 'P2P candidate update limit exceeded' });
81
+ return;
82
+ }
83
+ if (error instanceof ReachabilitySessionService_1.P2PCandidateSessionLimitExceededError) {
84
+ sendJson(response, 429, { error: 'P2P candidate session limit exceeded' });
85
+ return;
86
+ }
87
+ sendJson(response, 500, { error: 'Failed to create p2p session' });
88
+ }
89
+ return;
90
+ }
91
+ if (body.kind === 'relay') {
92
+ if (typeof body.reason !== 'string' || body.reason.trim().length === 0) {
93
+ sendJson(response, 400, { error: 'reason is required for relay sessions' });
94
+ return;
95
+ }
96
+ try {
97
+ const session = await service.createRelaySession(params.nodeId, {
98
+ kind: 'relay',
99
+ reason: body.reason,
100
+ ttlSeconds: typeof body.ttlSeconds === 'number' ? body.ttlSeconds : undefined,
101
+ bandwidthLimitBytes: typeof body.bandwidthLimitBytes === 'number' ? body.bandwidthLimitBytes : undefined,
102
+ bandwidthLimitBps: typeof body.bandwidthLimitBps === 'number' ? body.bandwidthLimitBps : undefined,
103
+ });
104
+ sendJson(response, 201, session);
105
+ }
106
+ catch (error) {
107
+ if (error instanceof ReachabilitySessionService_1.InvalidRelaySessionRequestError) {
108
+ sendJson(response, 400, { error: error.message });
109
+ return;
110
+ }
111
+ if (error instanceof ReachabilitySessionService_1.NodeRouteSourceNotFoundError) {
112
+ sendJson(response, 404, { error: 'Node not found' });
113
+ return;
114
+ }
115
+ sendJson(response, 500, { error: 'Failed to create relay session' });
116
+ }
117
+ return;
118
+ }
119
+ sendJson(response, 400, { error: 'kind must be p2p or relay' });
120
+ });
121
+ server.get('/v1/signal/nodes/:nodeId/sessions', async (request, response, params) => {
122
+ const access = resolveSessionAccess(request, params.nodeId);
123
+ if (!access.allowed) {
124
+ sendJson(response, access.status, { error: access.error });
125
+ return;
126
+ }
127
+ try {
128
+ const sessions = await service.listP2PSessions(params.nodeId);
129
+ sendJson(response, 200, filterP2PSessionListForAccess(sessions, access));
130
+ }
131
+ catch (error) {
132
+ if (error instanceof ReachabilitySessionService_1.NodeRouteSourceNotFoundError) {
133
+ sendJson(response, 404, { error: 'Node not found' });
134
+ return;
135
+ }
136
+ sendJson(response, 500, { error: 'Failed to list p2p sessions' });
137
+ }
138
+ });
139
+ server.get('/v1/signal/nodes/:nodeId/sessions/:sessionId', async (request, response, params) => {
140
+ const access = resolveSessionAccess(request, params.nodeId);
141
+ if (!access.allowed) {
142
+ sendJson(response, access.status, { error: access.error });
143
+ return;
144
+ }
145
+ try {
146
+ const session = await service.getP2PSession(params.nodeId, params.sessionId);
147
+ if (!canAccessP2PSession(access, session)) {
148
+ sendJson(response, 403, { error: 'Solid user cannot access another client signaling session' });
149
+ return;
150
+ }
151
+ sendJson(response, 200, session);
152
+ }
153
+ catch (error) {
154
+ sendP2PSessionError(response, error);
155
+ }
156
+ });
157
+ server.post('/v1/signal/nodes/:nodeId/sessions/:sessionId/candidates', async (request, response, params) => {
158
+ const access = resolveSessionAccess(request, params.nodeId);
159
+ if (!access.allowed) {
160
+ sendJson(response, access.status, { error: access.error });
161
+ return;
162
+ }
163
+ const body = await readJsonBody(request);
164
+ if (!isRecord(body) || !Array.isArray(body.candidates)) {
165
+ sendJson(response, 400, { error: 'candidates array is required' });
166
+ return;
167
+ }
168
+ try {
169
+ const currentSession = await service.getP2PSession(params.nodeId, params.sessionId);
170
+ if (!canAccessP2PSession(access, currentSession)) {
171
+ sendJson(response, 403, { error: 'Solid user cannot access another client signaling session' });
172
+ return;
173
+ }
174
+ const source = resolveCandidateSource(access, params.nodeId, currentSession, body);
175
+ const session = await service.addP2PCandidates(params.nodeId, params.sessionId, {
176
+ role: source.role,
177
+ sourceId: source.sourceId,
178
+ candidates: enrichP2PCandidatesWithObservedAddress(body.candidates, resolveObservedAddress(request)),
179
+ });
180
+ sendJson(response, 200, session);
181
+ }
182
+ catch (error) {
183
+ sendP2PSessionError(response, error);
184
+ }
185
+ });
186
+ }
187
+ async function loadRouteSource(repository, nodeId, baseStorageDomain) {
188
+ const [metadataRow, connectivity] = await Promise.all([
189
+ repository.getNodeMetadata(nodeId),
190
+ repository.getNodeConnectivityInfo(nodeId),
191
+ ]);
192
+ if (!metadataRow && !connectivity) {
193
+ throw new ReachabilitySessionService_1.NodeRouteSourceNotFoundError(`Node ${nodeId} not found`);
194
+ }
195
+ const metadata = metadataRow?.metadata ?? {};
196
+ return {
197
+ nodeId,
198
+ canonicalUrl: getString(metadata.canonicalUrl),
199
+ publicUrl: connectivity?.publicUrl,
200
+ subdomain: connectivity?.subdomain,
201
+ baseStorageDomain,
202
+ ipv4: connectivity?.ipv4,
203
+ publicPort: connectivity?.publicPort,
204
+ connectivityStatus: connectivity?.connectivityStatus,
205
+ metadata,
206
+ };
207
+ }
208
+ function resolveAccess(request, nodeId) {
209
+ const auth = request.auth;
210
+ if (!auth) {
211
+ return { allowed: true, audience: 'public' };
212
+ }
213
+ if ((0, AuthContext_1.isNodeAuth)(auth)) {
214
+ if (auth.nodeId !== nodeId) {
215
+ return { allowed: false, status: 403, error: 'Node token cannot access another node' };
216
+ }
217
+ return { allowed: true, audience: 'managed' };
218
+ }
219
+ if ((0, AuthContext_1.isServiceAuth)(auth)) {
220
+ return { allowed: true, audience: 'managed' };
221
+ }
222
+ return { allowed: true, audience: 'public' };
223
+ }
224
+ function resolveSessionAccess(request, nodeId) {
225
+ const auth = request.auth;
226
+ if (!auth) {
227
+ return { allowed: false, status: 401, error: 'Authentication required' };
228
+ }
229
+ if ((0, AuthContext_1.isNodeAuth)(auth)) {
230
+ if (auth.nodeId !== nodeId) {
231
+ return { allowed: false, status: 403, error: 'Node token cannot access another node' };
232
+ }
233
+ return { allowed: true, kind: 'node', nodeId: auth.nodeId };
234
+ }
235
+ if ((0, AuthContext_1.isServiceAuth)(auth)) {
236
+ return { allowed: true, kind: 'service' };
237
+ }
238
+ if ((0, AuthContext_1.isSolidAuth)(auth)) {
239
+ return { allowed: true, kind: 'solid', webId: auth.webId };
240
+ }
241
+ return { allowed: false, status: 403, error: 'Unsupported reachability session credentials' };
242
+ }
243
+ function filterP2PSessionListForAccess(sessions, access) {
244
+ if (access.kind !== 'solid') {
245
+ return sessions;
246
+ }
247
+ return {
248
+ kind: sessions.kind,
249
+ sessions: sessions.sessions.filter((session) => isOwnedBySolidUser(session, access.webId)),
250
+ };
251
+ }
252
+ function canAccessP2PSession(access, session) {
253
+ return access.kind !== 'solid' || isOwnedBySolidUser(session, access.webId);
254
+ }
255
+ function isOwnedBySolidUser(session, webId) {
256
+ return session.owner?.type === 'solid' && session.owner.webId === webId;
257
+ }
258
+ function resolveCandidateSource(access, nodeId, session, body) {
259
+ if (access.kind === 'node') {
260
+ return { role: 'node', sourceId: nodeId };
261
+ }
262
+ if (access.kind === 'solid') {
263
+ return { role: 'client', sourceId: session.clientId };
264
+ }
265
+ const role = body.role === 'node' ? 'node' : 'client';
266
+ const sourceId = getString(body.sourceId)
267
+ ?? getString(body.clientId)
268
+ ?? (role === 'node' ? nodeId : 'service-client');
269
+ return { role, sourceId };
270
+ }
271
+ function enrichP2PCandidatesWithObservedAddress(candidates, observedAddress) {
272
+ if (!observedAddress) {
273
+ return candidates;
274
+ }
275
+ return candidates.map((candidate) => {
276
+ if (!isRecord(candidate) || !isPortOnlyCandidate(candidate)) {
277
+ return candidate;
278
+ }
279
+ return {
280
+ ...candidate,
281
+ address: observedAddress,
282
+ };
283
+ });
284
+ }
285
+ function isPortOnlyCandidate(candidate) {
286
+ return normalizeCandidatePort(candidate.port) !== undefined
287
+ && getString(candidate.host) === undefined
288
+ && getString(candidate.address) === undefined
289
+ && getString(candidate.url) === undefined;
290
+ }
291
+ function normalizeCandidatePort(value) {
292
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
293
+ return undefined;
294
+ }
295
+ const port = Math.floor(value);
296
+ return port > 0 && port <= 65535 ? port : undefined;
297
+ }
298
+ function resolveObservedAddress(request) {
299
+ return firstForwardedAddress(request.headers['x-forwarded-for'])
300
+ ?? firstHeaderAddress(request.headers['x-real-ip'])
301
+ ?? normalizeObservedAddress(request.socket?.remoteAddress);
302
+ }
303
+ function firstForwardedAddress(value) {
304
+ const first = Array.isArray(value) ? value[0] : value;
305
+ return firstHeaderAddress(first?.split(',')[0]);
306
+ }
307
+ function firstHeaderAddress(value) {
308
+ const first = Array.isArray(value) ? value[0] : value;
309
+ return normalizeObservedAddress(first);
310
+ }
311
+ function normalizeObservedAddress(value) {
312
+ const trimmed = value?.trim().replace(/^"|"$/gu, '');
313
+ if (!trimmed) {
314
+ return undefined;
315
+ }
316
+ if (trimmed.startsWith('::ffff:')) {
317
+ return trimmed.slice('::ffff:'.length);
318
+ }
319
+ if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
320
+ return trimmed.slice(1, -1);
321
+ }
322
+ return trimmed;
323
+ }
324
+ async function readJsonBody(request) {
325
+ return new Promise((resolve, reject) => {
326
+ let data = '';
327
+ request.setEncoding('utf8');
328
+ request.on('data', (chunk) => {
329
+ data += chunk;
330
+ });
331
+ request.on('end', () => {
332
+ if (!data) {
333
+ resolve(undefined);
334
+ return;
335
+ }
336
+ try {
337
+ resolve(JSON.parse(data));
338
+ }
339
+ catch {
340
+ resolve(undefined);
341
+ }
342
+ });
343
+ request.on('error', reject);
344
+ });
345
+ }
346
+ function sendJson(response, status, data) {
347
+ response.statusCode = status;
348
+ response.setHeader('Content-Type', 'application/json');
349
+ response.end(JSON.stringify(data));
350
+ }
351
+ function sendP2PSessionError(response, error) {
352
+ if (error instanceof ReachabilitySessionService_1.P2PSessionExpiredError) {
353
+ sendJson(response, 410, { error: 'P2P session expired' });
354
+ return;
355
+ }
356
+ if (error instanceof ReachabilitySessionService_1.P2PSessionNotFoundError) {
357
+ sendJson(response, 404, { error: 'P2P session not found' });
358
+ return;
359
+ }
360
+ if (error instanceof ReachabilitySessionService_1.P2PCandidateUpdateLimitExceededError) {
361
+ sendJson(response, 429, { error: 'P2P candidate update limit exceeded' });
362
+ return;
363
+ }
364
+ if (error instanceof ReachabilitySessionService_1.P2PCandidateSessionLimitExceededError) {
365
+ sendJson(response, 429, { error: 'P2P candidate session limit exceeded' });
366
+ return;
367
+ }
368
+ if (error instanceof ReachabilitySessionService_1.NodeRouteSourceNotFoundError) {
369
+ sendJson(response, 404, { error: 'Node not found' });
370
+ return;
371
+ }
372
+ sendJson(response, 500, { error: 'Failed to update p2p session' });
373
+ }
374
+ function getString(value) {
375
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
376
+ }
377
+ function parsePositiveInteger(value) {
378
+ const trimmed = getString(value);
379
+ if (!trimmed) {
380
+ return undefined;
381
+ }
382
+ const parsed = Number.parseInt(trimmed, 10);
383
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
384
+ }
385
+ function isRecord(value) {
386
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
387
+ }
388
+ //# sourceMappingURL=ReachabilityHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReachabilityHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/ReachabilityHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport type { ApiServer } from '../ApiServer';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport { isNodeAuth, isServiceAuth, isSolidAuth } from '../auth/AuthContext';\nimport { buildRouteSet } from '../../edge/reachability/RouteSetBuilder';\nimport {\n InvalidRelaySessionRequestError,\n NodeRouteSourceNotFoundError,\n P2PActiveSessionLimitExceededError,\n P2PCandidateSessionLimitExceededError,\n P2PCandidateUpdateLimitExceededError,\n P2PSessionExpiredError,\n P2PSessionNotFoundError,\n ReachabilitySessionService,\n} from '../../edge/reachability/ReachabilitySessionService';\nimport type { BuildRouteSetSource, P2PCandidateRole, P2PSession, RouteAudience } from '../../edge/reachability/types';\n\nexport interface ReachabilityHandlerOptions {\n repository: EdgeNodeRepository;\n baseStorageDomain?: string;\n apiBaseUrl?: string | (() => string);\n now?: () => Date;\n randomId?: () => string;\n maxActiveP2PSessionsPerNode?: number;\n maxP2PCandidatesPerUpdate?: number;\n maxP2PCandidatesPerSession?: number;\n}\n\nexport function registerReachabilityRoutes(server: ApiServer, options: ReachabilityHandlerOptions): void {\n const service = new ReachabilitySessionService({\n repository: options.repository,\n baseStorageDomain: options.baseStorageDomain,\n apiBaseUrl: options.apiBaseUrl ?? process.env.XPOD_CLOUD_API_ENDPOINT ?? process.env.CSS_BASE_URL ?? 'http://localhost/',\n now: options.now,\n randomId: options.randomId,\n maxActiveP2PSessionsPerNode: options.maxActiveP2PSessionsPerNode\n ?? parsePositiveInteger(process.env.XPOD_P2P_MAX_ACTIVE_SESSIONS_PER_NODE),\n maxP2PCandidatesPerUpdate: options.maxP2PCandidatesPerUpdate\n ?? parsePositiveInteger(process.env.XPOD_P2P_MAX_CANDIDATES_PER_UPDATE),\n maxP2PCandidatesPerSession: options.maxP2PCandidatesPerSession\n ?? parsePositiveInteger(process.env.XPOD_P2P_MAX_CANDIDATES_PER_SESSION),\n });\n\n server.get('/v1/signal/nodes/:nodeId/routes', async (request, response, params) => {\n const access = resolveAccess(request, params.nodeId);\n if (!access.allowed) {\n sendJson(response, access.status, { error: access.error });\n return;\n }\n\n try {\n const source = await loadRouteSource(options.repository, params.nodeId, options.baseStorageDomain);\n const routeSet = buildRouteSet(source, {\n audience: access.audience,\n baseStorageDomain: options.baseStorageDomain,\n now: options.now?.() ?? new Date(),\n });\n sendJson(response, 200, routeSet);\n } catch (error) {\n if (error instanceof NodeRouteSourceNotFoundError) {\n sendJson(response, 404, { error: 'Node not found' });\n return;\n }\n sendJson(response, 500, { error: 'Failed to load routes' });\n }\n }, { optionalAuth: true });\n\n server.post('/v1/signal/nodes/:nodeId/sessions', async (request, response, params) => {\n const access = resolveSessionAccess(request, params.nodeId);\n if (!access.allowed) {\n sendJson(response, access.status, { error: access.error });\n return;\n }\n\n const body = await readJsonBody(request);\n if (!isRecord(body)) {\n sendJson(response, 400, { error: 'kind must be p2p or relay' });\n return;\n }\n\n if (body.kind === 'p2p') {\n if (typeof body.clientId !== 'string' || body.clientId.trim().length === 0) {\n sendJson(response, 400, { error: 'clientId is required' });\n return;\n }\n try {\n const session = await service.createP2PSession(params.nodeId, {\n kind: 'p2p',\n clientId: body.clientId.trim(),\n ...(access.kind === 'solid' ? { owner: { type: 'solid' as const, webId: access.webId } } : {}),\n capabilities: Array.isArray(body.capabilities) ? body.capabilities.filter((entry): entry is string => typeof entry === 'string') : [],\n candidates: enrichP2PCandidatesWithObservedAddress(\n Array.isArray(body.candidates) ? body.candidates : [],\n resolveObservedAddress(request),\n ),\n });\n sendJson(response, 201, session);\n } catch (error) {\n if (error instanceof NodeRouteSourceNotFoundError) {\n sendJson(response, 404, { error: 'Node not found' });\n return;\n }\n if (error instanceof P2PActiveSessionLimitExceededError) {\n sendJson(response, 429, { error: 'P2P active session limit exceeded' });\n return;\n }\n if (error instanceof P2PCandidateUpdateLimitExceededError) {\n sendJson(response, 429, { error: 'P2P candidate update limit exceeded' });\n return;\n }\n if (error instanceof P2PCandidateSessionLimitExceededError) {\n sendJson(response, 429, { error: 'P2P candidate session limit exceeded' });\n return;\n }\n sendJson(response, 500, { error: 'Failed to create p2p session' });\n }\n return;\n }\n\n if (body.kind === 'relay') {\n if (typeof body.reason !== 'string' || body.reason.trim().length === 0) {\n sendJson(response, 400, { error: 'reason is required for relay sessions' });\n return;\n }\n try {\n const session = await service.createRelaySession(params.nodeId, {\n kind: 'relay',\n reason: body.reason,\n ttlSeconds: typeof body.ttlSeconds === 'number' ? body.ttlSeconds : undefined,\n bandwidthLimitBytes: typeof body.bandwidthLimitBytes === 'number' ? body.bandwidthLimitBytes : undefined,\n bandwidthLimitBps: typeof body.bandwidthLimitBps === 'number' ? body.bandwidthLimitBps : undefined,\n });\n sendJson(response, 201, session);\n } catch (error) {\n if (error instanceof InvalidRelaySessionRequestError) {\n sendJson(response, 400, { error: error.message });\n return;\n }\n if (error instanceof NodeRouteSourceNotFoundError) {\n sendJson(response, 404, { error: 'Node not found' });\n return;\n }\n sendJson(response, 500, { error: 'Failed to create relay session' });\n }\n return;\n }\n\n sendJson(response, 400, { error: 'kind must be p2p or relay' });\n });\n\n server.get('/v1/signal/nodes/:nodeId/sessions', async (request, response, params) => {\n const access = resolveSessionAccess(request, params.nodeId);\n if (!access.allowed) {\n sendJson(response, access.status, { error: access.error });\n return;\n }\n\n try {\n const sessions = await service.listP2PSessions(params.nodeId);\n sendJson(response, 200, filterP2PSessionListForAccess(sessions, access));\n } catch (error) {\n if (error instanceof NodeRouteSourceNotFoundError) {\n sendJson(response, 404, { error: 'Node not found' });\n return;\n }\n sendJson(response, 500, { error: 'Failed to list p2p sessions' });\n }\n });\n\n server.get('/v1/signal/nodes/:nodeId/sessions/:sessionId', async (request, response, params) => {\n const access = resolveSessionAccess(request, params.nodeId);\n if (!access.allowed) {\n sendJson(response, access.status, { error: access.error });\n return;\n }\n\n try {\n const session = await service.getP2PSession(params.nodeId, params.sessionId);\n if (!canAccessP2PSession(access, session)) {\n sendJson(response, 403, { error: 'Solid user cannot access another client signaling session' });\n return;\n }\n sendJson(response, 200, session);\n } catch (error) {\n sendP2PSessionError(response, error);\n }\n });\n\n server.post('/v1/signal/nodes/:nodeId/sessions/:sessionId/candidates', async (request, response, params) => {\n const access = resolveSessionAccess(request, params.nodeId);\n if (!access.allowed) {\n sendJson(response, access.status, { error: access.error });\n return;\n }\n\n const body = await readJsonBody(request);\n if (!isRecord(body) || !Array.isArray(body.candidates)) {\n sendJson(response, 400, { error: 'candidates array is required' });\n return;\n }\n\n try {\n const currentSession = await service.getP2PSession(params.nodeId, params.sessionId);\n if (!canAccessP2PSession(access, currentSession)) {\n sendJson(response, 403, { error: 'Solid user cannot access another client signaling session' });\n return;\n }\n const source = resolveCandidateSource(access, params.nodeId, currentSession, body);\n const session = await service.addP2PCandidates(params.nodeId, params.sessionId, {\n role: source.role,\n sourceId: source.sourceId,\n candidates: enrichP2PCandidatesWithObservedAddress(body.candidates, resolveObservedAddress(request)),\n });\n sendJson(response, 200, session);\n } catch (error) {\n sendP2PSessionError(response, error);\n }\n });\n}\n\nasync function loadRouteSource(\n repository: EdgeNodeRepository,\n nodeId: string,\n baseStorageDomain?: string,\n): Promise<BuildRouteSetSource> {\n const [metadataRow, connectivity] = await Promise.all([\n repository.getNodeMetadata(nodeId),\n repository.getNodeConnectivityInfo(nodeId),\n ]);\n if (!metadataRow && !connectivity) {\n throw new NodeRouteSourceNotFoundError(`Node ${nodeId} not found`);\n }\n const metadata = metadataRow?.metadata ?? {};\n return {\n nodeId,\n canonicalUrl: getString(metadata.canonicalUrl),\n publicUrl: connectivity?.publicUrl,\n subdomain: connectivity?.subdomain,\n baseStorageDomain,\n ipv4: connectivity?.ipv4,\n publicPort: connectivity?.publicPort,\n connectivityStatus: connectivity?.connectivityStatus,\n metadata,\n };\n}\n\nfunction resolveAccess(request: AuthenticatedRequest, nodeId: string):\n | { allowed: true; audience: RouteAudience }\n | { allowed: false; status: number; error: string } {\n const auth = request.auth;\n if (!auth) {\n return { allowed: true, audience: 'public' };\n }\n if (isNodeAuth(auth)) {\n if (auth.nodeId !== nodeId) {\n return { allowed: false, status: 403, error: 'Node token cannot access another node' };\n }\n return { allowed: true, audience: 'managed' };\n }\n if (isServiceAuth(auth)) {\n return { allowed: true, audience: 'managed' };\n }\n return { allowed: true, audience: 'public' };\n}\n\nfunction resolveSessionAccess(request: AuthenticatedRequest, nodeId: string):\n | { allowed: true; kind: 'node'; nodeId: string }\n | { allowed: true; kind: 'service' }\n | { allowed: true; kind: 'solid'; webId: string }\n | { allowed: false; status: number; error: string } {\n const auth = request.auth;\n if (!auth) {\n return { allowed: false, status: 401, error: 'Authentication required' };\n }\n if (isNodeAuth(auth)) {\n if (auth.nodeId !== nodeId) {\n return { allowed: false, status: 403, error: 'Node token cannot access another node' };\n }\n return { allowed: true, kind: 'node', nodeId: auth.nodeId };\n }\n if (isServiceAuth(auth)) {\n return { allowed: true, kind: 'service' };\n }\n if (isSolidAuth(auth)) {\n return { allowed: true, kind: 'solid', webId: auth.webId };\n }\n return { allowed: false, status: 403, error: 'Unsupported reachability session credentials' };\n}\n\nfunction filterP2PSessionListForAccess(\n sessions: { kind: 'p2p'; sessions: P2PSession[] },\n access: { kind: 'node'; nodeId: string } | { kind: 'service' } | { kind: 'solid'; webId: string },\n): { kind: 'p2p'; sessions: P2PSession[] } {\n if (access.kind !== 'solid') {\n return sessions;\n }\n return {\n kind: sessions.kind,\n sessions: sessions.sessions.filter((session) => isOwnedBySolidUser(session, access.webId)),\n };\n}\n\nfunction canAccessP2PSession(\n access: { kind: 'node'; nodeId: string } | { kind: 'service' } | { kind: 'solid'; webId: string },\n session: P2PSession,\n): boolean {\n return access.kind !== 'solid' || isOwnedBySolidUser(session, access.webId);\n}\n\nfunction isOwnedBySolidUser(session: P2PSession, webId: string): boolean {\n return session.owner?.type === 'solid' && session.owner.webId === webId;\n}\n\nfunction resolveCandidateSource(\n access: { kind: 'node'; nodeId: string } | { kind: 'service' } | { kind: 'solid'; webId: string },\n nodeId: string,\n session: P2PSession,\n body: Record<string, unknown>,\n): { role: P2PCandidateRole; sourceId: string } {\n if (access.kind === 'node') {\n return { role: 'node', sourceId: nodeId };\n }\n if (access.kind === 'solid') {\n return { role: 'client', sourceId: session.clientId };\n }\n\n const role: P2PCandidateRole = body.role === 'node' ? 'node' : 'client';\n const sourceId = getString(body.sourceId)\n ?? getString(body.clientId)\n ?? (role === 'node' ? nodeId : 'service-client');\n return { role, sourceId };\n}\n\nfunction enrichP2PCandidatesWithObservedAddress(candidates: unknown[], observedAddress: string | undefined): unknown[] {\n if (!observedAddress) {\n return candidates;\n }\n\n return candidates.map((candidate) => {\n if (!isRecord(candidate) || !isPortOnlyCandidate(candidate)) {\n return candidate;\n }\n return {\n ...candidate,\n address: observedAddress,\n };\n });\n}\n\nfunction isPortOnlyCandidate(candidate: Record<string, unknown>): boolean {\n return normalizeCandidatePort(candidate.port) !== undefined\n && getString(candidate.host) === undefined\n && getString(candidate.address) === undefined\n && getString(candidate.url) === undefined;\n}\n\nfunction normalizeCandidatePort(value: unknown): number | undefined {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return undefined;\n }\n const port = Math.floor(value);\n return port > 0 && port <= 65535 ? port : undefined;\n}\n\nfunction resolveObservedAddress(request: AuthenticatedRequest): string | undefined {\n return firstForwardedAddress(request.headers['x-forwarded-for'])\n ?? firstHeaderAddress(request.headers['x-real-ip'])\n ?? normalizeObservedAddress(request.socket?.remoteAddress);\n}\n\nfunction firstForwardedAddress(value: string | string[] | undefined): string | undefined {\n const first = Array.isArray(value) ? value[0] : value;\n return firstHeaderAddress(first?.split(',')[0]);\n}\n\nfunction firstHeaderAddress(value: string | string[] | undefined): string | undefined {\n const first = Array.isArray(value) ? value[0] : value;\n return normalizeObservedAddress(first);\n}\n\nfunction normalizeObservedAddress(value: string | undefined): string | undefined {\n const trimmed = value?.trim().replace(/^\"|\"$/gu, '');\n if (!trimmed) {\n return undefined;\n }\n if (trimmed.startsWith('::ffff:')) {\n return trimmed.slice('::ffff:'.length);\n }\n if (trimmed.startsWith('[') && trimmed.endsWith(']')) {\n return trimmed.slice(1, -1);\n }\n return trimmed;\n}\n\nasync function readJsonBody(request: AuthenticatedRequest): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let data = '';\n request.setEncoding('utf8');\n request.on('data', (chunk: string) => {\n data += chunk;\n });\n request.on('end', () => {\n if (!data) {\n resolve(undefined);\n return;\n }\n try {\n resolve(JSON.parse(data));\n } catch {\n resolve(undefined);\n }\n });\n request.on('error', reject);\n });\n}\n\nfunction sendJson(response: ServerResponse, status: number, data: unknown): void {\n response.statusCode = status;\n response.setHeader('Content-Type', 'application/json');\n response.end(JSON.stringify(data));\n}\n\nfunction sendP2PSessionError(response: ServerResponse, error: unknown): void {\n if (error instanceof P2PSessionExpiredError) {\n sendJson(response, 410, { error: 'P2P session expired' });\n return;\n }\n if (error instanceof P2PSessionNotFoundError) {\n sendJson(response, 404, { error: 'P2P session not found' });\n return;\n }\n if (error instanceof P2PCandidateUpdateLimitExceededError) {\n sendJson(response, 429, { error: 'P2P candidate update limit exceeded' });\n return;\n }\n if (error instanceof P2PCandidateSessionLimitExceededError) {\n sendJson(response, 429, { error: 'P2P candidate session limit exceeded' });\n return;\n }\n if (error instanceof NodeRouteSourceNotFoundError) {\n sendJson(response, 404, { error: 'Node not found' });\n return;\n }\n sendJson(response, 500, { error: 'Failed to update p2p session' });\n}\n\nfunction getString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction parsePositiveInteger(value: string | undefined): number | undefined {\n const trimmed = getString(value);\n if (!trimmed) {\n return undefined;\n }\n const parsed = Number.parseInt(trimmed, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n"]}
@@ -147,7 +147,7 @@ export declare class InngestRunExecutionBackend implements RunExecutionBackend {
147
147
  readonly event: "xpod/run.continue_requested";
148
148
  }], Omit<Omit<import("inngest").BaseContext<Inngest.Any>, never> & Record<never, never> & {
149
149
  logger: import("inngest").Logger;
150
- }, "group" | "requestId" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "attempt" | "maxAttempts"> & import("inngest").FailureEventArgs<EventPayload<any>> & {
150
+ }, "group" | "requestId" | "attempt" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "maxAttempts"> & import("inngest").FailureEventArgs<EventPayload<any>> & {
151
151
  step: {
152
152
  sendEvent: (idOrOptions: import("inngest").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<Inngest<import("inngest").ClientOptions>>>>;
153
153
  waitForSignal: <TData>(idOrOptions: import("inngest").StepOptionsOrId, opts: {
@@ -318,7 +318,7 @@ export declare class InngestRunExecutionBackend implements RunExecutionBackend {
318
318
  readonly event: "xpod/run.continue_requested";
319
319
  }], Omit<Omit<import("inngest").BaseContext<Inngest.Any>, never> & Record<never, never> & {
320
320
  logger: import("inngest").Logger;
321
- }, "group" | "requestId" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "attempt" | "maxAttempts"> & import("inngest").FailureEventArgs<EventPayload<any>> & {
321
+ }, "group" | "requestId" | "attempt" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "maxAttempts"> & import("inngest").FailureEventArgs<EventPayload<any>> & {
322
322
  step: {
323
323
  sendEvent: (idOrOptions: import("inngest").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<Inngest<import("inngest").ClientOptions>>>>;
324
324
  waitForSignal: <TData>(idOrOptions: import("inngest").StepOptionsOrId, opts: {
@@ -128,7 +128,7 @@ export declare class InngestTaskScheduler<TContext = StoreContext> {
128
128
  readonly event: "xpod/task.materialize_due";
129
129
  }], Omit<Omit<import("inngest/types").BaseContext<import("inngest").Inngest.Any>, never> & Record<never, never> & {
130
130
  logger: import("inngest").Logger;
131
- }, "group" | "requestId" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "attempt" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
131
+ }, "group" | "requestId" | "attempt" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
132
132
  step: {
133
133
  sendEvent: (idOrOptions: import("inngest/types").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<import("inngest").Inngest<import("inngest/types").ClientOptions>>>>;
134
134
  waitForSignal: <TData>(idOrOptions: import("inngest/types").StepOptionsOrId, opts: {
@@ -296,7 +296,7 @@ export declare class InngestTaskScheduler<TContext = StoreContext> {
296
296
  readonly event: "xpod/task.materialize_due";
297
297
  }], Omit<Omit<import("inngest/types").BaseContext<import("inngest").Inngest.Any>, never> & Record<never, never> & {
298
298
  logger: import("inngest").Logger;
299
- }, "group" | "requestId" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "attempt" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
299
+ }, "group" | "requestId" | "attempt" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
300
300
  step: {
301
301
  sendEvent: (idOrOptions: import("inngest/types").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<import("inngest").Inngest<import("inngest/types").ClientOptions>>>>;
302
302
  waitForSignal: <TData>(idOrOptions: import("inngest/types").StepOptionsOrId, opts: {
@@ -469,7 +469,7 @@ export declare class InngestTaskScheduler<TContext = StoreContext> {
469
469
  readonly event: "xpod/task.event";
470
470
  }], Omit<Omit<import("inngest/types").BaseContext<import("inngest").Inngest.Any>, never> & Record<never, never> & {
471
471
  logger: import("inngest").Logger;
472
- }, "group" | "requestId" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "attempt" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
472
+ }, "group" | "requestId" | "attempt" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
473
473
  step: {
474
474
  sendEvent: (idOrOptions: import("inngest/types").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<import("inngest").Inngest<import("inngest/types").ClientOptions>>>>;
475
475
  waitForSignal: <TData>(idOrOptions: import("inngest/types").StepOptionsOrId, opts: {
@@ -635,7 +635,7 @@ export declare class InngestTaskScheduler<TContext = StoreContext> {
635
635
  readonly event: "xpod/task.event";
636
636
  }], Omit<Omit<import("inngest/types").BaseContext<import("inngest").Inngest.Any>, never> & Record<never, never> & {
637
637
  logger: import("inngest").Logger;
638
- }, "group" | "requestId" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "attempt" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
638
+ }, "group" | "requestId" | "attempt" | "event" | "runId" | "step" | "defer" | "events" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
639
639
  step: {
640
640
  sendEvent: (idOrOptions: import("inngest/types").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<import("inngest").Inngest<import("inngest/types").ClientOptions>>>>;
641
641
  waitForSignal: <TData>(idOrOptions: import("inngest/types").StepOptionsOrId, opts: {
@@ -14,6 +14,7 @@
14
14
  "undefineds:dist/storage/rdf/PostgresRdfEngine.jsonld",
15
15
  "undefineds:dist/dns/DnsProvider.jsonld",
16
16
  "undefineds:dist/edge/EdgeNodeCertificateProvisioner.jsonld",
17
+ "undefineds:dist/edge/EdgeNodeAgent.jsonld",
17
18
  "undefineds:dist/edge/interfaces/EdgeNodeTunnelManager.jsonld",
18
19
  "undefineds:dist/quota/QuotaService.jsonld",
19
20
  "undefineds:dist/quota/EntitlementProvider.jsonld",
@@ -56,7 +57,7 @@
56
57
  "undefineds:dist/edge/EdgeNodeTunnelManager.jsonld",
57
58
  "undefineds:dist/edge/FrpTunnelManager.jsonld",
58
59
  "undefineds:dist/edge/EdgeNodeHealthProbeService.jsonld",
59
- "undefineds:dist/edge/EdgeNodeAgent.jsonld",
60
+ "undefineds:dist/edge/EdgeNodeAgentInitializer.jsonld",
60
61
  "undefineds:dist/service/EdgeNodeCertificateService.jsonld",
61
62
  "undefineds:dist/edge/acme/AcmeCertificateManager.jsonld",
62
63
  "undefineds:dist/edge/EdgeNodeModeDetector.jsonld",
@@ -99,6 +100,17 @@
99
100
  "undefineds:dist/api/reconciler/coordination.jsonld",
100
101
  "undefineds:dist/api/reconciler/ClientReconcilerCoordinator.jsonld",
101
102
  "undefineds:dist/api/reconciler/WakeAgentQueue.jsonld",
102
- "undefineds:dist/api/reconciler/ServerGroupReconcilerService.jsonld"
103
+ "undefineds:dist/api/reconciler/ServerGroupReconcilerService.jsonld",
104
+ "undefineds:dist/edge/reachability/types.jsonld",
105
+ "undefineds:dist/edge/reachability/ReachabilitySessionService.jsonld",
106
+ "undefineds:dist/edge/reachability/ManagedRouteSelector.jsonld",
107
+ "undefineds:dist/edge/reachability/CanonicalFetch.jsonld",
108
+ "undefineds:dist/edge/reachability/P2PDataPlane.jsonld",
109
+ "undefineds:dist/edge/reachability/P2PSignalingClient.jsonld",
110
+ "undefineds:dist/edge/reachability/TcpP2PDataPlaneTransport.jsonld",
111
+ "undefineds:dist/edge/reachability/TcpP2PSignalingSession.jsonld",
112
+ "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld",
113
+ "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld",
114
+ "undefineds:dist/edge/reachability/P2PRealnetAcceptance.jsonld"
103
115
  ]
104
116
  }