@undefineds.co/xpod 0.4.9 → 0.4.11

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 (130) hide show
  1. package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +0 -1
  2. package/dist/api/auth/ClientCredentialsAuthenticator.js +0 -3
  3. package/dist/api/auth/index.d.ts +1 -0
  4. package/dist/api/auth/index.js +1 -1
  5. package/dist/identity/drizzle/AccountRoleRepository.d.ts +30 -2
  6. package/dist/identity/drizzle/AccountRoleRepository.js +163 -15
  7. package/dist/identity/drizzle/PodLookupRepository.d.ts +55 -12
  8. package/dist/identity/drizzle/PodLookupRepository.js +225 -243
  9. package/dist/identity/drizzle/db.d.ts +19 -0
  10. package/dist/identity/drizzle/db.js +39 -4
  11. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.js +1 -0
  12. package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +1 -0
  13. package/node_modules/@undefineds.co/ai-connections/dist/controller.js +94 -16
  14. package/node_modules/@undefineds.co/shared-ui/dist/index.d.ts +0 -1
  15. package/node_modules/@undefineds.co/shared-ui/dist/index.js +0 -1
  16. package/node_modules/@undefineds.co/solid-sdk/dist/index.cjs +112 -11
  17. package/node_modules/@undefineds.co/solid-sdk/dist/session.d.ts +2 -0
  18. package/node_modules/@undefineds.co/solid-sdk/dist/session.js +135 -11
  19. package/package.json +2 -2
  20. package/static/app/assets/main.css +1 -1
  21. package/static/app/assets/main.js +12 -13
  22. package/static/auth-callback/assets/{AiConfigContext-DJKvxduw.js → AiConfigContext-DqWRSm7n.js} +1 -1
  23. package/static/auth-callback/assets/{AiConfigPage-DgGmHb18.js → AiConfigPage-C5YYyvzl.js} +1 -1
  24. package/static/auth-callback/assets/{DocumentProcessingPanel-CJmxu8x1.js → DocumentProcessingPanel-DZCiacBz.js} +1 -1
  25. package/static/auth-callback/assets/{IndexLifecyclePanel-byCa7h7L.js → IndexLifecyclePanel-CCLeOljB.js} +1 -1
  26. package/static/auth-callback/assets/{IndexSubjectPanel-CDwT4Yx1.js → IndexSubjectPanel-BbUqop4b.js} +1 -1
  27. package/static/auth-callback/assets/{ModelAssignmentsPanel-CwM3k8w7.js → ModelAssignmentsPanel-jUUTDKuh.js} +1 -1
  28. package/static/auth-callback/assets/ModelsPage-B08zdtff.js +10 -0
  29. package/static/auth-callback/assets/{NetworkPage-DkVgxtNj.js → NetworkPage-Vp2PrMxs.js} +1 -1
  30. package/static/auth-callback/assets/{PaneListHeader-DRI0hjmt.js → PaneListHeader-DY7baSFl.js} +1 -1
  31. package/static/auth-callback/assets/{SearchIndexingPanel-mbppDZFE.js → SearchIndexingPanel-D4YRg1-8.js} +1 -1
  32. package/static/auth-callback/assets/{StatusSubjectPanel-DNsW76aC.js → StatusSubjectPanel-ArIS-TxT.js} +1 -1
  33. package/static/auth-callback/assets/{StatusWorkspace-DGcTbHT5.js → StatusWorkspace-Cp5LQ9Yx.js} +1 -1
  34. package/static/auth-callback/assets/SystemSettingsPage-BDvT1NUz.js +1 -0
  35. package/static/auth-callback/assets/SystemSettingsSubjectPanel-CDqRuC4t.js +33 -0
  36. package/static/auth-callback/assets/{activity-Ck-Mfxxo.js → activity-D1Dsfa_0.js} +1 -1
  37. package/static/auth-callback/assets/admin-C6qCRFiY.js +1 -0
  38. package/static/auth-callback/assets/{auth-callback-DG4Ge40c.js → auth-callback-Cmf4hXVZ.js} +105 -109
  39. package/static/auth-callback/assets/auth-callback-DWMMiFjh.css +1 -0
  40. package/static/auth-callback/assets/{badge-GSSnw1VV.js → badge-CZsHq3Ih.js} +1 -1
  41. package/static/auth-callback/assets/{download-BYyRX-K2.js → download-DU9ntlHw.js} +1 -1
  42. package/static/auth-callback/assets/{external-link-WEC0Ki-0.js → external-link-BfzaaZDx.js} +1 -1
  43. package/static/auth-callback/assets/{index-kju2k4Dp.js → index-DSNnLUbU.js} +4 -4
  44. package/static/auth-callback/assets/{index-DlsMCgo8.js → index-FHhHoKgp.js} +1 -1
  45. package/static/auth-callback/assets/{index-browser-D8YW6coF.js → index-browser-BNFqqC74.js} +1 -1
  46. package/static/auth-callback/assets/{key-round-DJUT5Nh0.js → key-round-BwP3DBWo.js} +1 -1
  47. package/static/auth-callback/assets/{rotate-ccw-CXwfZUKU.js → rotate-ccw-VMwnmyab.js} +1 -1
  48. package/static/auth-callback/assets/settings-projection-CUkyprDQ.js +1 -0
  49. package/static/auth-callback/assets/{skeleton-DWPtNhUB.js → skeleton-CoD55rBT.js} +1 -1
  50. package/static/auth-callback/assets/{waypoints-l1oDnRJ0.js → waypoints-DRo_1Ca7.js} +1 -1
  51. package/static/auth-callback/assets/{workspace-layout-B7iKpK-Q.js → workspace-layout--mcXl3My.js} +1 -1
  52. package/static/auth-callback/auth-callback.html +2 -2
  53. package/static/dashboard/assets/{AiConfigContext-U0cCSY28.js → AiConfigContext-BpPDKSzI.js} +1 -1
  54. package/static/dashboard/assets/{AiConfigPage-CMi0uJdo.js → AiConfigPage-wsqEZDIX.js} +1 -1
  55. package/static/dashboard/assets/{DocumentProcessingPanel-DEZiEpft.js → DocumentProcessingPanel-CDSy3juc.js} +1 -1
  56. package/static/dashboard/assets/{IndexLifecyclePanel-CGZfLUog.js → IndexLifecyclePanel-CVd6vQCX.js} +1 -1
  57. package/static/dashboard/assets/{IndexSubjectPanel-DiDZauHk.js → IndexSubjectPanel-B2cXwl8X.js} +1 -1
  58. package/static/dashboard/assets/{ModelAssignmentsPanel-DaGGZKuf.js → ModelAssignmentsPanel-BeBZhnQd.js} +1 -1
  59. package/static/dashboard/assets/ModelsPage-B6McSCJw.js +25 -0
  60. package/static/dashboard/assets/{NetworkPage-DI5-O7GT.js → NetworkPage-DV2V0qWO.js} +1 -1
  61. package/static/dashboard/assets/{PaneListHeader-4oa7uf59.js → PaneListHeader-D0c1yCFH.js} +1 -1
  62. package/static/dashboard/assets/{SearchIndexingPanel-DoqEwocD.js → SearchIndexingPanel-CGs0OZ12.js} +1 -1
  63. package/static/dashboard/assets/{StatusSubjectPanel-CLzBC5fu.js → StatusSubjectPanel-oPSECj7i.js} +1 -1
  64. package/static/dashboard/assets/{StatusWorkspace-UyKADZoM.js → StatusWorkspace-PdovQPKa.js} +1 -1
  65. package/static/dashboard/assets/SystemSettingsPage-CtNDklkx.js +1 -0
  66. package/static/dashboard/assets/SystemSettingsSubjectPanel-WaE8mTQi.js +33 -0
  67. package/static/dashboard/assets/{activity-CjIoNU4S.js → activity-B9ylSoyr.js} +1 -1
  68. package/static/dashboard/assets/admin-C6qCRFiY.js +1 -0
  69. package/static/dashboard/assets/{badge-dcDZpKh3.js → badge-BNbFbpIb.js} +1 -1
  70. package/static/dashboard/assets/{dashboard-BY8GdnEI.js → dashboard-CD-FFVRD.js} +129 -133
  71. package/static/dashboard/assets/dashboard-DWMMiFjh.css +1 -0
  72. package/static/dashboard/assets/{download-BeYCrc-_.js → download-nOP38LtW.js} +1 -1
  73. package/static/dashboard/assets/{external-link-626-5KVx.js → external-link-Bh2ewN9E.js} +1 -1
  74. package/static/dashboard/assets/{index-CBbk6hx-.js → index-PgS-Bt7X.js} +1 -1
  75. package/static/dashboard/assets/{index-CsxH6Hmz.js → index-QN2QgEwr.js} +4 -4
  76. package/static/dashboard/assets/{key-round-B9IL4Wua.js → key-round-bGmguBLv.js} +1 -1
  77. package/static/dashboard/assets/{rotate-ccw-DM4vCfiI.js → rotate-ccw-D2x__ODW.js} +1 -1
  78. package/static/dashboard/assets/settings-projection-CUkyprDQ.js +1 -0
  79. package/static/dashboard/assets/{skeleton-ChlFAPvP.js → skeleton-VOqft8WS.js} +1 -1
  80. package/static/dashboard/assets/{waypoints-CvpZzf_I.js → waypoints-fgRqm4oH.js} +1 -1
  81. package/static/dashboard/assets/{workspace-layout-Bxv7V-tt.js → workspace-layout-DV4dgeHX.js} +1 -1
  82. package/static/dashboard/dashboard.html +2 -2
  83. package/static/settings/assets/{AiConfigContext-CDVIHLBb.js → AiConfigContext-zKQYdfai.js} +1 -1
  84. package/static/settings/assets/{AiConfigPage-C7qfiiaJ.js → AiConfigPage-Bs1EE3Ei.js} +1 -1
  85. package/static/settings/assets/{DocumentProcessingPanel-B59QTe-V.js → DocumentProcessingPanel-CSFfdOXS.js} +1 -1
  86. package/static/settings/assets/{IndexLifecyclePanel-C-e-3eVi.js → IndexLifecyclePanel-BR9Su49B.js} +1 -1
  87. package/static/settings/assets/{IndexSubjectPanel-B-scRZ1u.js → IndexSubjectPanel-BgbI9c-a.js} +1 -1
  88. package/static/settings/assets/{ModelAssignmentsPanel-Wy8jzVg4.js → ModelAssignmentsPanel-C0BE7nbk.js} +1 -1
  89. package/static/settings/assets/ModelsPage-CU-YQ8_7.js +10 -0
  90. package/static/settings/assets/{NetworkPage-tBB-4Ox8.js → NetworkPage-C8OA9Mtp.js} +1 -1
  91. package/static/settings/assets/{PaneListHeader-DuD6glzn.js → PaneListHeader-COvlPJWj.js} +1 -1
  92. package/static/settings/assets/{SearchIndexingPanel-CiDHc0nx.js → SearchIndexingPanel-Bdd46mIV.js} +1 -1
  93. package/static/settings/assets/{StatusSubjectPanel-OF5cgWwt.js → StatusSubjectPanel-BuLml7qn.js} +1 -1
  94. package/static/settings/assets/{StatusWorkspace-CZ8Oa0gV.js → StatusWorkspace-CoMFRXoV.js} +1 -1
  95. package/static/settings/assets/SystemSettingsPage-DlzmaWV-.js +1 -0
  96. package/static/settings/assets/SystemSettingsSubjectPanel-DExjDo_v.js +33 -0
  97. package/static/settings/assets/{activity-2M1f5eBS.js → activity-TLerXgMs.js} +1 -1
  98. package/static/settings/assets/admin-C6qCRFiY.js +1 -0
  99. package/static/settings/assets/{badge-CRKan0qX.js → badge-Bcs8iWrH.js} +1 -1
  100. package/static/settings/assets/{download-PCHldCdu.js → download-Ct3QmHPt.js} +1 -1
  101. package/static/settings/assets/{external-link-tmKe7eYO.js → external-link-C8KiHbss.js} +1 -1
  102. package/static/settings/assets/{index-Cek02M79.js → index-1Q2cRjZd.js} +1 -1
  103. package/static/settings/assets/{index-BDCNTwWz.js → index-DBO_Vq0P.js} +4 -4
  104. package/static/settings/assets/{index-browser-EVDVm9T5.js → index-browser-CazX1pc2.js} +1 -1
  105. package/static/settings/assets/{key-round-CUGvQCkt.js → key-round-B19YD1GA.js} +1 -1
  106. package/static/settings/assets/{rotate-ccw-BRA3lIHM.js → rotate-ccw-AQm2kMgU.js} +1 -1
  107. package/static/settings/assets/{settings-BdlKrzJ3.js → settings-C-VBtt9r.js} +129 -133
  108. package/static/settings/assets/settings-DWMMiFjh.css +1 -0
  109. package/static/settings/assets/settings-projection-CUkyprDQ.js +1 -0
  110. package/static/settings/assets/{skeleton-CuDeifWb.js → skeleton-DyZ1OBYf.js} +1 -1
  111. package/static/settings/assets/{waypoints-w6LxsFY1.js → waypoints-CC3wq-SU.js} +1 -1
  112. package/static/settings/assets/{workspace-layout-TN-BWmT5.js → workspace-layout-BxXNKgf9.js} +1 -1
  113. package/static/settings/settings.html +2 -2
  114. package/node_modules/@undefineds.co/shared-ui/dist/oidc-consent.d.ts +0 -54
  115. package/node_modules/@undefineds.co/shared-ui/dist/oidc-consent.js +0 -21
  116. package/static/auth-callback/assets/ModelsPage-DapY2Dwf.js +0 -10
  117. package/static/auth-callback/assets/SystemSettingsPage-C6qklJ9k.js +0 -1
  118. package/static/auth-callback/assets/SystemSettingsSubjectPanel-B4VV9xkG.js +0 -31
  119. package/static/auth-callback/assets/admin-BU4m6Ko2.js +0 -1
  120. package/static/auth-callback/assets/auth-callback-C_7MUXJb.css +0 -1
  121. package/static/dashboard/assets/ModelsPage-DZgAOdxO.js +0 -25
  122. package/static/dashboard/assets/SystemSettingsPage-qP0hLHNV.js +0 -1
  123. package/static/dashboard/assets/SystemSettingsSubjectPanel-EaWax9Ot.js +0 -31
  124. package/static/dashboard/assets/admin-BU4m6Ko2.js +0 -1
  125. package/static/dashboard/assets/dashboard-C_7MUXJb.css +0 -1
  126. package/static/settings/assets/ModelsPage-DHNiaodk.js +0 -10
  127. package/static/settings/assets/SystemSettingsPage-DT2tC0F_.js +0 -1
  128. package/static/settings/assets/SystemSettingsSubjectPanel-Bkta1oDw.js +0 -31
  129. package/static/settings/assets/admin-BU4m6Ko2.js +0 -1
  130. package/static/settings/assets/settings-C_7MUXJb.css +0 -1
