@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.
- package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +0 -1
- package/dist/api/auth/ClientCredentialsAuthenticator.js +0 -3
- package/dist/api/auth/index.d.ts +1 -0
- package/dist/api/auth/index.js +1 -1
- package/dist/identity/drizzle/AccountRoleRepository.d.ts +30 -2
- package/dist/identity/drizzle/AccountRoleRepository.js +163 -15
- package/dist/identity/drizzle/PodLookupRepository.d.ts +55 -12
- package/dist/identity/drizzle/PodLookupRepository.js +225 -243
- package/dist/identity/drizzle/db.d.ts +19 -0
- package/dist/identity/drizzle/db.js +39 -4
- 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/shared-ui/dist/index.d.ts +0 -1
- package/node_modules/@undefineds.co/shared-ui/dist/index.js +0 -1
- 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.css +1 -1
- package/static/app/assets/main.js +12 -13
- package/static/auth-callback/assets/{AiConfigContext-DJKvxduw.js → AiConfigContext-DqWRSm7n.js} +1 -1
- package/static/auth-callback/assets/{AiConfigPage-DgGmHb18.js → AiConfigPage-C5YYyvzl.js} +1 -1
- package/static/auth-callback/assets/{DocumentProcessingPanel-CJmxu8x1.js → DocumentProcessingPanel-DZCiacBz.js} +1 -1
- package/static/auth-callback/assets/{IndexLifecyclePanel-byCa7h7L.js → IndexLifecyclePanel-CCLeOljB.js} +1 -1
- package/static/auth-callback/assets/{IndexSubjectPanel-CDwT4Yx1.js → IndexSubjectPanel-BbUqop4b.js} +1 -1
- package/static/auth-callback/assets/{ModelAssignmentsPanel-CwM3k8w7.js → ModelAssignmentsPanel-jUUTDKuh.js} +1 -1
- package/static/auth-callback/assets/ModelsPage-B08zdtff.js +10 -0
- package/static/auth-callback/assets/{NetworkPage-DkVgxtNj.js → NetworkPage-Vp2PrMxs.js} +1 -1
- package/static/auth-callback/assets/{PaneListHeader-DRI0hjmt.js → PaneListHeader-DY7baSFl.js} +1 -1
- package/static/auth-callback/assets/{SearchIndexingPanel-mbppDZFE.js → SearchIndexingPanel-D4YRg1-8.js} +1 -1
- package/static/auth-callback/assets/{StatusSubjectPanel-DNsW76aC.js → StatusSubjectPanel-ArIS-TxT.js} +1 -1
- package/static/auth-callback/assets/{StatusWorkspace-DGcTbHT5.js → StatusWorkspace-Cp5LQ9Yx.js} +1 -1
- package/static/auth-callback/assets/SystemSettingsPage-BDvT1NUz.js +1 -0
- package/static/auth-callback/assets/SystemSettingsSubjectPanel-CDqRuC4t.js +33 -0
- package/static/auth-callback/assets/{activity-Ck-Mfxxo.js → activity-D1Dsfa_0.js} +1 -1
- package/static/auth-callback/assets/admin-C6qCRFiY.js +1 -0
- package/static/auth-callback/assets/{auth-callback-DG4Ge40c.js → auth-callback-Cmf4hXVZ.js} +105 -109
- package/static/auth-callback/assets/auth-callback-DWMMiFjh.css +1 -0
- package/static/auth-callback/assets/{badge-GSSnw1VV.js → badge-CZsHq3Ih.js} +1 -1
- package/static/auth-callback/assets/{download-BYyRX-K2.js → download-DU9ntlHw.js} +1 -1
- package/static/auth-callback/assets/{external-link-WEC0Ki-0.js → external-link-BfzaaZDx.js} +1 -1
- package/static/auth-callback/assets/{index-kju2k4Dp.js → index-DSNnLUbU.js} +4 -4
- package/static/auth-callback/assets/{index-DlsMCgo8.js → index-FHhHoKgp.js} +1 -1
- package/static/auth-callback/assets/{index-browser-D8YW6coF.js → index-browser-BNFqqC74.js} +1 -1
- package/static/auth-callback/assets/{key-round-DJUT5Nh0.js → key-round-BwP3DBWo.js} +1 -1
- package/static/auth-callback/assets/{rotate-ccw-CXwfZUKU.js → rotate-ccw-VMwnmyab.js} +1 -1
- package/static/auth-callback/assets/settings-projection-CUkyprDQ.js +1 -0
- package/static/auth-callback/assets/{skeleton-DWPtNhUB.js → skeleton-CoD55rBT.js} +1 -1
- package/static/auth-callback/assets/{waypoints-l1oDnRJ0.js → waypoints-DRo_1Ca7.js} +1 -1
- package/static/auth-callback/assets/{workspace-layout-B7iKpK-Q.js → workspace-layout--mcXl3My.js} +1 -1
- package/static/auth-callback/auth-callback.html +2 -2
- package/static/dashboard/assets/{AiConfigContext-U0cCSY28.js → AiConfigContext-BpPDKSzI.js} +1 -1
- package/static/dashboard/assets/{AiConfigPage-CMi0uJdo.js → AiConfigPage-wsqEZDIX.js} +1 -1
- package/static/dashboard/assets/{DocumentProcessingPanel-DEZiEpft.js → DocumentProcessingPanel-CDSy3juc.js} +1 -1
- package/static/dashboard/assets/{IndexLifecyclePanel-CGZfLUog.js → IndexLifecyclePanel-CVd6vQCX.js} +1 -1
- package/static/dashboard/assets/{IndexSubjectPanel-DiDZauHk.js → IndexSubjectPanel-B2cXwl8X.js} +1 -1
- package/static/dashboard/assets/{ModelAssignmentsPanel-DaGGZKuf.js → ModelAssignmentsPanel-BeBZhnQd.js} +1 -1
- package/static/dashboard/assets/ModelsPage-B6McSCJw.js +25 -0
- package/static/dashboard/assets/{NetworkPage-DI5-O7GT.js → NetworkPage-DV2V0qWO.js} +1 -1
- package/static/dashboard/assets/{PaneListHeader-4oa7uf59.js → PaneListHeader-D0c1yCFH.js} +1 -1
- package/static/dashboard/assets/{SearchIndexingPanel-DoqEwocD.js → SearchIndexingPanel-CGs0OZ12.js} +1 -1
- package/static/dashboard/assets/{StatusSubjectPanel-CLzBC5fu.js → StatusSubjectPanel-oPSECj7i.js} +1 -1
- package/static/dashboard/assets/{StatusWorkspace-UyKADZoM.js → StatusWorkspace-PdovQPKa.js} +1 -1
- package/static/dashboard/assets/SystemSettingsPage-CtNDklkx.js +1 -0
- package/static/dashboard/assets/SystemSettingsSubjectPanel-WaE8mTQi.js +33 -0
- package/static/dashboard/assets/{activity-CjIoNU4S.js → activity-B9ylSoyr.js} +1 -1
- package/static/dashboard/assets/admin-C6qCRFiY.js +1 -0
- package/static/dashboard/assets/{badge-dcDZpKh3.js → badge-BNbFbpIb.js} +1 -1
- package/static/dashboard/assets/{dashboard-BY8GdnEI.js → dashboard-CD-FFVRD.js} +129 -133
- package/static/dashboard/assets/dashboard-DWMMiFjh.css +1 -0
- package/static/dashboard/assets/{download-BeYCrc-_.js → download-nOP38LtW.js} +1 -1
- package/static/dashboard/assets/{external-link-626-5KVx.js → external-link-Bh2ewN9E.js} +1 -1
- package/static/dashboard/assets/{index-CBbk6hx-.js → index-PgS-Bt7X.js} +1 -1
- package/static/dashboard/assets/{index-CsxH6Hmz.js → index-QN2QgEwr.js} +4 -4
- package/static/dashboard/assets/{key-round-B9IL4Wua.js → key-round-bGmguBLv.js} +1 -1
- package/static/dashboard/assets/{rotate-ccw-DM4vCfiI.js → rotate-ccw-D2x__ODW.js} +1 -1
- package/static/dashboard/assets/settings-projection-CUkyprDQ.js +1 -0
- package/static/dashboard/assets/{skeleton-ChlFAPvP.js → skeleton-VOqft8WS.js} +1 -1
- package/static/dashboard/assets/{waypoints-CvpZzf_I.js → waypoints-fgRqm4oH.js} +1 -1
- package/static/dashboard/assets/{workspace-layout-Bxv7V-tt.js → workspace-layout-DV4dgeHX.js} +1 -1
- package/static/dashboard/dashboard.html +2 -2
- package/static/settings/assets/{AiConfigContext-CDVIHLBb.js → AiConfigContext-zKQYdfai.js} +1 -1
- package/static/settings/assets/{AiConfigPage-C7qfiiaJ.js → AiConfigPage-Bs1EE3Ei.js} +1 -1
- package/static/settings/assets/{DocumentProcessingPanel-B59QTe-V.js → DocumentProcessingPanel-CSFfdOXS.js} +1 -1
- package/static/settings/assets/{IndexLifecyclePanel-C-e-3eVi.js → IndexLifecyclePanel-BR9Su49B.js} +1 -1
- package/static/settings/assets/{IndexSubjectPanel-B-scRZ1u.js → IndexSubjectPanel-BgbI9c-a.js} +1 -1
- package/static/settings/assets/{ModelAssignmentsPanel-Wy8jzVg4.js → ModelAssignmentsPanel-C0BE7nbk.js} +1 -1
- package/static/settings/assets/ModelsPage-CU-YQ8_7.js +10 -0
- package/static/settings/assets/{NetworkPage-tBB-4Ox8.js → NetworkPage-C8OA9Mtp.js} +1 -1
- package/static/settings/assets/{PaneListHeader-DuD6glzn.js → PaneListHeader-COvlPJWj.js} +1 -1
- package/static/settings/assets/{SearchIndexingPanel-CiDHc0nx.js → SearchIndexingPanel-Bdd46mIV.js} +1 -1
- package/static/settings/assets/{StatusSubjectPanel-OF5cgWwt.js → StatusSubjectPanel-BuLml7qn.js} +1 -1
- package/static/settings/assets/{StatusWorkspace-CZ8Oa0gV.js → StatusWorkspace-CoMFRXoV.js} +1 -1
- package/static/settings/assets/SystemSettingsPage-DlzmaWV-.js +1 -0
- package/static/settings/assets/SystemSettingsSubjectPanel-DExjDo_v.js +33 -0
- package/static/settings/assets/{activity-2M1f5eBS.js → activity-TLerXgMs.js} +1 -1
- package/static/settings/assets/admin-C6qCRFiY.js +1 -0
- package/static/settings/assets/{badge-CRKan0qX.js → badge-Bcs8iWrH.js} +1 -1
- package/static/settings/assets/{download-PCHldCdu.js → download-Ct3QmHPt.js} +1 -1
- package/static/settings/assets/{external-link-tmKe7eYO.js → external-link-C8KiHbss.js} +1 -1
- package/static/settings/assets/{index-Cek02M79.js → index-1Q2cRjZd.js} +1 -1
- package/static/settings/assets/{index-BDCNTwWz.js → index-DBO_Vq0P.js} +4 -4
- package/static/settings/assets/{index-browser-EVDVm9T5.js → index-browser-CazX1pc2.js} +1 -1
- package/static/settings/assets/{key-round-CUGvQCkt.js → key-round-B19YD1GA.js} +1 -1
- package/static/settings/assets/{rotate-ccw-BRA3lIHM.js → rotate-ccw-AQm2kMgU.js} +1 -1
- package/static/settings/assets/{settings-BdlKrzJ3.js → settings-C-VBtt9r.js} +129 -133
- package/static/settings/assets/settings-DWMMiFjh.css +1 -0
- package/static/settings/assets/settings-projection-CUkyprDQ.js +1 -0
- package/static/settings/assets/{skeleton-CuDeifWb.js → skeleton-DyZ1OBYf.js} +1 -1
- package/static/settings/assets/{waypoints-w6LxsFY1.js → waypoints-CC3wq-SU.js} +1 -1
- package/static/settings/assets/{workspace-layout-TN-BWmT5.js → workspace-layout-BxXNKgf9.js} +1 -1
- package/static/settings/settings.html +2 -2
- package/node_modules/@undefineds.co/shared-ui/dist/oidc-consent.d.ts +0 -54
- package/node_modules/@undefineds.co/shared-ui/dist/oidc-consent.js +0 -21
- package/static/auth-callback/assets/ModelsPage-DapY2Dwf.js +0 -10
- package/static/auth-callback/assets/SystemSettingsPage-C6qklJ9k.js +0 -1
- package/static/auth-callback/assets/SystemSettingsSubjectPanel-B4VV9xkG.js +0 -31
- package/static/auth-callback/assets/admin-BU4m6Ko2.js +0 -1
- package/static/auth-callback/assets/auth-callback-C_7MUXJb.css +0 -1
- package/static/dashboard/assets/ModelsPage-DZgAOdxO.js +0 -25
- package/static/dashboard/assets/SystemSettingsPage-qP0hLHNV.js +0 -1
- package/static/dashboard/assets/SystemSettingsSubjectPanel-EaWax9Ot.js +0 -31
- package/static/dashboard/assets/admin-BU4m6Ko2.js +0 -1
- package/static/dashboard/assets/dashboard-C_7MUXJb.css +0 -1
- package/static/settings/assets/ModelsPage-DHNiaodk.js +0 -10
- package/static/settings/assets/SystemSettingsPage-DT2tC0F_.js +0 -1
- package/static/settings/assets/SystemSettingsSubjectPanel-Bkta1oDw.js +0 -31
- package/static/settings/assets/admin-BU4m6Ko2.js +0 -1
- 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
|
-
|
|
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,
|
|
@@ -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
|
-
|
|
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 = () =>
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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;
|