@undefineds.co/xpod 0.4.14 → 0.4.15

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 (202) hide show
  1. package/config/cloud.json +0 -3
  2. package/config/local.json +0 -3
  3. package/config/xpod.base.json +0 -24
  4. package/dist/api/ai-config/AiConfigStore.d.ts +2 -2
  5. package/dist/api/ai-config/AiConfigStore.js +4 -1
  6. package/dist/api/ai-gateway/auth/CallerPodAccess.d.ts +2 -5
  7. package/dist/api/ai-gateway/auth/CallerPodAccess.js +3 -42
  8. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.d.ts +2 -1
  9. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js +1 -1
  10. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.d.ts +3 -8
  11. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js +9 -32
  12. package/dist/api/ai-gateway/connect/index.d.ts +3 -3
  13. package/dist/api/ai-gateway/connect/index.js +9 -38
  14. package/dist/api/ai-gateway/errors.js +2 -1
  15. package/dist/api/ai-gateway/models/PodModelSelectionRepository.d.ts +3 -3
  16. package/dist/api/ai-gateway/models/PodModelSelectionRepository.js +7 -11
  17. package/dist/api/ai-gateway/pod/HostedPodRoute.d.ts +27 -0
  18. package/dist/api/ai-gateway/pod/HostedPodRoute.js +47 -0
  19. package/dist/api/ai-gateway/pod/OwnerPodAccess.d.ts +79 -0
  20. package/dist/api/ai-gateway/pod/OwnerPodAccess.js +192 -0
  21. package/dist/api/ai-gateway/pod/PodInterfaceKeyStore.d.ts +52 -0
  22. package/dist/api/ai-gateway/pod/PodInterfaceKeyStore.js +61 -0
  23. package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.d.ts +3 -3
  24. package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.js +7 -32
  25. package/dist/api/auth/ClientCredentialsAuthenticator.js +5 -28
  26. package/dist/api/auth/TokenEndpointRoute.d.ts +18 -0
  27. package/dist/api/auth/TokenEndpointRoute.js +36 -0
  28. package/dist/api/chatkit/pod-store.d.ts +4 -4
  29. package/dist/api/chatkit/pod-store.js +24 -95
  30. package/dist/api/container/common.js +36 -23
  31. package/dist/api/container/local.js +62 -29
  32. package/dist/api/container/routes.js +39 -7
  33. package/dist/api/container/types.d.ts +2 -2
  34. package/dist/api/handlers/AiGatewayManagementHandler.d.ts +6 -0
  35. package/dist/api/handlers/AiGatewayManagementHandler.js +17 -4
  36. package/dist/api/handlers/NetworkSettingsHandler.d.ts +7 -0
  37. package/dist/api/handlers/NetworkSettingsHandler.js +7 -0
  38. package/dist/api/handlers/PodSettingsHandler.d.ts +3 -3
  39. package/dist/api/handlers/PodSettingsHandler.js +3 -3
  40. package/dist/api/handlers/ProvisionHandler.d.ts +29 -0
  41. package/dist/api/handlers/ProvisionHandler.js +46 -0
  42. package/dist/api/handlers/ReachabilityHandler.js +3 -1
  43. package/dist/api/runtime.d.ts +9 -0
  44. package/dist/api/runtime.js +16 -3
  45. package/dist/cli/commands/start.d.ts +9 -2
  46. package/dist/cli/commands/start.js +15 -17
  47. package/dist/components/components.jsonld +1 -1
  48. package/dist/components/context.jsonld +5 -42
  49. package/dist/edge/EdgeNodeAgent.d.ts +10 -0
  50. package/dist/edge/EdgeNodeAgent.js +55 -0
  51. package/dist/edge/EdgeNodeAgent.jsonld +8 -0
  52. package/dist/edge/reachability/ReachabilitySessionService.js +4 -1
  53. package/dist/edge/reachability/RouteSetBuilder.d.ts +18 -1
  54. package/dist/edge/reachability/RouteSetBuilder.js +47 -0
  55. package/dist/edge/reachability/RouteSetBuilder.jsonld +29 -0
  56. package/dist/edge/reachability/types.d.ts +8 -2
  57. package/dist/http/SubgraphSparqlHttpHandler.d.ts +1 -25
  58. package/dist/http/SubgraphSparqlHttpHandler.js +0 -100
  59. package/dist/http/SubgraphSparqlHttpHandler.jsonld +0 -8
  60. package/dist/identity/drizzle/PodInterfaceKeyRepository.d.ts +33 -0
  61. package/dist/identity/drizzle/PodInterfaceKeyRepository.js +78 -0
  62. package/dist/identity/drizzle/db.d.ts +7 -0
  63. package/dist/identity/drizzle/db.js +30 -0
  64. package/dist/identity/drizzle/schema.pg.d.ts +9 -0
  65. package/dist/identity/drizzle/schema.pg.js +16 -1
  66. package/dist/identity/drizzle/schema.sqlite.d.ts +106 -0
  67. package/dist/identity/drizzle/schema.sqlite.js +16 -1
  68. package/dist/index.d.ts +1 -2
  69. package/dist/index.js +3 -5
  70. package/dist/runtime/GatewayAdminProxyAuth.d.ts +1 -16
  71. package/dist/runtime/GatewayAdminProxyAuth.js +2 -71
  72. package/dist/runtime/Proxy.d.ts +10 -1
  73. package/dist/runtime/Proxy.js +95 -28
  74. package/dist/runtime/bootstrap.d.ts +10 -0
  75. package/dist/runtime/bootstrap.js +24 -0
  76. package/dist/runtime/host/node/NodeRuntimeHost.js +5 -3
  77. package/dist/runtime/index.d.ts +1 -1
  78. package/dist/runtime/index.js +2 -3
  79. package/dist/runtime/js-runtime.js +10 -3
  80. package/dist/runtime/lifecycle.js +5 -4
  81. package/dist/runtime/port-finder.d.ts +9 -13
  82. package/dist/runtime/port-finder.js +18 -37
  83. package/dist/tunnel/AutoTunnelProvider.d.ts +56 -0
  84. package/dist/tunnel/AutoTunnelProvider.js +181 -0
  85. package/dist/tunnel/index.d.ts +1 -0
  86. package/dist/tunnel/index.js +3 -1
  87. package/node_modules/@undefineds.co/ai-connections/dist/error-wording.js +7 -2
  88. package/node_modules/@undefineds.co/solid-sdk/dist/access-route.cjs +230 -0
  89. package/node_modules/@undefineds.co/solid-sdk/dist/access-route.d.ts +76 -0
  90. package/node_modules/@undefineds.co/solid-sdk/dist/access-route.js +115 -0
  91. package/node_modules/@undefineds.co/solid-sdk/dist/index.cjs +89 -0
  92. package/node_modules/@undefineds.co/solid-sdk/dist/index.d.ts +1 -0
  93. package/node_modules/@undefineds.co/solid-sdk/dist/index.js +1 -0
  94. package/node_modules/@undefineds.co/solid-sdk/package.json +6 -1
  95. package/package.json +2 -2
  96. package/static/app/assets/inrupt-smoke.js +23 -23
  97. package/static/auth-callback/assets/{AiConfigContext-DCXpjzpj.js → AiConfigContext-Bx0HCAW5.js} +1 -1
  98. package/static/auth-callback/assets/{AiConfigPage-fe-xMIXa.js → AiConfigPage-ChQImMbm.js} +1 -1
  99. package/static/auth-callback/assets/{DocumentProcessingPanel-BGnkfMuU.js → DocumentProcessingPanel-D830H4SH.js} +1 -1
  100. package/static/auth-callback/assets/{IndexLifecyclePanel-42Jups0j.js → IndexLifecyclePanel-CnlkjMF8.js} +1 -1
  101. package/static/auth-callback/assets/{IndexSubjectPanel-CYyLOlQU.js → IndexSubjectPanel-CdEfg_CO.js} +1 -1
  102. package/static/auth-callback/assets/{ModelAssignmentsPanel-BOvUH4yr.js → ModelAssignmentsPanel-D-8A8dZx.js} +1 -1
  103. package/static/auth-callback/assets/{ModelsPage-DwyITxt7.js → ModelsPage-Cim7cubp.js} +3 -3
  104. package/static/auth-callback/assets/{NetworkPage-CG9RelJA.js → NetworkPage-CfK3x-pb.js} +2 -2
  105. package/static/auth-callback/assets/{PaneListHeader-yvd5ohs6.js → PaneListHeader-Bf_kEd4U.js} +1 -1
  106. package/static/auth-callback/assets/{SearchIndexingPanel-2_a6ezzj.js → SearchIndexingPanel-4kD4RBEQ.js} +1 -1
  107. package/static/auth-callback/assets/{StatusSubjectPanel-8qtVxnzC.js → StatusSubjectPanel-Bw9AiTSf.js} +1 -1
  108. package/static/auth-callback/assets/{StatusWorkspace-CBdRahCF.js → StatusWorkspace-C-301H-0.js} +1 -1
  109. package/static/auth-callback/assets/{SystemSettingsPage-BZJ6vWkg.js → SystemSettingsPage-BbWn-eG6.js} +1 -1
  110. package/static/auth-callback/assets/{SystemSettingsSubjectPanel-xm08UhXP.js → SystemSettingsSubjectPanel-DA4wvNSm.js} +1 -1
  111. package/static/auth-callback/assets/{XpodAiConnectionsPodStore-V6vgGfuw.js → XpodAiConnectionsPodStore-CohdyLoX.js} +1 -1
  112. package/static/auth-callback/assets/{activity-CeM7xly8.js → activity-CCsb9Oam.js} +1 -1
  113. package/static/auth-callback/assets/ai-connections-DexTPyfC.js +1 -0
  114. package/static/auth-callback/assets/{auth-callback-DTG8cSj7.js → auth-callback-Ds-c62W3.js} +5 -5
  115. package/static/auth-callback/assets/{badge-qJo2Zr4d.js → badge-CQm5OyzR.js} +1 -1
  116. package/static/auth-callback/assets/{collection-runtime-Bztsz31a.js → collection-runtime-DlAuU5TW.js} +1 -1
  117. package/static/auth-callback/assets/{dialog-D-h6x5x0.js → dialog-CnGzg5Ff.js} +1 -1
  118. package/static/auth-callback/assets/{download-C2ir4lmR.js → download-DwMONJvm.js} +1 -1
  119. package/static/auth-callback/assets/{external-link-THLDL4On.js → external-link-Bu5AGqCO.js} +1 -1
  120. package/static/auth-callback/assets/{index-BlVZb9A7.js → index-B0T-1YO8.js} +1 -1
  121. package/static/auth-callback/assets/{index-BRpv4yO3.js → index-C8QZf_4W.js} +1 -1
  122. package/static/auth-callback/assets/{index-D983R1FV.js → index-DwIlrIDG.js} +1 -1
  123. package/static/auth-callback/assets/{index-browser-3FvR0aaf.js → index-browser-lRsMcP_7.js} +1 -1
  124. package/static/auth-callback/assets/{key-round-C9U5j1Ly.js → key-round-BDMtU57P.js} +1 -1
  125. package/static/auth-callback/assets/{rotate-ccw-DP45cD4o.js → rotate-ccw-CoKIGCsQ.js} +1 -1
  126. package/static/auth-callback/assets/{skeleton-Dn4EiSCS.js → skeleton-CPoSWHDr.js} +1 -1
  127. package/static/auth-callback/assets/{waypoints-uqFQ3uEG.js → waypoints-BeYHxqHv.js} +1 -1
  128. package/static/auth-callback/assets/{workspace-layout-DLYK0Wwu.js → workspace-layout-xA5YxiK3.js} +1 -1
  129. package/static/auth-callback/auth-callback.html +1 -1
  130. package/static/dashboard/assets/{AiConfigContext-DJcMECNw.js → AiConfigContext-rWx4TyiH.js} +1 -1
  131. package/static/dashboard/assets/{AiConfigPage-BvE7dOTy.js → AiConfigPage-StVJpDFT.js} +1 -1
  132. package/static/dashboard/assets/{DocumentProcessingPanel-D9780krf.js → DocumentProcessingPanel-DJ5yul2O.js} +1 -1
  133. package/static/dashboard/assets/{IndexLifecyclePanel-SjsUcnbM.js → IndexLifecyclePanel-CpjtgezK.js} +1 -1
  134. package/static/dashboard/assets/{IndexSubjectPanel-C-ym2Tqi.js → IndexSubjectPanel-BVKcot1_.js} +1 -1
  135. package/static/dashboard/assets/{ModelAssignmentsPanel-BRjZ-arB.js → ModelAssignmentsPanel-BxVgWZPc.js} +1 -1
  136. package/static/dashboard/assets/{ModelsPage-BtTU6du3.js → ModelsPage-CECBwmeO.js} +3 -3
  137. package/static/dashboard/assets/{NetworkPage-CV9qpkDg.js → NetworkPage-zQgFscEW.js} +2 -2
  138. package/static/dashboard/assets/{PaneListHeader-KyPTTBsv.js → PaneListHeader-DIPe8pS6.js} +1 -1
  139. package/static/dashboard/assets/{SearchIndexingPanel-DolHGawg.js → SearchIndexingPanel-lalFJ3c8.js} +1 -1
  140. package/static/dashboard/assets/{StatusSubjectPanel-_YZvKiYi.js → StatusSubjectPanel-CpY_KU-G.js} +1 -1
  141. package/static/dashboard/assets/{StatusWorkspace-Dlbh_G40.js → StatusWorkspace-BwWCii3A.js} +1 -1
  142. package/static/dashboard/assets/{SystemSettingsPage-Be7QSIoz.js → SystemSettingsPage-BKZxX-_n.js} +1 -1
  143. package/static/dashboard/assets/{SystemSettingsSubjectPanel-DBp0gja7.js → SystemSettingsSubjectPanel-BZrqb2C5.js} +1 -1
  144. package/static/dashboard/assets/{XpodAiConnectionsPodStore-ClpSjzpP.js → XpodAiConnectionsPodStore-Bbpx9dTN.js} +1 -1
  145. package/static/dashboard/assets/{activity-lsNbTXJu.js → activity-BJ15TLVP.js} +1 -1
  146. package/static/dashboard/assets/ai-connections-GG6qkTJt.js +1 -0
  147. package/static/dashboard/assets/{badge-CVatKjaG.js → badge-BBX5FlaB.js} +1 -1
  148. package/static/dashboard/assets/{collection-runtime-Ec4UnY28.js → collection-runtime-UBbHBzdf.js} +1 -1
  149. package/static/dashboard/assets/{dashboard-BEzsRsvF.js → dashboard-7Y6BHyV9.js} +62 -62
  150. package/static/dashboard/assets/{dialog-BuyelQ4V.js → dialog-QrOJ-hB0.js} +1 -1
  151. package/static/dashboard/assets/{download-B4Nf678F.js → download-DK9x-VOP.js} +1 -1
  152. package/static/dashboard/assets/{external-link-PYv9CvKL.js → external-link-Cnkn87mJ.js} +1 -1
  153. package/static/dashboard/assets/{index-C9XPE-mI.js → index-B1z84it5.js} +1 -1
  154. package/static/dashboard/assets/{index-awr7ulur.js → index-DzjauX3N.js} +1 -1
  155. package/static/dashboard/assets/{index-Cx69bTNh.js → index-cIa8_efI.js} +1 -1
  156. package/static/dashboard/assets/{key-round-VsY80U1Z.js → key-round-BoS6UqKz.js} +1 -1
  157. package/static/dashboard/assets/{rotate-ccw-Cz1K4vE9.js → rotate-ccw-BeWV76i0.js} +1 -1
  158. package/static/dashboard/assets/{skeleton-BhNv_8tO.js → skeleton-CypcJL07.js} +1 -1
  159. package/static/dashboard/assets/{waypoints-DYS9XFYV.js → waypoints-zHOOqfhi.js} +1 -1
  160. package/static/dashboard/assets/{workspace-layout-OZJAFx5V.js → workspace-layout-DZ9JuxnM.js} +1 -1
  161. package/static/dashboard/dashboard.html +1 -1
  162. package/static/settings/assets/{AiConfigContext-B8JS3MXF.js → AiConfigContext-D3b1l_Kw.js} +1 -1
  163. package/static/settings/assets/{AiConfigPage-C3Zl8LUl.js → AiConfigPage-DYKw-atP.js} +1 -1
  164. package/static/settings/assets/{DocumentProcessingPanel-DpDZAQvf.js → DocumentProcessingPanel-DguRfsIV.js} +1 -1
  165. package/static/settings/assets/{IndexLifecyclePanel-Ec1XF88t.js → IndexLifecyclePanel-B85qJJA4.js} +1 -1
  166. package/static/settings/assets/{IndexSubjectPanel-BulT9BmO.js → IndexSubjectPanel-CsN7IxaV.js} +1 -1
  167. package/static/settings/assets/{ModelAssignmentsPanel-BcGSmYjb.js → ModelAssignmentsPanel-DEp9byI6.js} +1 -1
  168. package/static/settings/assets/{ModelsPage-CvTCKy1T.js → ModelsPage-CzTnPs_G.js} +3 -3
  169. package/static/settings/assets/{NetworkPage-DQF5u9Bn.js → NetworkPage-BDVnmUU0.js} +2 -2
  170. package/static/settings/assets/{PaneListHeader-D40hqcVl.js → PaneListHeader-Cpe39N76.js} +1 -1
  171. package/static/settings/assets/{SearchIndexingPanel-iCUPOx_0.js → SearchIndexingPanel-DA9w8lLL.js} +1 -1
  172. package/static/settings/assets/{StatusSubjectPanel-E--ZHSql.js → StatusSubjectPanel-BU0Aq0CS.js} +1 -1
  173. package/static/settings/assets/{StatusWorkspace-YjDAx1II.js → StatusWorkspace-D1AC1i5N.js} +1 -1
  174. package/static/settings/assets/{SystemSettingsPage-CDdV3mC9.js → SystemSettingsPage-ChKvOioO.js} +1 -1
  175. package/static/settings/assets/{SystemSettingsSubjectPanel-Br8WazDq.js → SystemSettingsSubjectPanel-DyTpOnD2.js} +1 -1
  176. package/static/settings/assets/{XpodAiConnectionsPodStore-CLr38P_Z.js → XpodAiConnectionsPodStore-VU5Bsz1a.js} +1 -1
  177. package/static/settings/assets/{activity-C5DAHHTE.js → activity-DGpLfsQB.js} +1 -1
  178. package/static/settings/assets/ai-connections-DexTPyfC.js +1 -0
  179. package/static/settings/assets/{badge-D8eIyxKt.js → badge-_hCrrWRP.js} +1 -1
  180. package/static/settings/assets/{collection-runtime-DLc9zil8.js → collection-runtime-E40TZ8Cl.js} +1 -1
  181. package/static/settings/assets/{dialog-CyuR7Va4.js → dialog-DTr2JkHn.js} +1 -1
  182. package/static/settings/assets/{download-c0oxZrCz.js → download-B_D21DDX.js} +1 -1
  183. package/static/settings/assets/{external-link-D3Z34fP_.js → external-link-BATYaM13.js} +1 -1
  184. package/static/settings/assets/{index-CSr3-2Cj.js → index-BAlAVi-J.js} +1 -1
  185. package/static/settings/assets/{index-ComidxAm.js → index-Ckh4h9jj.js} +1 -1
  186. package/static/settings/assets/{index-CKGbRFlc.js → index-DOiwLF4B.js} +1 -1
  187. package/static/settings/assets/{index-browser-CAscEbae.js → index-browser-DFFWN3Kr.js} +1 -1
  188. package/static/settings/assets/{key-round-DyjLmXuB.js → key-round-ZTVU_1O1.js} +1 -1
  189. package/static/settings/assets/{rotate-ccw-679HF_ec.js → rotate-ccw-DDdw8PTU.js} +1 -1
  190. package/static/settings/assets/{settings-DKknqu4C.js → settings-CyLnISOv.js} +62 -62
  191. package/static/settings/assets/{skeleton-CkopNkPB.js → skeleton-DEzA3hc2.js} +1 -1
  192. package/static/settings/assets/{waypoints-CrqfAftE.js → waypoints-BSdfNvEb.js} +1 -1
  193. package/static/settings/assets/{workspace-layout-UN6c4qTG.js → workspace-layout-BOQnl_3d.js} +1 -1
  194. package/static/settings/settings.html +1 -1
  195. package/dist/api/ai-gateway/pod/HostedPodDataAccess.d.ts +0 -31
  196. package/dist/api/ai-gateway/pod/HostedPodDataAccess.js +0 -330
  197. package/dist/http/InternalPodDataHttpHandler.d.ts +0 -57
  198. package/dist/http/InternalPodDataHttpHandler.js +0 -551
  199. package/dist/http/InternalPodDataHttpHandler.jsonld +0 -243
  200. package/static/auth-callback/assets/ai-connections-C6yaE6WQ.js +0 -1
  201. package/static/dashboard/assets/ai-connections-CXp5gd1J.js +0 -1
  202. package/static/settings/assets/ai-connections-C6yaE6WQ.js +0 -1
