@undefineds.co/xpod 0.4.9 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/identity/drizzle/PodLookupRepository.d.ts +5 -16
- package/dist/identity/drizzle/PodLookupRepository.js +40 -201
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.js +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/controller.js +94 -16
- package/node_modules/@undefineds.co/solid-sdk/dist/index.cjs +112 -11
- package/node_modules/@undefineds.co/solid-sdk/dist/session.d.ts +2 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/session.js +135 -11
- package/package.json +2 -2
- package/static/app/assets/main.js +12 -12
- package/static/auth-callback/assets/{AiConfigContext-DJKvxduw.js → AiConfigContext-C81deMM9.js} +1 -1
- package/static/auth-callback/assets/{AiConfigPage-DgGmHb18.js → AiConfigPage-evWI8T56.js} +1 -1
- package/static/auth-callback/assets/{DocumentProcessingPanel-CJmxu8x1.js → DocumentProcessingPanel-B9GrDEMe.js} +1 -1
- package/static/auth-callback/assets/{IndexLifecyclePanel-byCa7h7L.js → IndexLifecyclePanel-qCo8ABQZ.js} +1 -1
- package/static/auth-callback/assets/{IndexSubjectPanel-CDwT4Yx1.js → IndexSubjectPanel-C4YG0whI.js} +1 -1
- package/static/auth-callback/assets/{ModelAssignmentsPanel-CwM3k8w7.js → ModelAssignmentsPanel-DG7Lxh3t.js} +1 -1
- package/static/auth-callback/assets/ModelsPage-CgEuqE6p.js +10 -0
- package/static/auth-callback/assets/{NetworkPage-DkVgxtNj.js → NetworkPage-Cbdn_P1e.js} +1 -1
- package/static/auth-callback/assets/{PaneListHeader-DRI0hjmt.js → PaneListHeader-jy70Q00L.js} +1 -1
- package/static/auth-callback/assets/{SearchIndexingPanel-mbppDZFE.js → SearchIndexingPanel-ByU3u7kp.js} +1 -1
- package/static/auth-callback/assets/{StatusSubjectPanel-DNsW76aC.js → StatusSubjectPanel-hfjoB5aS.js} +1 -1
- package/static/auth-callback/assets/{StatusWorkspace-DGcTbHT5.js → StatusWorkspace-BZd617sV.js} +1 -1
- package/static/auth-callback/assets/{SystemSettingsPage-C6qklJ9k.js → SystemSettingsPage-BirzWAQM.js} +1 -1
- package/static/auth-callback/assets/{SystemSettingsSubjectPanel-B4VV9xkG.js → SystemSettingsSubjectPanel-CmsVWNCx.js} +1 -1
- package/static/auth-callback/assets/{activity-Ck-Mfxxo.js → activity-BcsZZxxw.js} +1 -1
- package/static/auth-callback/assets/{auth-callback-DG4Ge40c.js → auth-callback-atKg_oig.js} +124 -124
- package/static/auth-callback/assets/{badge-GSSnw1VV.js → badge-BWbRHThQ.js} +1 -1
- package/static/auth-callback/assets/{download-BYyRX-K2.js → download-BmvG1Vya.js} +1 -1
- package/static/auth-callback/assets/{external-link-WEC0Ki-0.js → external-link-DZ34d3g3.js} +1 -1
- package/static/auth-callback/assets/{index-kju2k4Dp.js → index-BpV1hsqs.js} +1 -1
- package/static/auth-callback/assets/{index-DlsMCgo8.js → index-D9oE6FXK.js} +1 -1
- package/static/auth-callback/assets/{index-browser-D8YW6coF.js → index-browser-B9Z7GU-T.js} +1 -1
- package/static/auth-callback/assets/{key-round-DJUT5Nh0.js → key-round-tjP76Se-.js} +1 -1
- package/static/auth-callback/assets/{rotate-ccw-CXwfZUKU.js → rotate-ccw-C_L5ZLx1.js} +1 -1
- package/static/auth-callback/assets/{skeleton-DWPtNhUB.js → skeleton-C7tGDQIs.js} +1 -1
- package/static/auth-callback/assets/{waypoints-l1oDnRJ0.js → waypoints-VMNQlWdU.js} +1 -1
- package/static/auth-callback/assets/{workspace-layout-B7iKpK-Q.js → workspace-layout-SAp6BtYK.js} +1 -1
- package/static/auth-callback/auth-callback.html +1 -1
- package/static/dashboard/assets/{AiConfigContext-U0cCSY28.js → AiConfigContext-De-OtAWo.js} +1 -1
- package/static/dashboard/assets/{AiConfigPage-CMi0uJdo.js → AiConfigPage-macP4Mop.js} +1 -1
- package/static/dashboard/assets/{DocumentProcessingPanel-DEZiEpft.js → DocumentProcessingPanel-C5ObvCCl.js} +1 -1
- package/static/dashboard/assets/{IndexLifecyclePanel-CGZfLUog.js → IndexLifecyclePanel-xVNa2l40.js} +1 -1
- package/static/dashboard/assets/{IndexSubjectPanel-DiDZauHk.js → IndexSubjectPanel-DQFPP6-p.js} +1 -1
- package/static/dashboard/assets/{ModelAssignmentsPanel-DaGGZKuf.js → ModelAssignmentsPanel-DMOATFpQ.js} +1 -1
- package/static/dashboard/assets/ModelsPage-91UbnETA.js +25 -0
- package/static/dashboard/assets/{NetworkPage-DI5-O7GT.js → NetworkPage-CHQ3YxIB.js} +1 -1
- package/static/dashboard/assets/{PaneListHeader-4oa7uf59.js → PaneListHeader-DsXemAdR.js} +1 -1
- package/static/dashboard/assets/{SearchIndexingPanel-DoqEwocD.js → SearchIndexingPanel-Dh70PNFZ.js} +1 -1
- package/static/dashboard/assets/{StatusSubjectPanel-CLzBC5fu.js → StatusSubjectPanel-D3PGEYT4.js} +1 -1
- package/static/dashboard/assets/{StatusWorkspace-UyKADZoM.js → StatusWorkspace-BcpePYUW.js} +1 -1
- package/static/dashboard/assets/{SystemSettingsPage-qP0hLHNV.js → SystemSettingsPage-BiN5GcKx.js} +1 -1
- package/static/dashboard/assets/{SystemSettingsSubjectPanel-EaWax9Ot.js → SystemSettingsSubjectPanel-B9QrSiFp.js} +1 -1
- package/static/dashboard/assets/{activity-CjIoNU4S.js → activity-j5zrznu-.js} +1 -1
- package/static/dashboard/assets/{badge-dcDZpKh3.js → badge-CzeBdG7h.js} +1 -1
- package/static/dashboard/assets/{dashboard-BY8GdnEI.js → dashboard-D-xANHk1.js} +103 -103
- package/static/dashboard/assets/{download-BeYCrc-_.js → download-IcNdEZWi.js} +1 -1
- package/static/dashboard/assets/{external-link-626-5KVx.js → external-link-DxBxK8Vu.js} +1 -1
- package/static/dashboard/assets/{index-CBbk6hx-.js → index-FWZ-s52O.js} +1 -1
- package/static/dashboard/assets/{index-CsxH6Hmz.js → index-ZiQZ96oC.js} +1 -1
- package/static/dashboard/assets/{key-round-B9IL4Wua.js → key-round-D1K7warM.js} +1 -1
- package/static/dashboard/assets/{rotate-ccw-DM4vCfiI.js → rotate-ccw-CcN1N6p9.js} +1 -1
- package/static/dashboard/assets/{skeleton-ChlFAPvP.js → skeleton-CKnmJMUH.js} +1 -1
- package/static/dashboard/assets/{waypoints-CvpZzf_I.js → waypoints-DuZPO_wu.js} +1 -1
- package/static/dashboard/assets/{workspace-layout-Bxv7V-tt.js → workspace-layout-BUj3wZ_m.js} +1 -1
- package/static/dashboard/dashboard.html +1 -1
- package/static/settings/assets/{AiConfigContext-CDVIHLBb.js → AiConfigContext-C0cQMPfG.js} +1 -1
- package/static/settings/assets/{AiConfigPage-C7qfiiaJ.js → AiConfigPage-CPhB4iFb.js} +1 -1
- package/static/settings/assets/{DocumentProcessingPanel-B59QTe-V.js → DocumentProcessingPanel-sYI7OpuH.js} +1 -1
- package/static/settings/assets/{IndexLifecyclePanel-C-e-3eVi.js → IndexLifecyclePanel-yj8iyiT8.js} +1 -1
- package/static/settings/assets/{IndexSubjectPanel-B-scRZ1u.js → IndexSubjectPanel-73sM16Ma.js} +1 -1
- package/static/settings/assets/{ModelAssignmentsPanel-Wy8jzVg4.js → ModelAssignmentsPanel-BFIgUX5v.js} +1 -1
- package/static/settings/assets/ModelsPage-BdppXl7E.js +10 -0
- package/static/settings/assets/{NetworkPage-tBB-4Ox8.js → NetworkPage-i6j-M2RY.js} +1 -1
- package/static/settings/assets/{PaneListHeader-DuD6glzn.js → PaneListHeader-DpqC0jd5.js} +1 -1
- package/static/settings/assets/{SearchIndexingPanel-CiDHc0nx.js → SearchIndexingPanel-BlPnrkT3.js} +1 -1
- package/static/settings/assets/{StatusSubjectPanel-OF5cgWwt.js → StatusSubjectPanel-Dv4q3PGh.js} +1 -1
- package/static/settings/assets/{StatusWorkspace-CZ8Oa0gV.js → StatusWorkspace-CKD_Rzdr.js} +1 -1
- package/static/settings/assets/{SystemSettingsPage-DT2tC0F_.js → SystemSettingsPage-CiOm2NzV.js} +1 -1
- package/static/settings/assets/{SystemSettingsSubjectPanel-Bkta1oDw.js → SystemSettingsSubjectPanel-Cd-GECon.js} +1 -1
- package/static/settings/assets/{activity-2M1f5eBS.js → activity-DaFkk8f3.js} +1 -1
- package/static/settings/assets/{badge-CRKan0qX.js → badge-d5SHdc-x.js} +1 -1
- package/static/settings/assets/{download-PCHldCdu.js → download-CpkwomDr.js} +1 -1
- package/static/settings/assets/{external-link-tmKe7eYO.js → external-link-_4YT42Mf.js} +1 -1
- package/static/settings/assets/{index-Cek02M79.js → index-BFi9yn7_.js} +1 -1
- package/static/settings/assets/{index-BDCNTwWz.js → index-BGgEhIUy.js} +1 -1
- package/static/settings/assets/{index-browser-EVDVm9T5.js → index-browser-B06xjBdZ.js} +1 -1
- package/static/settings/assets/{key-round-CUGvQCkt.js → key-round-Dlqmp1Kq.js} +1 -1
- package/static/settings/assets/{rotate-ccw-BRA3lIHM.js → rotate-ccw-TAY65xWT.js} +1 -1
- package/static/settings/assets/{settings-BdlKrzJ3.js → settings-ExBXGgbj.js} +103 -103
- package/static/settings/assets/{skeleton-CuDeifWb.js → skeleton-DKIWjNr9.js} +1 -1
- package/static/settings/assets/{waypoints-w6LxsFY1.js → waypoints-9erLIxyR.js} +1 -1
- package/static/settings/assets/{workspace-layout-TN-BWmT5.js → workspace-layout-CYXfYIa3.js} +1 -1
- package/static/settings/settings.html +1 -1
- package/static/auth-callback/assets/ModelsPage-DapY2Dwf.js +0 -10
- package/static/dashboard/assets/ModelsPage-DZgAOdxO.js +0 -25
- package/static/settings/assets/ModelsPage-DHNiaodk.js +0 -10
|
@@ -29,15 +29,14 @@ export declare class PodLookupRepository {
|
|
|
29
29
|
*/
|
|
30
30
|
findById(podId: string): Promise<PodLookupResult | undefined>;
|
|
31
31
|
/**
|
|
32
|
-
* Find Pod by
|
|
32
|
+
* Find Pod by an explicitly recorded owner WebID URL.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
* the
|
|
36
|
-
* not have to match the storage base URL.
|
|
34
|
+
* Account WebID links are not ownership. IdP/SP split deployments still use
|
|
35
|
+
* the complete recorded WebID, independently of the storage base URL.
|
|
37
36
|
*/
|
|
38
37
|
findByWebId(webId: string): Promise<PodLookupResult | undefined>;
|
|
39
38
|
/**
|
|
40
|
-
* Find all Pods
|
|
39
|
+
* Find all Pods explicitly owned by a WebID.
|
|
41
40
|
*
|
|
42
41
|
* A Cloud WebID can legitimately back both a Cloud Pod and a Local SP Pod.
|
|
43
42
|
* Callers that are scoped to a storage provider must inspect all candidates
|
|
@@ -45,7 +44,7 @@ export declare class PodLookupRepository {
|
|
|
45
44
|
*/
|
|
46
45
|
findAllByWebId(webId: string): Promise<PodLookupResult[]>;
|
|
47
46
|
/**
|
|
48
|
-
* Find Pods by
|
|
47
|
+
* Find Pods by explicit owner WebID URLs in one scan.
|
|
49
48
|
*/
|
|
50
49
|
findByWebIds(webIds: string[]): Promise<PodLookupResult[]>;
|
|
51
50
|
/**
|
|
@@ -62,16 +61,6 @@ export declare class PodLookupRepository {
|
|
|
62
61
|
private getAllPods;
|
|
63
62
|
private getAccountRowsFromKv;
|
|
64
63
|
private getNodeAssignments;
|
|
65
|
-
/**
|
|
66
|
-
* Fast path for CSS WrappedIndexedStorage. WebID indexes point to the root
|
|
67
|
-
* account id, so a single indexed key plus account data row can resolve the
|
|
68
|
-
* profile without scanning all account records.
|
|
69
|
-
*/
|
|
70
|
-
private findByWebIdIndex;
|
|
71
|
-
private readAccountData;
|
|
72
|
-
private readStringArrayFromKv;
|
|
73
|
-
private readKvValue;
|
|
74
|
-
private extractPodsFromAccountData;
|
|
75
64
|
/**
|
|
76
65
|
* DrizzleIndexedStorage stores CSS identity facts as typed rows in
|
|
77
66
|
* identity_store; this is the canonical clustered identity source.
|
|
@@ -39,17 +39,16 @@ class PodLookupRepository {
|
|
|
39
39
|
return pods.find((p) => p.podId === podId);
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* Find Pod by
|
|
42
|
+
* Find Pod by an explicitly recorded owner WebID URL.
|
|
43
43
|
*
|
|
44
|
-
*
|
|
45
|
-
* the
|
|
46
|
-
* not have to match the storage base URL.
|
|
44
|
+
* Account WebID links are not ownership. IdP/SP split deployments still use
|
|
45
|
+
* the complete recorded WebID, independently of the storage base URL.
|
|
47
46
|
*/
|
|
48
47
|
async findByWebId(webId) {
|
|
49
48
|
return (await this.findAllByWebId(webId))[0];
|
|
50
49
|
}
|
|
51
50
|
/**
|
|
52
|
-
* Find all Pods
|
|
51
|
+
* Find all Pods explicitly owned by a WebID.
|
|
53
52
|
*
|
|
54
53
|
* A Cloud WebID can legitimately back both a Cloud Pod and a Local SP Pod.
|
|
55
54
|
* Callers that are scoped to a storage provider must inspect all candidates
|
|
@@ -71,19 +70,10 @@ class PodLookupRepository {
|
|
|
71
70
|
});
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
|
-
|
|
75
|
-
return results;
|
|
76
|
-
}
|
|
77
|
-
const indexed = await this.findByWebIdIndex(exact);
|
|
78
|
-
if (!indexed) {
|
|
79
|
-
return [];
|
|
80
|
-
}
|
|
81
|
-
return [{
|
|
82
|
-
...indexed,
|
|
83
|
-
}];
|
|
73
|
+
return results;
|
|
84
74
|
}
|
|
85
75
|
/**
|
|
86
|
-
* Find Pods by
|
|
76
|
+
* Find Pods by explicit owner WebID URLs in one scan.
|
|
87
77
|
*/
|
|
88
78
|
async findByWebIds(webIds) {
|
|
89
79
|
const exactTargets = new Set(webIds.map(exactWebId).filter((value) => Boolean(value)));
|
|
@@ -105,18 +95,6 @@ class PodLookupRepository {
|
|
|
105
95
|
webId: matchedWebId,
|
|
106
96
|
});
|
|
107
97
|
}
|
|
108
|
-
if (results.length < exactTargets.size) {
|
|
109
|
-
const seen = new Set(results.map((result) => exactWebId(result.webId)).filter(Boolean));
|
|
110
|
-
for (const exact of exactTargets) {
|
|
111
|
-
if (seen.has(exact)) {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
const indexed = await this.findByWebIdIndex(exact);
|
|
115
|
-
if (indexed) {
|
|
116
|
-
results.push(indexed);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
98
|
return results;
|
|
121
99
|
}
|
|
122
100
|
/**
|
|
@@ -150,18 +128,17 @@ class PodLookupRepository {
|
|
|
150
128
|
}
|
|
151
129
|
const data = unwrapStoredValue(typeof row.value === 'string' ? JSON.parse(row.value) : row.value);
|
|
152
130
|
const podMap = data['**pod**'] || data.pod || {};
|
|
153
|
-
const webIds = extractAccountWebIds(data);
|
|
154
131
|
for (const [podId, podData] of Object.entries(podMap)) {
|
|
155
132
|
const pod = podData;
|
|
156
133
|
if (pod.baseUrl && typeof pod.baseUrl === 'string') {
|
|
157
134
|
const storageUrl = stringValue(pod.storageUrl) ?? stringValue(pod.storage);
|
|
158
|
-
const podWebIds =
|
|
135
|
+
const podWebIds = dedupeStrings(explicitPodWebIds(pod));
|
|
159
136
|
pods.push({
|
|
160
137
|
podId,
|
|
161
138
|
accountId,
|
|
162
139
|
baseUrl: pod.baseUrl,
|
|
163
140
|
storageUrl,
|
|
164
|
-
webId:
|
|
141
|
+
webId: podWebIds[0],
|
|
165
142
|
...webIdsProperty(podWebIds),
|
|
166
143
|
nodeId: typeof pod.nodeId === 'string' ? pod.nodeId : findNodeIdForPod(nodeAssignments, [storageUrl, pod.baseUrl]),
|
|
167
144
|
edgeNodeId: typeof pod.edgeNodeId === 'string' ? pod.edgeNodeId : undefined,
|
|
@@ -173,10 +150,13 @@ class PodLookupRepository {
|
|
|
173
150
|
// Skip malformed entries.
|
|
174
151
|
}
|
|
175
152
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
153
|
+
const indexed = await this.getPodsFromIndexedStore(nodeAssignments);
|
|
154
|
+
// Presence in the canonical table shadows the whole legacy record, even
|
|
155
|
+
// when the canonical payload is incomplete. Never resurrect old owners.
|
|
156
|
+
return [...new Map([
|
|
157
|
+
...pods.filter((pod) => !indexed.podIds.has(pod.podId)),
|
|
158
|
+
...indexed.pods,
|
|
159
|
+
].map((pod) => [pod.podId, pod])).values()];
|
|
180
160
|
}
|
|
181
161
|
async getAccountRowsFromKv() {
|
|
182
162
|
const kvTableId = drizzle_orm_1.sql.identifier(this.kvTableName);
|
|
@@ -213,90 +193,6 @@ class PodLookupRepository {
|
|
|
213
193
|
return [];
|
|
214
194
|
}
|
|
215
195
|
}
|
|
216
|
-
/**
|
|
217
|
-
* Fast path for CSS WrappedIndexedStorage. WebID indexes point to the root
|
|
218
|
-
* account id, so a single indexed key plus account data row can resolve the
|
|
219
|
-
* profile without scanning all account records.
|
|
220
|
-
*/
|
|
221
|
-
async findByWebIdIndex(webId) {
|
|
222
|
-
const accountIds = await this.readStringArrayFromKv(`accounts/index/webIdLink/webId/${encodeURIComponent(webId)}`);
|
|
223
|
-
for (const accountId of accountIds) {
|
|
224
|
-
const account = await this.readAccountData(accountId);
|
|
225
|
-
if (!account) {
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
const pods = this.extractPodsFromAccountData(accountId, account);
|
|
229
|
-
const match = pods.find((pod) => getPodWebIds(pod).some((candidate) => exactWebId(candidate) === webId));
|
|
230
|
-
if (match) {
|
|
231
|
-
return {
|
|
232
|
-
...match,
|
|
233
|
-
webId,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
if (pods.length === 1 && !hasExplicitPodWebIds(account, pods[0].podId)) {
|
|
237
|
-
return {
|
|
238
|
-
...pods[0],
|
|
239
|
-
webId,
|
|
240
|
-
...webIdsProperty([webId, ...getPodWebIds(pods[0])]),
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return undefined;
|
|
245
|
-
}
|
|
246
|
-
async readAccountData(accountId) {
|
|
247
|
-
for (const key of [`accounts/data/${accountId}`, `/.internal/accounts/data/${accountId}`]) {
|
|
248
|
-
const value = await this.readKvValue(key);
|
|
249
|
-
const record = parsePayloadRecord(value);
|
|
250
|
-
if (record) {
|
|
251
|
-
return record;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return undefined;
|
|
255
|
-
}
|
|
256
|
-
async readStringArrayFromKv(key) {
|
|
257
|
-
const value = await this.readKvValue(key);
|
|
258
|
-
return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
|
|
259
|
-
}
|
|
260
|
-
async readKvValue(key) {
|
|
261
|
-
const tableId = drizzle_orm_1.sql.identifier(this.kvTableName);
|
|
262
|
-
try {
|
|
263
|
-
const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
|
|
264
|
-
SELECT value FROM ${tableId}
|
|
265
|
-
WHERE key = ${key}
|
|
266
|
-
LIMIT 1
|
|
267
|
-
`);
|
|
268
|
-
if (result.rows.length === 0) {
|
|
269
|
-
return undefined;
|
|
270
|
-
}
|
|
271
|
-
return parseStoredValue(result.rows[0].value);
|
|
272
|
-
}
|
|
273
|
-
catch {
|
|
274
|
-
return undefined;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
extractPodsFromAccountData(accountId, data) {
|
|
278
|
-
const podMap = data['**pod**'] || data.pod || {};
|
|
279
|
-
const webIds = extractAccountWebIds(data);
|
|
280
|
-
const pods = [];
|
|
281
|
-
for (const [podId, podData] of Object.entries(podMap)) {
|
|
282
|
-
const pod = podData;
|
|
283
|
-
if (pod.baseUrl && typeof pod.baseUrl === 'string') {
|
|
284
|
-
const storageUrl = stringValue(pod.storageUrl) ?? stringValue(pod.storage);
|
|
285
|
-
const podWebIds = resolvePodWebIds(pod, webIds);
|
|
286
|
-
pods.push({
|
|
287
|
-
podId,
|
|
288
|
-
accountId,
|
|
289
|
-
baseUrl: pod.baseUrl,
|
|
290
|
-
storageUrl,
|
|
291
|
-
webId: dedupeStrings(podWebIds)[0],
|
|
292
|
-
...webIdsProperty(podWebIds),
|
|
293
|
-
nodeId: typeof pod.nodeId === 'string' ? pod.nodeId : undefined,
|
|
294
|
-
edgeNodeId: typeof pod.edgeNodeId === 'string' ? pod.edgeNodeId : undefined,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
return pods;
|
|
299
|
-
}
|
|
300
196
|
/**
|
|
301
197
|
* DrizzleIndexedStorage stores CSS identity facts as typed rows in
|
|
302
198
|
* identity_store; this is the canonical clustered identity source.
|
|
@@ -307,19 +203,25 @@ class PodLookupRepository {
|
|
|
307
203
|
try {
|
|
308
204
|
result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
|
|
309
205
|
SELECT container, id, payload FROM ${storeTableId}
|
|
310
|
-
WHERE container IN ('pod', 'owner'
|
|
206
|
+
WHERE container IN ('pod', 'owner')
|
|
311
207
|
`);
|
|
312
208
|
}
|
|
313
|
-
catch {
|
|
314
|
-
|
|
209
|
+
catch (error) {
|
|
210
|
+
// Legacy-only installations have no canonical table. An unavailable or
|
|
211
|
+
// unreadable existing table must not reactivate legacy ownership.
|
|
212
|
+
if (!isMissingIdentityStore(error))
|
|
213
|
+
throw error;
|
|
214
|
+
return { pods: [], podIds: new Set() };
|
|
315
215
|
}
|
|
216
|
+
const podIds = new Set();
|
|
316
217
|
const podPayloads = new Map();
|
|
317
218
|
const ownerWebIdsByPodId = new Map();
|
|
318
|
-
const webIdsByAccountId = new Map();
|
|
319
219
|
for (const row of result?.rows ?? []) {
|
|
320
220
|
if (!row.id || !row.container) {
|
|
321
221
|
continue;
|
|
322
222
|
}
|
|
223
|
+
if (row.container === 'pod')
|
|
224
|
+
podIds.add(row.id);
|
|
323
225
|
const payload = parsePayloadRecord(row.payload);
|
|
324
226
|
if (!payload) {
|
|
325
227
|
continue;
|
|
@@ -334,14 +236,6 @@ class PodLookupRepository {
|
|
|
334
236
|
if (podId && webId) {
|
|
335
237
|
appendMapValue(ownerWebIdsByPodId, podId, webId);
|
|
336
238
|
}
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
if (row.container === 'webIdLink') {
|
|
340
|
-
const accountId = stringValue(payload.accountId);
|
|
341
|
-
const webId = stringValue(payload.webId);
|
|
342
|
-
if (accountId && webId) {
|
|
343
|
-
appendMapValue(webIdsByAccountId, accountId, webId);
|
|
344
|
-
}
|
|
345
239
|
}
|
|
346
240
|
}
|
|
347
241
|
const pods = [];
|
|
@@ -355,9 +249,7 @@ class PodLookupRepository {
|
|
|
355
249
|
stringValue(pod.webId),
|
|
356
250
|
...(ownerWebIdsByPodId.get(podId) ?? []),
|
|
357
251
|
].filter((value) => typeof value === 'string');
|
|
358
|
-
const podWebIds = dedupeStrings(explicitPodWebIds
|
|
359
|
-
? explicitPodWebIds
|
|
360
|
-
: webIdsByAccountId.get(accountId) ?? []);
|
|
252
|
+
const podWebIds = dedupeStrings(explicitPodWebIds);
|
|
361
253
|
const storageUrl = stringValue(pod.storageUrl) ?? stringValue(pod.storage);
|
|
362
254
|
pods.push({
|
|
363
255
|
podId,
|
|
@@ -370,10 +262,23 @@ class PodLookupRepository {
|
|
|
370
262
|
edgeNodeId: stringValue(pod.edgeNodeId),
|
|
371
263
|
});
|
|
372
264
|
}
|
|
373
|
-
return pods;
|
|
265
|
+
return { pods, podIds };
|
|
374
266
|
}
|
|
375
267
|
}
|
|
376
268
|
exports.PodLookupRepository = PodLookupRepository;
|
|
269
|
+
function isMissingIdentityStore(error) {
|
|
270
|
+
const visited = new Set();
|
|
271
|
+
while (error && typeof error === 'object' && !visited.has(error)) {
|
|
272
|
+
visited.add(error);
|
|
273
|
+
const failure = error;
|
|
274
|
+
if (failure.message === 'no such table: identity_store'
|
|
275
|
+
|| (failure.code === '42P01' && failure.message === 'relation "identity_store" does not exist')) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
error = failure.cause;
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
377
282
|
function extractAccountIdFromAccountDataKey(key) {
|
|
378
283
|
const marker = 'accounts/data/';
|
|
379
284
|
const index = key.indexOf(marker);
|
|
@@ -383,25 +288,6 @@ function extractAccountIdFromAccountDataKey(key) {
|
|
|
383
288
|
const accountId = key.slice(index + marker.length).replace(/\.json$/u, '');
|
|
384
289
|
return accountId || undefined;
|
|
385
290
|
}
|
|
386
|
-
function extractAccountWebIds(data) {
|
|
387
|
-
if (!data || typeof data !== 'object') {
|
|
388
|
-
return [];
|
|
389
|
-
}
|
|
390
|
-
const record = data;
|
|
391
|
-
const linkMap = record['**webIdLink**'] || record.webIdLink || {};
|
|
392
|
-
if (!linkMap || typeof linkMap !== 'object') {
|
|
393
|
-
return [];
|
|
394
|
-
}
|
|
395
|
-
return Object.values(linkMap)
|
|
396
|
-
.map((value) => {
|
|
397
|
-
if (!value || typeof value !== 'object') {
|
|
398
|
-
return undefined;
|
|
399
|
-
}
|
|
400
|
-
const webId = value.webId;
|
|
401
|
-
return typeof webId === 'string' ? webId : undefined;
|
|
402
|
-
})
|
|
403
|
-
.filter((value) => typeof value === 'string');
|
|
404
|
-
}
|
|
405
291
|
function extractPodOwnerWebIds(pod) {
|
|
406
292
|
const ownerMap = pod['**owner**'] || pod.owner || {};
|
|
407
293
|
if (!ownerMap || typeof ownerMap !== 'object') {
|
|
@@ -423,17 +309,6 @@ function explicitPodWebIds(pod) {
|
|
|
423
309
|
...extractPodOwnerWebIds(pod),
|
|
424
310
|
].filter((value) => typeof value === 'string');
|
|
425
311
|
}
|
|
426
|
-
function hasExplicitPodWebIds(account, podId) {
|
|
427
|
-
const podMap = account['**pod**'] ?? account.pod;
|
|
428
|
-
if (!podMap || typeof podMap !== 'object')
|
|
429
|
-
return false;
|
|
430
|
-
const pod = podMap[podId];
|
|
431
|
-
return Boolean(pod && typeof pod === 'object' && explicitPodWebIds(pod).length > 0);
|
|
432
|
-
}
|
|
433
|
-
function resolvePodWebIds(pod, accountWebIds) {
|
|
434
|
-
const explicit = explicitPodWebIds(pod);
|
|
435
|
-
return dedupeStrings(explicit.length > 0 ? explicit : accountWebIds);
|
|
436
|
-
}
|
|
437
312
|
function exactWebId(webId) {
|
|
438
313
|
// WebID identity is the complete original string, not URL equivalence.
|
|
439
314
|
return webId && webId === webId.trim() && !/[\r\n\t]/u.test(webId) ? webId : undefined;
|
|
@@ -466,31 +341,6 @@ function webIdsProperty(values) {
|
|
|
466
341
|
const webIds = dedupeStrings(values);
|
|
467
342
|
return webIds.length > 1 ? { webIds } : {};
|
|
468
343
|
}
|
|
469
|
-
function mergePodLookupResults(values) {
|
|
470
|
-
const byPodId = new Map();
|
|
471
|
-
for (const value of values) {
|
|
472
|
-
const existing = byPodId.get(value.podId);
|
|
473
|
-
if (!existing) {
|
|
474
|
-
byPodId.set(value.podId, value);
|
|
475
|
-
continue;
|
|
476
|
-
}
|
|
477
|
-
const webIds = dedupeStrings([
|
|
478
|
-
...getPodWebIds(existing),
|
|
479
|
-
...getPodWebIds(value),
|
|
480
|
-
]);
|
|
481
|
-
byPodId.set(value.podId, {
|
|
482
|
-
...existing,
|
|
483
|
-
baseUrl: existing.baseUrl || value.baseUrl,
|
|
484
|
-
storageUrl: existing.storageUrl ?? value.storageUrl,
|
|
485
|
-
accountId: existing.accountId || value.accountId,
|
|
486
|
-
webId: webIds[0],
|
|
487
|
-
...webIdsProperty(webIds),
|
|
488
|
-
nodeId: existing.nodeId ?? value.nodeId,
|
|
489
|
-
edgeNodeId: existing.edgeNodeId ?? value.edgeNodeId,
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
return [...byPodId.values()];
|
|
493
|
-
}
|
|
494
344
|
function parsePayloadRecord(value) {
|
|
495
345
|
if (!value) {
|
|
496
346
|
return undefined;
|
|
@@ -508,17 +358,6 @@ function parsePayloadRecord(value) {
|
|
|
508
358
|
const unwrapped = unwrapStoredValue(value);
|
|
509
359
|
return typeof unwrapped === 'object' ? unwrapped : undefined;
|
|
510
360
|
}
|
|
511
|
-
function parseStoredValue(value) {
|
|
512
|
-
if (typeof value === 'string') {
|
|
513
|
-
try {
|
|
514
|
-
return unwrapStoredValue(JSON.parse(value));
|
|
515
|
-
}
|
|
516
|
-
catch {
|
|
517
|
-
return undefined;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
return unwrapStoredValue(value);
|
|
521
|
-
}
|
|
522
361
|
function unwrapStoredValue(value) {
|
|
523
362
|
if (value && typeof value === 'object' && 'key' in value && 'payload' in value) {
|
|
524
363
|
return value.payload;
|
|
@@ -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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
198
|
+
if (providerLoadOperation === operation) {
|
|
199
|
+
providerLoadOperation = undefined;
|
|
200
|
+
providerLoadPromise = undefined;
|
|
201
|
+
}
|
|
143
202
|
}
|
|
144
|
-
})
|
|
145
|
-
|
|
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
|
|
253
|
-
? await operationsClient.listAuthorizationMethods()
|
|
254
|
-
: []),
|
|
332
|
+
listProviders,
|
|
255
333
|
listModels: podStore.listModels
|
|
256
334
|
? async () => podStore.listModels()
|
|
257
335
|
: operationsClient.listModels,
|