@@ -36,6 +36,10 @@ exports.executePostgresLockedStatements = executePostgresLockedStatements;
36
36
  exports.ensureCloudClusterTables = ensureCloudClusterTables;
37
37
  exports.toDbTimestamp = toDbTimestamp;
38
38
  exports.fromDbTimestamp = fromDbTimestamp;
39
+ exports.jsonFieldExtract = jsonFieldExtract;
40
+ exports.jsonFieldEquals = jsonFieldEquals;
41
+ exports.jsonFieldIn = jsonFieldIn;
42
+ exports.jsonFieldIsPrefixOf = jsonFieldIsPrefixOf;
39
43
  const pg_1 = require("pg");
40
44
  const node_postgres_1 = require("drizzle-orm/node-postgres");
41
45
  const drizzle_orm_1 = require("drizzle-orm");
@@ -425,6 +429,41 @@ function fromDbTimestamp(value) {
425
429
  }
426
430
  return undefined;
427
431
  }
432
+ function jsonFieldPath(field) {
433
+ return `$."${field.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
434
+ }
435
+ /**
436
+ * `payload` JSON 字段提取表达式,按方言生成(SQLite json_extract / PG ->>)。
437
+ * alias 用于 join 场景下限定来源表。
438
+ */
439
+ function jsonFieldExtract(db, field, alias) {
440
+ const target = alias ? (0, drizzle_orm_1.sql) `${drizzle_orm_1.sql.identifier(alias)}.payload` : (0, drizzle_orm_1.sql) `payload`;
441
+ if (isDatabaseSqlite(db)) {
442
+ return (0, drizzle_orm_1.sql) `json_extract(${target}, ${jsonFieldPath(field)})`;
443
+ }
444
+ return (0, drizzle_orm_1.sql) `${target} ->> ${field}`;
445
+ }
446
+ /**
447
+ * `payload` 字段等于标量值的方言条件。
448
+ */
449
+ function jsonFieldEquals(db, field, value, alias) {
450
+ return (0, drizzle_orm_1.sql) `${jsonFieldExtract(db, field, alias)} = ${value}`;
451
+ }
452
+ /**
453
+ * `payload` 字段落在值集合内的方言条件。values 为空时调用方应跳过查询。
454
+ */
455
+ function jsonFieldIn(db, field, values, alias) {
456
+ const list = drizzle_orm_1.sql.join(values.map((value) => (0, drizzle_orm_1.sql) `${value}`), (0, drizzle_orm_1.sql) `, `);
457
+ return (0, drizzle_orm_1.sql) `${jsonFieldExtract(db, field, alias)} IN (${list})`;
458
+ }
459
+ /**
460
+ * 反向前缀匹配:`value LIKE payload->field || '%'`。
461
+ * 用于"资源 URL 以存储的 baseUrl 为前缀"的下推预筛;存储值中的 LIKE 元字符只会多匹配,
462
+ * 调用方需在 JS 侧用 startsWith 复核,不会漏匹配。
463
+ */
464
+ function jsonFieldIsPrefixOf(db, field, value, alias) {
465
+ return (0, drizzle_orm_1.sql) `${value} LIKE (${jsonFieldExtract(db, field, alias)}) || '%'`;
466
+ }
428
467
  /**
429
468
  * Ensure SQLite tables exist (simple DDL for local/dev mode).
430
469
  */
@@ -520,9 +559,5 @@ async function ensurePostgresTables(pool) {
520
559
  );
521
560
 
522
561
  `);