@@ -0,0 +1,115 @@
1
+ import { createSolidLocalRouteFetch } from './local-route-fetch.js';
2
+ /**
3
+ * Pick the best route that actually answers: candidates are ordered by priority
4
+ * and probed in parallel, so a same-machine client lands on loopback while a
5
+ * remote one skips it without waiting for it to time out.
6
+ */
7
+ export async function chooseAccessRoute(routeSet, options = {}) {
8
+ const candidates = usableRoutes(routeSet, options.managedClient ?? true, options.allowLocalOnlyRoutes ?? false);
9
+ if (candidates.length === 0) {
10
+ return null;
11
+ }
12
+ const probe = options.probe ?? defaultProbe;
13
+ const timeoutMs = options.probeTimeoutMs ?? 1000;
14
+ const results = await Promise.all(candidates.map(async (route) => ({
15
+ route,
16
+ ok: await probeWithTimeout(route, probe, timeoutMs),
17
+ })));
18
+ return results.find((result) => result.ok)?.route ?? null;
19
+ }
20
+ export function usableRoutes(routeSet, managedClient, allowLocalOnlyRoutes) {
21
+ return routeSet.routes
22
+ .filter((route) => route.health !== 'unreachable')
23
+ .filter((route) => managedClient || !route.requiresManagedClient)
24
+ .filter((route) => allowLocalOnlyRoutes || route.visibility !== 'local-only')
25
+ .slice()
26
+ .sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id));
27
+ }
28
+ /**
29
+ * A fetch that keeps canonical URLs canonical while sending the request over the
30
+ * best route this client can use.
31
+ *
32
+ * Canonical identity is preserved end to end: the target URL is rewritten, the
33
+ * canonical host travels in the `x-xpod-canonical-*` headers, and the response
34
+ * reports its canonical URL again. When the chosen route stops answering, the
35
+ * routes are refreshed and the request is retried once against the new best one.
36
+ */
37
+ export function createSolidAccessRouteFetch(options) {
38
+ const managedClient = options.managedClient ?? true;
39
+ let selected;
40
+ const selectRoute = async () => {
41
+ const routeSet = {
42
+ canonicalUrl: options.routes()[0]?.canonicalUrl ?? '',
43
+ routes: options.routes(),
44
+ };
45
+ const candidate = await chooseAccessRoute(routeSet, {
46
+ managedClient,
47
+ ...(options.allowLocalOnlyRoutes === undefined
48
+ ? {}
49
+ : { allowLocalOnlyRoutes: options.allowLocalOnlyRoutes }),
50
+ ...(options.probeTimeoutMs === undefined ? {} : { probeTimeoutMs: options.probeTimeoutMs }),
51
+ ...(options.probe === undefined ? {} : { probe: options.probe }),
52
+ });
53
+ selected = candidate ?? undefined;
54
+ return candidate;
55
+ };
56
+ const routeFor = async () => {
57
+ if (selected && options.routes().some((route) => route.id === selected?.id && route.targetUrl === selected?.targetUrl)) {
58
+ return selected;
59
+ }
60
+ return (await selectRoute()) ?? undefined;
61
+ };
62
+ const routedFetchFor = (route) => createSolidLocalRouteFetch({
63
+ fetch: options.fetch,
64
+ routes: () => [{
65
+ canonicalBaseUrl: route.canonicalUrl,
66
+ localBaseUrl: route.targetUrl,
67
+ }],
68
+ });
69
+ return async (input, init) => {
70
+ const route = await routeFor();
71
+ if (!route) {
72
+ return init === undefined
73
+ ? options.fetch.call(globalThis, input)
74
+ : options.fetch.call(globalThis, input, init);
75
+ }
76
+ try {
77
+ return await routedFetchFor(route)(input, init);
78
+ }
79
+ catch (error) {
80
+ // The route went away (laptop moved networks, tunnel restarted). Ask for
81
+ // the current set once and retry against whatever is best now.
82
+ selected = undefined;
83
+ await options.refreshRoutes?.();
84
+ const retryRoute = await selectRoute();
85
+ if (!retryRoute || retryRoute.id === route.id) {
86
+ throw error;
87
+ }
88
+ return routedFetchFor(retryRoute)(input, init);
89
+ }
90
+ };
91
+ }
92
+ async function probeWithTimeout(route, probe, timeoutMs) {
93
+ const controller = new AbortController();
94
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
95
+ try {
96
+ return await Promise.resolve(probe(route, controller.signal));
97
+ }
98
+ catch {
99
+ return false;
100
+ }
101
+ finally {
102
+ clearTimeout(timer);
103
+ }
104
+ }
105
+ async function defaultProbe(route, signal) {
106
+ if (!route.targetUrl.startsWith('http://') && !route.targetUrl.startsWith('https://')) {
107
+ // Non-HTTP routes (p2p sockets) report their own readiness.
108
+ return route.health === 'healthy';
109
+ }
110
+ const response = await fetch(new URL('/.well-known/solid', route.targetUrl), {
111
+ method: 'HEAD',
112
+ signal,
113
+ });
114
+ return response.ok || response.status === 401 || response.status === 403;
115
+ }
@@ -43,6 +43,7 @@ __export(exports_src, {
43
43
  useSolidSessionSnapshot: () => useSolidSessionSnapshot,
44
44
  useSolidRuntime: () => useSolidRuntime,
45
45
  useLoginStore: () => useLoginStore,
46
+ usableRoutes: () => usableRoutes,
46
47
  storageBindingMatches: () => storageBindingMatches,
47
48
  resolveSolidLocalRouteUrl: () => resolveSolidLocalRouteUrl,
48
49
  resolveSolidLocalRouteFromStatus: () => resolveSolidLocalRouteFromStatus,
@@ -57,8 +58,10 @@ __export(exports_src, {
57
58
  createStorageSelectionState: () => createStorageSelectionState,
58
59
  createSolidSessionRuntime: () => createSolidSessionRuntime,
59
60
  createSolidLocalRouteFetch: () => createSolidLocalRouteFetch,
61
+ createSolidAccessRouteFetch: () => createSolidAccessRouteFetch,
60
62
  createPodRuntime: () => createPodRuntime,
61
63
  createLoginStore: () => createLoginStore,
64
+ chooseAccessRoute: () => chooseAccessRoute,
62
65
  SolidRuntimeProvider: () => SolidRuntimeProvider,
63
66
  SOLID_LOCAL_ROUTE_LOCAL_URL_HEADER: () => SOLID_LOCAL_ROUTE_LOCAL_URL_HEADER,
64
67
  SOLID_LOCAL_ROUTE_CANONICAL_URL_HEADER: () => SOLID_LOCAL_ROUTE_CANONICAL_URL_HEADER,
@@ -1154,3 +1157,89 @@ function routedHeaders(input, init, canonicalUrl, localUrl) {
1154
1157
  headers.set(SOLID_LOCAL_ROUTE_LOCAL_URL_HEADER, localUrl.toString());
1155
1158
  return headers;
1156
1159
  }
1160
+ // src/access-route.ts
1161
+ async function chooseAccessRoute(routeSet, options = {}) {
1162
+ const candidates = usableRoutes(routeSet, options.managedClient ?? true, options.allowLocalOnlyRoutes ?? false);
1163
+ if (candidates.length === 0) {
1164
+ return null;
1165
+ }
1166
+ const probe = options.probe ?? defaultProbe;
1167
+ const timeoutMs = options.probeTimeoutMs ?? 1000;
1168
+ const results = await Promise.all(candidates.map(async (route) => ({
1169
+ route,
1170
+ ok: await probeWithTimeout(route, probe, timeoutMs)
1171
+ })));
1172
+ return results.find((result) => result.ok)?.route ?? null;
1173
+ }
1174
+ function usableRoutes(routeSet, managedClient, allowLocalOnlyRoutes) {
1175
+ return routeSet.routes.filter((route) => route.health !== "unreachable").filter((route) => managedClient || !route.requiresManagedClient).filter((route) => allowLocalOnlyRoutes || route.visibility !== "local-only").slice().sort((left, right) => left.priority - right.priority || left.id.localeCompare(right.id));
1176
+ }
1177
+ function createSolidAccessRouteFetch(options) {
1178
+ const managedClient = options.managedClient ?? true;
1179
+ let selected;
1180
+ const selectRoute = async () => {
1181
+ const routeSet = {
1182
+ canonicalUrl: options.routes()[0]?.canonicalUrl ?? "",
1183
+ routes: options.routes()
1184
+ };
1185
+ const candidate = await chooseAccessRoute(routeSet, {
1186
+ managedClient,
1187
+ ...options.allowLocalOnlyRoutes === undefined ? {} : { allowLocalOnlyRoutes: options.allowLocalOnlyRoutes },
1188
+ ...options.probeTimeoutMs === undefined ? {} : { probeTimeoutMs: options.probeTimeoutMs },
1189
+ ...options.probe === undefined ? {} : { probe: options.probe }
1190
+ });
1191
+ selected = candidate ?? undefined;
1192
+ return candidate;
1193
+ };
1194
+ const routeFor = async () => {
1195
+ if (selected && options.routes().some((route) => route.id === selected?.id && route.targetUrl === selected?.targetUrl)) {
1196
+ return selected;
1197
+ }
1198
+ return await selectRoute() ?? undefined;
1199
+ };
1200
+ const routedFetchFor = (route) => createSolidLocalRouteFetch({
1201
+ fetch: options.fetch,
1202
+ routes: () => [{
1203
+ canonicalBaseUrl: route.canonicalUrl,
1204
+ localBaseUrl: route.targetUrl
1205
+ }]
1206
+ });
1207
+ return async (input, init) => {
1208
+ const route = await routeFor();
1209
+ if (!route) {
1210
+ return init === undefined ? options.fetch.call(globalThis, input) : options.fetch.call(globalThis, input, init);
1211
+ }
1212
+ try {
1213
+ return await routedFetchFor(route)(input, init);
1214
+ } catch (error) {
1215
+ selected = undefined;
1216
+ await options.refreshRoutes?.();
1217
+ const retryRoute = await selectRoute();
1218
+ if (!retryRoute || retryRoute.id === route.id) {
1219
+ throw error;
1220
+ }
1221
+ return routedFetchFor(retryRoute)(input, init);
1222
+ }
1223
+ };
1224
+ }
1225
+ async function probeWithTimeout(route, probe, timeoutMs) {
1226
+ const controller = new AbortController;
1227
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
1228
+ try {
1229
+ return await Promise.resolve(probe(route, controller.signal));
1230
+ } catch {
1231
+ return false;
1232
+ } finally {
1233
+ clearTimeout(timer);
1234
+ }
1235
+ }
1236
+ async function defaultProbe(route, signal) {
1237
+ if (!route.targetUrl.startsWith("http://") && !route.targetUrl.startsWith("https://")) {
1238
+ return route.health === "healthy";
1239
+ }
1240
+ const response = await fetch(new URL("/.well-known/solid", route.targetUrl), {
1241
+ method: "HEAD",
1242
+ signal
1243
+ });
1244
+ return response.ok || response.status === 401 || response.status === 403;
1245
+ }
@@ -6,3 +6,4 @@ export * from './storage-selection';
6
6
  /** LinX four-phase store (`linx-login`). Xpod uses `xpod.remembered-login.v1` instead. */
7
7
  export * from './login-store';
8
8
  export * from './local-route-fetch';
9
+ export * from './access-route';
@@ -6,3 +6,4 @@ export * from './storage-selection.js';
6
6
  /** LinX four-phase store (`linx-login`). Xpod uses `xpod.remembered-login.v1` instead. */
7
7
  export * from './login-store.js';
8
8
  export * from './local-route-fetch.js';
9
+ export * from './access-route.js';
@@ -42,10 +42,15 @@
42
42
  "types": "./dist/local-route-fetch.d.ts",
43
43
  "import": "./dist/local-route-fetch.js",
44
44
  "require": "./dist/local-route-fetch.cjs"
45
+ },
46
+ "./access-route": {
47
+ "types": "./dist/access-route.d.ts",
48
+ "import": "./dist/access-route.js",
49
+ "require": "./dist/access-route.cjs"
45
50
  }
46
51
  },
47
52
  "scripts": {
48
- "build": "rm -rf dist && tsc -p tsconfig.json && bun ../../scripts/fix-dist-js-imports.mjs dist && bun build src/index.ts --target=node --format=cjs --packages=external --outfile=dist/index.cjs && bun build src/local-route-fetch.ts --target=node --format=cjs --packages=external --outfile=dist/local-route-fetch.cjs",
53
+ "build": "rm -rf dist && tsc -p tsconfig.json && bun ../../scripts/fix-dist-js-imports.mjs dist && bun build src/index.ts --target=node --format=cjs --packages=external --outfile=dist/index.cjs && bun build src/local-route-fetch.ts --target=node --format=cjs --packages=external --outfile=dist/local-route-fetch.cjs && bun build src/access-route.ts --target=node --format=cjs --packages=external --outfile=dist/access-route.cjs",
49
54
  "test": "vitest run test --config ../../vitest.packages.config.mts"
50
55
  },
51
56
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@undefineds.co/xpod",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
4
4
  "description": "Xpod is an extended Community Solid Server, offering rich-feature, production-level Solid Pod and identity management.",
5
5
  "repository": "https://github.com/undefinedsco/xpod",
6
6
  "author": "developer@undefineds.co",
@@ -384,7 +384,7 @@
384
384
  }
385
385
  },
386
386
  "optionalDependencies": {
387
- "@undefineds.co/xpod-darwin-arm64": "0.4.14"
387
+ "@undefineds.co/xpod-darwin-arm64": "0.4.15"
388
388
  },
389
389
  "resolutions": {
390
390
  "@inrupt/solid-client-authn-browser": "^3.1.1",