523
- await migratePostgresColumns(pool);
524
- }
525
- async function migratePostgresColumns(pool) {
526
- void pool;
527
562
  }
528
563
  //# sourceMappingURL=db.js.map
@@ -31,6 +31,7 @@ export function AiConnectionsMain({ controller, renderToaster = true }) {
31
31
  useEffect(() => {
32
32
  if (controller.client)
33
33
  void controller.loadProviders();
34
+ return () => controller.cancelProviderLoads();
34
35
  }, [controller]);
35
36
  if (!controller.client) {
36
37
  return (_jsxs("section", { role: "alert", "aria-label": "AI Connections unavailable", children: [_jsx("h2", { children: "AI Connections \u5C1A\u672A\u5C31\u7EEA" }), _jsx("p", { children: "\u5BBF\u4E3B\u9700\u8981\u5148\u63D0\u4F9B\u5DF2\u767B\u5F55\u7684 WebID \u548C\u53EF\u7528\u7684 Pod\u3002" })] }));
@@ -38,6 +38,7 @@ export interface AiConnectionsController {
38
38
  setSearchQuery(value: string): void;
39
39
  setProviderState(provider: AiConnectionsProvider, state: ProviderProductState): void;
40
40
  loadProviders(): Promise<void>;
41
+ cancelProviderLoads(): void;
41
42
  subscribe(listener: () => void): () => void;
42
43
  }
43
44
  export declare function createAiConnectionsController(host: WebExtensionHost): AiConnectionsController;
@@ -16,18 +16,72 @@ export const AI_CONNECTIONS_PINNED_SECTIONS = [
16
16
  if (PROVIDERS.map((provider) => provider.id).join(',') !== AI_CONNECTIONS_PROVIDERS.join(',')) {
17
17
  throw new Error('AI Connection provider UI is out of sync with the client catalog');
18
18
  }
19
+ class ProviderLoadCancelled extends Error {
20
+ }
19
21
  export function createAiConnectionsController(host) {
20
22
  const sessionSnapshot = host.solid.session.getSnapshot();
21
23
  const pod = host.solid.pod;
22
24
  const readyPod = pod?.status === 'ready' ? pod : undefined;
23
25
  const authenticated = sessionSnapshot.status === 'authenticated'
24
26
  && readyPod !== undefined;
27
+ let providerLoadGeneration = 0;
28
+ let providerLoadPromise;
29
+ let providerLoadOperation;
30
+ const activeProviderLoads = new Set();
31
+ const isCurrentSession = () => {
32
+ const current = host.solid.session.getSnapshot();
33
+ return authenticated && current.status === 'authenticated' && current.webId === sessionSnapshot.webId;
34
+ };
35
+ const cancelProviderLoads = () => {
36
+ providerLoadGeneration += 1;
37
+ providerLoadOperation = undefined;
38
+ providerLoadPromise = undefined;
39
+ for (const guard of activeProviderLoads)
40
+ guard.cancel();
41
+ };
42
+ const beginProviderLoad = () => {
43
+ const generation = providerLoadGeneration;
44
+ let invalidated = !isCurrentSession();
45
+ let released = false;
46
+ let unsubscribe = () => undefined;
47
+ const guard = {
48
+ assertCurrent() {
49
+ if (invalidated || generation !== providerLoadGeneration || !isCurrentSession())
50
+ throw new ProviderLoadCancelled();
51
+ },
52
+ dispose() {
53
+ if (released)
54
+ return;
55
+ released = true;
56
+ activeProviderLoads.delete(guard);
57
+ unsubscribe();
58
+ },
59
+ cancel() {
60
+ invalidated = true;
61
+ guard.dispose();
62
+ },
63
+ };
64
+ activeProviderLoads.add(guard);
65
+ let subscribing = true;
66
+ unsubscribe = host.solid.session.subscribe((current) => {
67
+ // The Solid SDK deduplicates unchanged state except new LOGIN/RESTORED
68
+ // sessions; SESSION_EXTENDED does not notify. Accept a synchronous initial
69
+ // snapshot, then invalidate on every published session transition.
70
+ if (!subscribing || current.status !== 'authenticated' || current.webId !== sessionSnapshot.webId)
71
+ cancelProviderLoads();
72
+ });
73
+ subscribing = false;
74
+ // Also handle a host that synchronously publishes its state on subscribe.
75
+ if (released)
76
+ unsubscribe();
77
+ return guard;
78
+ };
25
79
  const client = authenticated
26
80
  ? createInteractiveAiConnectionsClient(withAccountClientCredentials(createAiConnectionsClient({
27
81
  webId: sessionSnapshot.webId,
28
82
  podBaseUrl: readyPod.current.podUrl,
29
83
  authenticatedFetch: host.solid.session.fetch,
30
- }), host.capabilities.aiClientCredentials), host.capabilities.aiConnectionsPodStore)
84
+ }), host.capabilities.aiClientCredentials), host.capabilities.aiConnectionsPodStore, beginProviderLoad)
31
85
  : null;
32
86
  let selectedSection = 'keys';
33
87
  let selectedProvider = 'openai';
@@ -36,8 +90,6 @@ export function createAiConnectionsController(host) {
36
90
  let providerStates = {};
37
91
  let providerSummaries = {};
38
92
  let providerLoadError;
39
- let providerLoadGeneration = 0;
40
- let providerLoadPromise;
41
93
  const listeners = new Set();
42
94
  const notify = () => listeners.forEach((listener) => listener());
43
95
  const controller = {
@@ -111,19 +163,23 @@ export function createAiConnectionsController(host) {
111
163
  }
112
164
  notify();
113
165
  },
166
+ cancelProviderLoads,
114
167
  async loadProviders() {
115
168
  if (!client)
116
169
  return;
117
170
  if (providerLoadPromise)
118
171
  return providerLoadPromise;
119
- providerLoadPromise = (async () => {
120
- const generation = providerLoadGeneration + 1;
121
- providerLoadGeneration = generation;
172
+ const operation = {};
173
+ providerLoadOperation = operation;
174
+ const generation = ++providerLoadGeneration;
175
+ const load = Promise.resolve().then(async () => {
176
+ if (providerLoadOperation !== operation)
177
+ return;
122
178
  providerLoadError = undefined;
123
179
  notify();
124
180
  try {
125
181
  const summaries = await client.listProviders();
126
- if (generation !== providerLoadGeneration)
182
+ if (generation !== providerLoadGeneration || !isCurrentSession())
127
183
  return;
128
184
  providerSummaries = Object.fromEntries(summaries.map((summary) => [summary.id, summary]));
129
185
  providerStates = Object.fromEntries(PROVIDERS.map((provider) => [
@@ -133,16 +189,20 @@ export function createAiConnectionsController(host) {
133
189
  notify();
134
190
  }
135
191
  catch (error) {
136
- if (generation !== providerLoadGeneration)
192
+ if (error instanceof ProviderLoadCancelled || generation !== providerLoadGeneration || !isCurrentSession())
137
193
  return;
138
194
  providerLoadError = errorMessage(error);
139
195
  notify();
140
196
  }
141
197
  finally {
142
- providerLoadPromise = undefined;
198
+ if (providerLoadOperation === operation) {
199
+ providerLoadOperation = undefined;
200
+ providerLoadPromise = undefined;
201
+ }
143
202
  }
144
- })();
145
- return providerLoadPromise;
203
+ });
204
+ providerLoadPromise = load;
205
+ return load;
146
206
  },
147
207
  subscribe(listener) {
148
208
  listeners.add(listener);
@@ -195,9 +255,29 @@ function withAccountClientCredentials(client, credentials) {
195
255
  },
196
256
  };
197
257
  }
198
- function createInteractiveAiConnectionsClient(operationsClient, podStore) {
258
+ function createInteractiveAiConnectionsClient(operationsClient, podStore, beginProviderLoad) {
259
+ const listProviders = async () => {
260
+ const guard = beginProviderLoad();
261
+ try {
262
+ guard.assertCurrent();
263
+ const providers = podStore
264
+ ? await podStore.listProviders()
265
+ : await operationsClient.listProviders();
266
+ guard.assertCurrent();
267
+ if (!podStore)
268
+ return providers;
269
+ const methods = operationsClient.listAuthorizationMethods
270
+ ? await operationsClient.listAuthorizationMethods()
271
+ : [];
272
+ guard.assertCurrent();
273
+ return mergeAuthorizationMethodsIntoProviders(providers, methods);
274
+ }
275
+ finally {
276
+ guard.dispose();
277
+ }
278
+ };
199
279
  if (!podStore)
200
- return operationsClient;
280
+ return { ...operationsClient, listProviders };
201
281
  const refreshTasks = new Map();
202
282
  const refreshOAuthCredential = podStore.readCredentialSecret && podStore.updateOAuthCredential
203
283
  ? (provider, credentialId) => {
@@ -249,9 +329,7 @@ function createInteractiveAiConnectionsClient(operationsClient, podStore) {
249
329
  };
250
330
  return {
251
331
  ...operationsClient,
252
- listProviders: async () => mergeAuthorizationMethodsIntoProviders(await podStore.listProviders(), operationsClient.listAuthorizationMethods
253
- ? await operationsClient.listAuthorizationMethods()
254
- : []),
332
+ listProviders,
255
333
  listModels: podStore.listModels
256
334
  ? async () => podStore.listModels()
257
335
  : operationsClient.listModels,
@@ -17,7 +17,6 @@ export { LocalReachabilitySummary, } from './login/LocalReachabilitySummary';
17
17
  export { getProviderActionLabel, getProviderDisplayLabel, getProviderInfoText, getProviderSourceLabel, getProviderStatusBadge, getProviderSubtitle, type ProviderStatusBadge, } from './login/presentation';
18
18
  export { isLocalLoginProvider, isLocalLoginProviderSource, resolveLoginProviderSource, } from './login/provider-model';
19
19
  export type { AuthWindowStatus, ConnectingProviderInfo, LocalLoginProviderSource, LocalLoginStatus, LocalOnboardingCapabilities, LocalOnboardingConnectivity, LocalOnboardingProgress, LocalOnboardingRouteKind, LocalOnboardingRouteProbe, LocalOnboardingSnapshot, LocalOnboardingState, LocalOnboardingTunnel, LocalPodRuntime, LocalSpaceKind, LoginEndpoint, LoginEndpointKind, LoginModalProps, LoginProviderOption, LoginProviderSource, LoginState, ProviderOption, StorageConflict, StoredAccount, } from './login/types';
20
- export * from './oidc-consent';
21
20
  export * from './scroll-area';
22
21
  export * from './separator';
23
22
  export * from './skeleton';
@@ -16,7 +16,6 @@ export { LoginView, } from './login/LoginView.js';
16
16
  export { LocalReachabilitySummary, } from './login/LocalReachabilitySummary.js';
17
17
  export { getProviderActionLabel, getProviderDisplayLabel, getProviderInfoText, getProviderSourceLabel, getProviderStatusBadge, getProviderSubtitle, } from './login/presentation.js';
18
18
  export { isLocalLoginProvider, isLocalLoginProviderSource, resolveLoginProviderSource, } from './login/provider-model.js';
19
- export * from './oidc-consent.js';
20
19
  export * from './scroll-area.js';
21
20
  export * from './separator.js';
22
21
  export * from './skeleton.js';
@@ -117,11 +117,24 @@ function createSolidSessionRuntime(options = {}) {
117
117
  let isInitializing = false;
118
118
  let initializationErrorSnapshot;
119
119
  let disposed = false;
120
+ let authenticationGeneration = 0;
121
+ let authenticationUnavailable = false;
122
+ let authenticationOperations = 0;
123
+ let identityOperationGeneration = 0;
124
+ let pendingIdentity;
125
+ const invalidateAuthentication = () => {
126
+ authenticationGeneration += 1;
127
+ authenticationUnavailable = true;
128
+ };
120
129
  const publish = (nextSnapshot) => {
121
- snapshot = nextSnapshot;
122
- if (disposed) {
130
+ if (disposed)
123
131
  return snapshot;
132
+ if (nextSnapshot.status !== "authenticated" || snapshot.status !== "authenticated" || snapshot.webId !== nextSnapshot.webId) {
133
+ invalidateAuthentication();
124
134
  }
135
+ if (nextSnapshot.status === "authenticated")
136
+ authenticationUnavailable = false;
137
+ snapshot = nextSnapshot;
125
138
  if (areSnapshotsEqual(lastNotifiedSnapshot, snapshot)) {
126
139
  return snapshot;
127
140
  }
@@ -131,10 +144,28 @@ function createSolidSessionRuntime(options = {}) {
131
144
  }
132
145
  return snapshot;
133
146
  };
134
- const publishSessionInfo = () => publish(snapshotFromSessionInfo(session.info));
135
- const publishAnonymous = () => publish({ status: "anonymous" });
136
- const publishSessionExpired = () => publish(snapshotFromSessionExpired(session.info));
147
+ const publishSessionInfo = () => {
148
+ if (pendingIdentity) {
149
+ if (pendingIdentity.generation !== identityOperationGeneration)
150
+ return snapshot;
151
+ if (pendingIdentity.eventWebId && pendingIdentity.eventWebId !== session.info.webId)
152
+ return snapshot;
153
+ pendingIdentity.eventWebId = session.info.webId;
154
+ }
155
+ invalidateAuthentication();
156
+ lastNotifiedSnapshot = undefined;
157
+ return publish(snapshotFromSessionInfo(session.info));
158
+ };
159
+ const publishAnonymous = () => {
160
+ identityOperationGeneration += 1;
161
+ return publish({ status: "anonymous" });
162
+ };
163
+ const publishSessionExpired = () => {
164
+ identityOperationGeneration += 1;
165
+ return publish(snapshotFromSessionExpired(session.info));
166
+ };
137
167
  const publishSessionError = (code, description) => {
168
+ identityOperationGeneration += 1;
138
169
  const errorSnapshot = snapshotFromSessionError(description ?? code ?? "Solid session error", session.info);
139
170
  if (isInitializing) {
140
171
  initializationErrorSnapshot = errorSnapshot;
@@ -149,6 +180,29 @@ function createSolidSessionRuntime(options = {}) {
149
180
  errorEvents.on(import_solid_client_authn_browser.EVENTS.ERROR, publishSessionError);
150
181
  return {
151
182
  fetch: session.fetch,
183
+ createAuthenticatedFetch(webId) {
184
+ const generation = authenticationGeneration;
185
+ const assertCurrent = () => {
186
+ if (disposed || authenticationOperations > 0 || authenticationUnavailable || generation !== authenticationGeneration || snapshot.status !== "authenticated" || snapshot.webId !== webId || !session.info.isLoggedIn || session.info.webId !== webId) {
187
+ const error = new Error("Authenticated session is no longer current");
188
+ error.name = "AbortError";
189
+ throw error;
190
+ }
191
+ };
192
+ return async (input, init) => {
193
+ assertCurrent();
194
+ const response = await (init === undefined ? session.fetch(input) : session.fetch(input, init));
195
+ try {
196
+ assertCurrent();
197
+ } catch (error) {
198
+ response.body?.cancel().catch(() => {
199
+ return;
200
+ });
201
+ throw error;
202
+ }
203
+ return response;
204
+ };
205
+ },
152
206
  getSnapshot() {
153
207
  return snapshot;
154
208
  },
@@ -159,20 +213,29 @@ function createSolidSessionRuntime(options = {}) {
159
213
  if (initialized) {
160
214
  return Promise.resolve(snapshot);
161
215
  }
216
+ if (disposed)
217
+ return Promise.resolve(snapshot);
218
+ const operation = { generation: ++identityOperationGeneration, eventWebId: undefined };
219
+ pendingIdentity = operation;
220
+ const isCurrentOperation = (info) => !disposed && operation.generation === identityOperationGeneration && (!info?.isLoggedIn || !operation.eventWebId || operation.eventWebId === info.webId);
162
221
  isInitializing = true;
163
222
  initializationErrorSnapshot = undefined;
164
223
  publish({ status: "initializing" });
165
224
  const nextInitialization = session.handleIncomingRedirect({
166
225
  restorePreviousSession: options2.restorePreviousSession ?? true
167
226
  }).then((info) => {
227
+ if (!isCurrentOperation(info ?? session.info))
228
+ return snapshot;
168
229
  const nextSnapshot = snapshotFromSessionInfo(info ?? session.info);
169
230
  if (nextSnapshot.status === "anonymous" && initializationErrorSnapshot?.status === "error") {
170
231
  return initializationErrorSnapshot;
171
232
  }
172
233
  initialized = true;
173
234
  return publish(nextSnapshot);
174
- }).catch((error) => publish(snapshotFromSessionError(error, session.info))).finally(() => {
235
+ }).catch((error) => isCurrentOperation() ? publish(snapshotFromSessionError(error, session.info)) : snapshot).finally(() => {
175
236
  if (initialization === nextInitialization) {
237
+ if (pendingIdentity === operation)
238
+ pendingIdentity = undefined;
176
239
  initialization = undefined;
177
240
  isInitializing = false;
178
241
  initializationErrorSnapshot = undefined;
@@ -185,18 +248,27 @@ function createSolidSessionRuntime(options = {}) {
185
248
  if (initialization) {
186
249
  return initialization;
187
250
  }
251
+ if (disposed)
252
+ return Promise.resolve(snapshot);
253
+ const operation = { generation: ++identityOperationGeneration, eventWebId: undefined };
254
+ pendingIdentity = operation;
255
+ const isCurrentOperation = (info) => !disposed && operation.generation === identityOperationGeneration && (!info?.isLoggedIn || !operation.eventWebId || operation.eventWebId === info.webId);
188
256
  isInitializing = true;
189
257
  initializationErrorSnapshot = undefined;
190
258
  publish({ status: "initializing" });
191
259
  const nextInitialization = session.handleIncomingRedirect(url).then((info) => {
260
+ if (!isCurrentOperation(info ?? session.info))
261
+ return snapshot;
192
262
  const nextSnapshot = snapshotFromSessionInfo(info ?? session.info);
193
263
  if (nextSnapshot.status === "anonymous" && initializationErrorSnapshot?.status === "error") {
194
264
  return initializationErrorSnapshot;
195
265
  }
196
266
  initialized = true;
197
267
  return publish(nextSnapshot);
198
- }).catch((error) => publish(snapshotFromSessionError(error, session.info))).finally(() => {
268
+ }).catch((error) => isCurrentOperation() ? publish(snapshotFromSessionError(error, session.info)) : snapshot).finally(() => {
199
269
  if (initialization === nextInitialization) {
270
+ if (pendingIdentity === operation)
271
+ pendingIdentity = undefined;
200
272
  initialization = undefined;
201
273
  isInitializing = false;
202
274
  initializationErrorSnapshot = undefined;
@@ -205,12 +277,39 @@ function createSolidSessionRuntime(options = {}) {
205
277
  initialization = nextInitialization;
206
278
  return nextInitialization;
207
279
  },
208
- login(options2) {
209
- return session.login(options2);
280
+ async login(options2) {
281
+ identityOperationGeneration += 1;
282
+ invalidateAuthentication();
283
+ authenticationOperations += 1;
284
+ try {
285
+ if (initialization) {
286
+ const error = new Error("Previous login is still completing. Reload before reconnecting.");
287
+ error.name = "SolidSessionPendingError";
288
+ throw error;
289
+ }
290
+ if (disposed)
291
+ throw new Error("Solid session runtime is disposed");
292
+ await session.login(options2);
293
+ } catch (error) {
294
+ publish(snapshotFromSessionError(error, session.info));
295
+ throw error;
296
+ } finally {
297
+ authenticationOperations -= 1;
298
+ }
210
299
  },
211
300
  async logout(options2) {
212
- await session.logout(options2);
213
- publish({ status: "anonymous" });
301
+ identityOperationGeneration += 1;
302
+ invalidateAuthentication();
303
+ authenticationOperations += 1;
304
+ try {
305
+ await session.logout(options2);
306
+ publish({ status: "anonymous" });
307
+ } catch (error) {
308
+ publish(snapshotFromSessionError(error, session.info));
309
+ throw error;
310
+ } finally {
311
+ authenticationOperations -= 1;
312
+ }
214
313
  },
215
314
  subscribe(listener) {
216
315
  listeners.add(listener);
@@ -219,6 +318,8 @@ function createSolidSessionRuntime(options = {}) {
219
318
  };
220
319
  },
221
320
  dispose() {
321
+ identityOperationGeneration += 1;
322
+ invalidateAuthentication();
222
323
  disposed = true;
223
324
  listeners.clear();
224
325
  session.events.off(import_solid_client_authn_browser.EVENTS.LOGIN, publishSessionInfo);
@@ -31,6 +31,8 @@ export type SolidSessionAdapter = {
31
31
  };
32
32
  export type SolidSessionRuntime = {
33
33
  readonly fetch: typeof fetch;
34
+ /** A revocable capability for this exact WebID and authenticated session. */
35
+ createAuthenticatedFetch(webId: string): typeof fetch;
34
36
  getSnapshot(): SolidSessionSnapshot;
35
37
  initialize(options?: {
36
38
  restorePreviousSession?: boolean;