@undefineds.co/xpod 0.4.25 → 0.4.27
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/ApiServer.d.ts +1 -0
- package/dist/api/ApiServer.js +38 -0
- package/dist/api/ai-config/AiConfigStore.js +18 -6
- package/dist/api/ai-gateway/connect/index.d.ts +1 -0
- package/dist/api/ai-gateway/connect/index.js +39 -18
- package/dist/api/ai-gateway/models/ProviderModelsAdapter.js +5 -2
- package/dist/api/container/common.js +8 -2
- package/dist/api/container/index.js +2 -2
- package/dist/api/container/local.js +6 -3
- package/dist/api/handlers/AiGatewayHandler.d.ts +1 -0
- package/dist/api/handlers/AiGatewayHandler.js +53 -3
- package/dist/api/handlers/AiGatewayManagementHandler.js +9 -0
- package/dist/api/handlers/TaskHandler.js +15 -10
- package/dist/api/network/NetworkEnvironmentConfigurationStore.js +1 -1
- package/dist/api/runs/ManagedRunWorker.d.ts +0 -1
- package/dist/api/runs/ManagedRunWorker.js +2 -14
- package/dist/api/runs/PiAgentRuntimeDriver.d.ts +0 -8
- package/dist/api/runs/PiAgentRuntimeDriver.js +65 -21
- package/dist/api/runs/RunStateCenter.js +1 -0
- package/dist/api/runs/store.d.ts +1 -0
- package/dist/api/runs/store.js +13 -0
- package/dist/api/tasks/TaskMaterializer.js +13 -6
- package/dist/api/tasks/TaskResumeDiagnostics.d.ts +28 -0
- package/dist/api/tasks/TaskResumeDiagnostics.js +148 -0
- package/dist/api/tasks/TaskService.js +40 -23
- package/dist/storage/conditions/StorageETagHandler.d.ts +1 -1
- package/dist/storage/conditions/StorageETagHandler.js +13 -4
- package/dist/util/task-model-diagnostics.d.ts +67 -0
- package/dist/util/task-model-diagnostics.js +118 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiApiKeyPool.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiApiKeyPool.js +2 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectDialog.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.js +10 -10
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.d.ts +3 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeyRow.js +2 -2
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +77 -23
- package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiSortableCredentialList.js +59 -59
- package/node_modules/@undefineds.co/ai-connections/dist/collection-runtime.js +2 -0
- package/node_modules/@undefineds.co/ai-connections/dist/collections.js +14 -6
- package/node_modules/@undefineds.co/ai-connections/dist/contract/ai-connections-client.d.ts +2 -1
- package/node_modules/@undefineds.co/ai-connections/dist/contract/ai-connections-client.js +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/gateway-model-roles.d.ts +11 -0
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/gateway-model-roles.js +11 -0
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/normalize.d.ts +2 -1
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/normalize.js +12 -5
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/request.js +10 -2
- package/node_modules/@undefineds.co/ai-connections/dist/contract/client/types.d.ts +10 -0
- package/node_modules/@undefineds.co/ai-connections/package.json +1 -1
- package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +1 -0
- package/node_modules/@undefineds.co/extension-sdk/package.json +1 -1
- package/node_modules/@undefineds.co/pod-collections/dist/index.cjs +60 -13
- package/node_modules/@undefineds.co/pod-collections/dist/mapping.d.ts +16 -1
- package/node_modules/@undefineds.co/pod-collections/dist/mapping.js +51 -0
- package/node_modules/@undefineds.co/pod-collections/dist/mutations.js +14 -18
- package/node_modules/@undefineds.co/pod-collections/package.json +1 -1
- package/node_modules/@undefineds.co/shared-ui/dist/list.d.ts +8 -1
- package/node_modules/@undefineds.co/shared-ui/dist/list.js +4 -1
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpChrome.d.ts +1 -1
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpChrome.js +2 -2
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/IdpViews.js +2 -2
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/PodSignInFrame.d.ts +3 -3
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/PodSignInFrame.js +5 -5
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/parts.d.ts +5 -5
- package/node_modules/@undefineds.co/shared-ui/dist/pod-sign-in/parts.js +5 -5
- package/node_modules/@undefineds.co/shared-ui/dist/section-header.js +1 -1
- package/node_modules/@undefineds.co/shared-ui/dist/theme.css +5 -32
- package/node_modules/@undefineds.co/shared-ui/package.json +1 -1
- package/node_modules/@undefineds.co/solid-sdk/package.json +1 -1
- package/package.json +2 -2
- package/static/app/assets/main.css +1 -1
- package/static/app/assets/main.js +1 -1
- package/static/dashboard/assets/AccountAuthBoundary-Dv52SZc6.js +1 -0
- package/static/dashboard/assets/DesktopOidcCallback-DaT_Rumf.js +1 -0
- package/static/dashboard/assets/DevicePages-HjclpIVm.js +2 -0
- package/static/dashboard/assets/ModelsPage-Be2TPd-x.js +11 -0
- package/static/dashboard/assets/{PaneListHeader-Dn5O0mwy.js → PaneListHeader-DtvnRmFq.js} +1 -1
- package/static/dashboard/assets/{PodDeletionAuthorizationPanel-D3NOG6ZM.js → PodDeletionAuthorizationPanel-BJk_8rJq.js} +3 -3
- package/static/dashboard/assets/PodPage-C4gzEspV.js +1 -0
- package/static/dashboard/assets/{SystemSettingsPage-BNQP_Yuc.js → SystemSettingsPage-HaNrP1H1.js} +1 -1
- package/static/dashboard/assets/TasksPage-BdO5K2L7.js +1 -0
- package/static/dashboard/assets/{WebDesktopEntry-vnb4CZWp.js → WebDesktopEntry-Cj9_ZfJU.js} +1 -1
- package/static/dashboard/assets/XpodAiConnectionsPodStore-B7gmXBmC.js +3 -0
- package/static/dashboard/assets/XpodShellApp-D_KY4nCW.js +4 -0
- package/static/dashboard/assets/{account-session-DCUmCop1.js → account-session-DlcKNG7k.js} +1 -1
- package/static/dashboard/assets/{button-By5Bgvmb.js → button-wslF55uZ.js} +1 -1
- package/static/dashboard/assets/collection-runtime-uxxgEu13.js +1 -0
- package/static/dashboard/assets/{createLucideIcon-bunMzPms.js → createLucideIcon-BJQl82Qk.js} +1 -1
- package/static/dashboard/assets/dashboard-CbJ-JkrI.css +1 -0
- package/static/dashboard/assets/{dashboard-mlsg3yaw.js → dashboard-Or0gnkou.js} +2 -2
- package/static/dashboard/assets/index-BqFeOML9.js +1 -0
- package/static/dashboard/assets/{index-DvMG5pHk.js → index-DgKn1twH.js} +2 -2
- package/static/dashboard/assets/{label-Bj03L9xs.js → label-CS6Sx6Zb.js} +1 -1
- package/static/dashboard/assets/list-UyhlCFKk.js +1 -0
- package/static/dashboard/assets/{nav-item-style-BNqWQY6p.js → nav-item-style-CW1Nnorq.js} +1 -1
- package/static/dashboard/assets/{pod-collections-host-KnnGFZHL.js → pod-collections-host-DjK2xf-b.js} +6 -6
- package/static/dashboard/assets/{safe-continuation-D9AhcWiB.js → safe-continuation-ClWAhcma.js} +1 -1
- package/static/dashboard/assets/{section-header-CLCzbOp_.js → section-header-CEIEcco-.js} +1 -1
- package/static/dashboard/assets/{setting-row-DTCec8A2.js → setting-row-CaYv3dfF.js} +1 -1
- package/static/dashboard/assets/xpod-remembered-login-BUF9fin1.js +310 -0
- package/static/dashboard/dashboard.html +2 -2
- package/static/settings/assets/{AccountAuthBoundary-C0og_gIA.js → AccountAuthBoundary-Chqd38oU.js} +1 -1
- package/static/settings/assets/{DesktopOidcCallback-C5q4oFZ0.js → DesktopOidcCallback-VUrOSM_q.js} +1 -1
- package/static/settings/assets/DevicePages-C6qwvWwu.js +2 -0
- package/static/settings/assets/ModelsPage-Ry_dObRQ.js +11 -0
- package/static/settings/assets/{PaneListHeader-DqVlMe2D.js → PaneListHeader-B5RkRqr4.js} +1 -1
- package/static/settings/assets/{PodDeletionAuthorizationPanel-DgVvarVY.js → PodDeletionAuthorizationPanel-DPM-5Izt.js} +3 -3
- package/static/settings/assets/PodPage-CxND6wwc.js +1 -0
- package/static/settings/assets/{SystemSettingsPage-Crzm_DrI.js → SystemSettingsPage-C8EdMkW7.js} +1 -1
- package/static/settings/assets/TasksPage-DuqptEm8.js +1 -0
- package/static/settings/assets/{WebDesktopEntry-BGC5eRF2.js → WebDesktopEntry-BRcyktkv.js} +1 -1
- package/static/settings/assets/XpodAiConnectionsPodStore-CyA_BJVC.js +3 -0
- package/static/settings/assets/XpodShellApp-CsivHJl0.js +4 -0
- package/static/settings/assets/XpodThemeProvider-CbJ-JkrI.css +1 -0
- package/static/settings/assets/{XpodThemeProvider-CB6LepTA.js → XpodThemeProvider-DK3pIdRG.js} +2 -2
- package/static/settings/assets/{account-session-BcLjCj0x.js → account-session-DJOZ03xr.js} +1 -1
- package/static/settings/assets/auth-callback-GIBYEz_7.js +1 -0
- package/static/settings/assets/{button-BObT8pTE.js → button-D3yW-2o1.js} +1 -1
- package/static/settings/assets/collection-runtime-DfGo25mh.js +1 -0
- package/static/settings/assets/{createLucideIcon-BVsUxXpZ.js → createLucideIcon-CacwLsmB.js} +1 -1
- package/static/settings/assets/{index-ktjMkj7-.js → index-r5KRYsKC.js} +1 -1
- package/static/settings/assets/{label-BrqO8_m_.js → label-MJjUmvzG.js} +1 -1
- package/static/settings/assets/list-Ce-5k1MX.js +1 -0
- package/static/settings/assets/{nav-item-style-Di2CxkDU.js → nav-item-style-ZUbL8KOZ.js} +1 -1
- package/static/settings/assets/{pod-collections-host-KnnGFZHL.js → pod-collections-host-DjK2xf-b.js} +6 -6
- package/static/settings/assets/{safe-continuation-Dx6ceJpY.js → safe-continuation-vlBZFSlU.js} +1 -1
- package/static/settings/assets/{section-header-Dtl7yjVr.js → section-header-DGJdEvQw.js} +1 -1
- package/static/settings/assets/{setting-row-BL6Hhe8N.js → setting-row-C7tuFiho.js} +1 -1
- package/static/settings/assets/{settings-DaRBxoZk.js → settings-CyQPih1O.js} +1 -1
- package/static/settings/assets/xpod-remembered-login-D-TzzT5K.js +567 -0
- package/static/settings/auth-callback.html +3 -3
- package/static/settings/settings.html +3 -3
- package/static/dashboard/assets/AccountAuthBoundary-BvoDydRz.js +0 -1
- package/static/dashboard/assets/DesktopOidcCallback-BwIj874x.js +0 -1
- package/static/dashboard/assets/DevicePages-jOjMcvsr.js +0 -2
- package/static/dashboard/assets/ModelsPage-CMTdfqBR.js +0 -11
- package/static/dashboard/assets/PodPage-BueBdJ9_.js +0 -1
- package/static/dashboard/assets/TasksPage-CxtLJiSF.js +0 -1
- package/static/dashboard/assets/XpodAiConnectionsPodStore-wx3Ttupm.js +0 -18
- package/static/dashboard/assets/XpodShellApp-5FWY8-d5.js +0 -4
- package/static/dashboard/assets/collection-runtime-BnomuB0E.js +0 -1
- package/static/dashboard/assets/dashboard-C64R553B.css +0 -1
- package/static/dashboard/assets/xpod-remembered-login-Dr69A0Rv.js +0 -295
- package/static/settings/assets/DevicePages-fyHjjucT.js +0 -2
- package/static/settings/assets/ModelsPage-EyOByLyE.js +0 -11
- package/static/settings/assets/PodPage-DY4Oi9wg.js +0 -1
- package/static/settings/assets/TasksPage-CfXJbnlt.js +0 -1
- package/static/settings/assets/XpodAiConnectionsPodStore-CXcmgKmQ.js +0 -3
- package/static/settings/assets/XpodShellApp-D1PS3Iut.js +0 -4
- package/static/settings/assets/XpodThemeProvider-C64R553B.css +0 -1
- package/static/settings/assets/auth-callback-DSPQ28ar.js +0 -1
- package/static/settings/assets/collection-runtime-DzNbdZGe.js +0 -1
- package/static/settings/assets/index-browser-3WfrNbcG.js +0 -273
- package/static/settings/assets/xpod-remembered-login-Q66DH12t.js +0 -295
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
-
import { cn } from '@undefineds.co/shared-ui';
|
|
3
|
+
import { cn, interactiveFocusClass, ListSurface } from '@undefineds.co/shared-ui';
|
|
4
4
|
import { GripVertical } from 'lucide-react';
|
|
5
5
|
import { credentialDisplayLabel } from './credential-labels.js';
|
|
6
6
|
export function AiSortableCredentialList({ credentials, disabled, onMove, children }) {
|
|
@@ -133,64 +133,64 @@ export function AiSortableCredentialList({ credentials, disabled, onMove, childr
|
|
|
133
133
|
row.style.transform = `translateY(${shift}px)`;
|
|
134
134
|
});
|
|
135
135
|
};
|
|
136
|
-
return (
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
const next = event.key === 'ArrowUp' ? index - 1 : event.key === 'ArrowDown' ? index + 1
|
|
142
|
-
: event.key === 'Home' ? 0 : event.key === 'End' ? credentials.length - 1 : undefined;
|
|
143
|
-
if (next === undefined)
|
|
144
|
-
return;
|
|
145
|
-
event.preventDefault();
|
|
146
|
-
move(index, next);
|
|
147
|
-
}, onPointerDown: (event) => {
|
|
148
|
-
if (unavailable || event.button !== 0 || dragRef.current)
|
|
149
|
-
return;
|
|
150
|
-
event.preventDefault();
|
|
151
|
-
dropRef.current = null;
|
|
152
|
-
clearMotion();
|
|
153
|
-
event.currentTarget.focus();
|
|
154
|
-
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
155
|
-
const elements = rows();
|
|
156
|
-
const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;
|
|
157
|
-
dragRef.current = { id: credential.id, from: index, to: index, pointerId: event.pointerId,
|
|
158
|
-
startY: event.clientY, y: event.clientY, handle: event.currentTarget, rows: elements,
|
|
159
|
-
rects: elements.map((row) => row.getBoundingClientRect()) };
|
|
160
|
-
for (const [rowIndex, row] of elements.entries()) {
|
|
161
|
-
row.style.willChange = 'transform';
|
|
162
|
-
row.style.transition = reduced || rowIndex === index ? 'none' : 'transform 160ms ease-out';
|
|
163
|
-
if (rowIndex === index) {
|
|
164
|
-
row.dataset.dragging = 'true';
|
|
165
|
-
row.style.position = 'relative';
|
|
166
|
-
row.style.zIndex = '10';
|
|
167
|
-
row.style.boxShadow = '0 8px 24px rgb(0 0 0 / 0.18)';
|
|
168
|
-
row.style.backgroundColor = 'hsl(var(--card))';
|
|
136
|
+
return (_jsx(ListSurface, { asChild: true, ref: listRef, className: cn('divide-y-0 bg-transparent', credentials.length > 0 ? 'border-border/70' : 'border-0 rounded-none'), children: _jsxs("div", { children: [_jsx("span", { className: "sr-only", role: "status", children: announcement }), credentials.map((credential, index) => (_jsx("div", { "data-sortable-credential": credential.id, className: "border-b border-border/60 first-of-type:rounded-t-xl last-of-type:rounded-b-xl last:border-b-0", children: children(credential, onMove ? (_jsx("button", { type: "button", disabled: unavailable, "aria-label": `拖动排序 ${credentialDisplayLabel(credential)}`, title: credentials.length < 2 ? '至少添加两条连接后可拖动排序' : '拖动调整优先级,或使用方向键、Home / End 排序', className: cn('flex h-8 w-6 shrink-0 touch-none select-none items-center justify-center rounded text-muted-foreground hover:text-foreground disabled:opacity-40 enabled:cursor-grab active:cursor-grabbing', interactiveFocusClass), onKeyDown: (event) => {
|
|
137
|
+
if (event.key === 'Escape') {
|
|
138
|
+
finish(false);
|
|
139
|
+
return;
|
|
169
140
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
141
|
+
const next = event.key === 'ArrowUp' ? index - 1 : event.key === 'ArrowDown' ? index + 1
|
|
142
|
+
: event.key === 'Home' ? 0 : event.key === 'End' ? credentials.length - 1 : undefined;
|
|
143
|
+
if (next === undefined)
|
|
144
|
+
return;
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
move(index, next);
|
|
147
|
+
}, onPointerDown: (event) => {
|
|
148
|
+
if (unavailable || event.button !== 0 || dragRef.current)
|
|
149
|
+
return;
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
dropRef.current = null;
|
|
152
|
+
clearMotion();
|
|
153
|
+
event.currentTarget.focus();
|
|
154
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
155
|
+
const elements = rows();
|
|
156
|
+
const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;
|
|
157
|
+
dragRef.current = { id: credential.id, from: index, to: index, pointerId: event.pointerId,
|
|
158
|
+
startY: event.clientY, y: event.clientY, handle: event.currentTarget, rows: elements,
|
|
159
|
+
rects: elements.map((row) => row.getBoundingClientRect()) };
|
|
160
|
+
for (const [rowIndex, row] of elements.entries()) {
|
|
161
|
+
row.style.willChange = 'transform';
|
|
162
|
+
row.style.transition = reduced || rowIndex === index ? 'none' : 'transform 160ms ease-out';
|
|
163
|
+
if (rowIndex === index) {
|
|
164
|
+
row.dataset.dragging = 'true';
|
|
165
|
+
row.style.position = 'relative';
|
|
166
|
+
row.style.zIndex = '10';
|
|
167
|
+
row.style.boxShadow = '0 8px 24px rgb(0 0 0 / 0.18)';
|
|
168
|
+
row.style.backgroundColor = 'hsl(var(--card))';
|
|
169
|
+
}
|
|
182
170
|
}
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
171
|
+
}, onPointerMove: (event) => {
|
|
172
|
+
const drag = dragRef.current;
|
|
173
|
+
if (!drag || drag.pointerId !== event.pointerId || unavailable)
|
|
174
|
+
return;
|
|
175
|
+
drag.y = event.clientY;
|
|
176
|
+
let distance = Infinity;
|
|
177
|
+
drag.rects.forEach((rect, rowIndex) => {
|
|
178
|
+
const delta = Math.abs(event.clientY - (rect.top + rect.bottom) / 2);
|
|
179
|
+
if (delta < distance) {
|
|
180
|
+
distance = delta;
|
|
181
|
+
drag.to = rowIndex;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
if (frameRef.current === undefined)
|
|
185
|
+
frameRef.current = requestAnimationFrame(paintDrag);
|
|
186
|
+
}, onPointerUp: (event) => {
|
|
187
|
+
if (dragRef.current?.pointerId === event.pointerId)
|
|
188
|
+
finish(true);
|
|
189
|
+
}, onPointerCancel: (event) => {
|
|
190
|
+
if (dragRef.current?.pointerId === event.pointerId)
|
|
191
|
+
finish(false);
|
|
192
|
+
}, onLostPointerCapture: (event) => {
|
|
193
|
+
if (dragRef.current?.pointerId === event.pointerId)
|
|
194
|
+
finish(false);
|
|
195
|
+
}, children: _jsx(GripVertical, { "aria-hidden": "true", className: "h-4 w-4" }) })) : null) }, credential.id)))] }) }));
|
|
196
196
|
}
|
|
@@ -188,7 +188,9 @@ export function collectionCredentialMutations(credentials, store) {
|
|
|
188
188
|
const before = collection.get(key);
|
|
189
189
|
if (Object.keys(patch).length > 0)
|
|
190
190
|
await updateCredentialRow(collection, key, patch);
|
|
191
|
+
// Replacement secrets belong only to store completion, never to projected rows.
|
|
191
192
|
const attributes = {
|
|
193
|
+
...(input.apiKey === undefined ? {} : { apiKey: input.apiKey }),
|
|
192
194
|
...(input.priority === undefined ? {} : { priority: input.priority }),
|
|
193
195
|
...(input.baseUrl === undefined ? {} : { baseUrl: input.baseUrl }),
|
|
194
196
|
...(input.proxyUrl === undefined ? {} : { proxyUrl: input.proxyUrl }),
|
|
@@ -105,6 +105,10 @@ export function credentialSummaryFromRow(row, provider, carrier) {
|
|
|
105
105
|
enabled: status === undefined ? carrier?.enabled ?? true : status === 'active',
|
|
106
106
|
priority: carrier?.priority ?? 100,
|
|
107
107
|
health: reauthRequired ? 'expired' : carrier?.health ?? 'unknown',
|
|
108
|
+
lastFailureCode: stringValue(row.lastFailureCode),
|
|
109
|
+
lastFailureAt: isoStringValue(row.lastFailureAt),
|
|
110
|
+
rateLimitResetAt: isoStringValue(row.rateLimitResetAt),
|
|
111
|
+
failCount: numberValue(row.failCount),
|
|
108
112
|
maskedHint: carrier?.maskedHint,
|
|
109
113
|
baseUrl: carrier?.baseUrl,
|
|
110
114
|
proxyUrl: carrier?.proxyUrl,
|
|
@@ -115,7 +119,7 @@ export function credentialSummaryFromRow(row, provider, carrier) {
|
|
|
115
119
|
}
|
|
116
120
|
/** The provider's credential list, rebuilt from the live rows of one table document. */
|
|
117
121
|
export function credentialSummariesForProvider(provider, rows, carrier = []) {
|
|
118
|
-
const byId = new Map(carrier.map((credential) => [credential.id, credential]));
|
|
122
|
+
const byId = new Map(carrier.map((credential) => [credentialResource.buildId({ id: credential.id }), credential]));
|
|
119
123
|
const summaries = [];
|
|
120
124
|
for (const row of rows) {
|
|
121
125
|
if (providerOfCredentialRow(row) !== provider)
|
|
@@ -137,11 +141,15 @@ export function credentialCarriers(storeProducts, overlayProducts) {
|
|
|
137
141
|
const carriers = {};
|
|
138
142
|
for (const provider of AI_CONNECTIONS_PROVIDERS) {
|
|
139
143
|
const byId = new Map();
|
|
140
|
-
for (const credential of
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
for (const credential of [
|
|
145
|
+
...(storeProducts[provider]?.credentials ?? []),
|
|
146
|
+
...(overlayProducts[provider]?.credentials ?? []),
|
|
147
|
+
]) {
|
|
148
|
+
const id = credentialResource.buildId({ id: credential.id });
|
|
149
|
+
const current = byId.get(id);
|
|
150
|
+
// Each persisted mutation advances the version; a later store echo can supersede it.
|
|
151
|
+
if (!current || credential.version >= current.version)
|
|
152
|
+
byId.set(id, credential);
|
|
145
153
|
}
|
|
146
154
|
carriers[provider] = [...byId.values()];
|
|
147
155
|
}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* parsers under `./client` are internal, and `export *` would publish them.
|
|
12
12
|
*/
|
|
13
13
|
export { AI_CONNECTIONS_PROVIDERS } from './client/types.js';
|
|
14
|
-
export
|
|
14
|
+
export { PLATFORM_MODEL_ROLES, matchesPlatformModelRole } from './client/gateway-model-roles.js';
|
|
15
|
+
export type { AiConnectAttempt, AiConnectionBeginOptions, AiConnectionsClient, AiConnectionsCredential, AiConnectionsMode, AiConnectionsProvider, AiConnectStatus, AiGatewayModel, AiGatewayCatalogModel, AiProviderAuthorizationMethod, AiProviderAuthorizationMethodId, AiProviderAuthorizationMethodsSummary, AiProviderConnectionSummary, AiProviderCredentialSummary, AiProviderOffering, AiProviderSummary, AiProviderSummaryStatus, AiQuotaSnapshot, AiQuotaWindow, CreateApiKeyCredentialInput, CreatedGatewayKey, CustomProviderModel, DiscoveredProviderModel, GatewayKeyRecord, ProviderModelDiscovery, TestProviderCredentialInput, UpdateProviderCredentialInput, } from './client/types.js';
|
|
15
16
|
export { AiConnectionsRequestError, aiConnectionsErrorCode, normalizeProxyUrl, redactProxyUrl, } from './client/normalize.js';
|
|
16
17
|
export { createAiConnectionsClient, resolveAiConnectionsApiBase } from './client/request.js';
|
|
17
18
|
/**
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* parsers under `./client` are internal, and `export *` would publish them.
|
|
12
12
|
*/
|
|
13
13
|
export { AI_CONNECTIONS_PROVIDERS } from './client/types.js';
|
|
14
|
+
export { PLATFORM_MODEL_ROLES, matchesPlatformModelRole } from './client/gateway-model-roles.js';
|
|
14
15
|
export { AiConnectionsRequestError, aiConnectionsErrorCode, normalizeProxyUrl, redactProxyUrl, } from './client/normalize.js';
|
|
15
16
|
export { createAiConnectionsClient, resolveAiConnectionsApiBase } from './client/request.js';
|
|
16
17
|
/**
|
package/node_modules/@undefineds.co/ai-connections/dist/contract/client/gateway-model-roles.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Platform routing roles published by the Gateway, independent of Pod model resources. */
|
|
2
|
+
export declare const PLATFORM_MODEL_ROLES: {
|
|
3
|
+
readonly smart: {
|
|
4
|
+
readonly id: "linx";
|
|
5
|
+
};
|
|
6
|
+
readonly fast: {
|
|
7
|
+
readonly id: "linx-lite";
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
/** Match only the Gateway's published bare IDs or its existing protocol namespace. */
|
|
11
|
+
export declare function matchesPlatformModelRole(modelId: string, role: keyof typeof PLATFORM_MODEL_ROLES): boolean;
|
package/node_modules/@undefineds.co/ai-connections/dist/contract/client/gateway-model-roles.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Platform routing roles published by the Gateway, independent of Pod model resources. */
|
|
2
|
+
export const PLATFORM_MODEL_ROLES = {
|
|
3
|
+
smart: { id: 'linx' },
|
|
4
|
+
fast: { id: 'linx-lite' },
|
|
5
|
+
};
|
|
6
|
+
/** Match only the Gateway's published bare IDs or its existing protocol namespace. */
|
|
7
|
+
export function matchesPlatformModelRole(modelId, role) {
|
|
8
|
+
const normalized = modelId.toLowerCase();
|
|
9
|
+
const id = PLATFORM_MODEL_ROLES[role].id;
|
|
10
|
+
return normalized === id || normalized === `undefineds/${id}`;
|
|
11
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AiConnectAttempt, AiConnectionsCredential, AiConnectionsProvider, AiGatewayModel, AiProviderAuthorizationMethodsSummary, AiProviderCredentialSummary, AiProviderSummary, CustomProviderModel, GatewayKeyRecord, ProviderModelDiscovery } from './types.js';
|
|
1
|
+
import type { AiConnectAttempt, AiConnectionsCredential, AiConnectionsProvider, AiGatewayModel, AiGatewayCatalogModel, AiProviderAuthorizationMethodsSummary, AiProviderCredentialSummary, AiProviderSummary, CustomProviderModel, GatewayKeyRecord, ProviderModelDiscovery } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Everything that turns untrusted API payloads, user input and failures into the
|
|
4
4
|
* shapes the rest of the app can rely on: proxy/pod URL normalising, error text,
|
|
@@ -41,6 +41,7 @@ export declare function assertProvider(provider: string): asserts provider is Ai
|
|
|
41
41
|
export declare function parseGatewayKeyRecord(value: unknown): GatewayKeyRecord | undefined;
|
|
42
42
|
export declare function parseCustomModelList(value: unknown): CustomProviderModel[];
|
|
43
43
|
export declare function parseGatewayModel(value: unknown): AiGatewayModel | undefined;
|
|
44
|
+
export declare function parseGatewayCatalogModel(value: unknown): AiGatewayCatalogModel | undefined;
|
|
44
45
|
export declare function parseProviderSummaries(values: unknown[]): AiProviderSummary[];
|
|
45
46
|
export declare function parseAuthorizationMethodsSummary(value: unknown): AiProviderAuthorizationMethodsSummary | undefined;
|
|
46
47
|
export declare function parseProviderCredentialSummary(value: unknown): AiProviderCredentialSummary | undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PROVIDER_LABELS } from '../provider-catalog.js';
|
|
2
2
|
import { AI_MODEL_CLASS } from '@undefineds.co/models';
|
|
3
3
|
import { AI_CONNECTIONS_PROVIDERS } from './types.js';
|
|
4
|
+
import { PLATFORM_MODEL_ROLES, matchesPlatformModelRole } from './gateway-model-roles.js';
|
|
4
5
|
/**
|
|
5
6
|
* Everything that turns untrusted API payloads, user input and failures into the
|
|
6
7
|
* shapes the rest of the app can rely on: proxy/pod URL normalising, error text,
|
|
@@ -188,6 +189,15 @@ export function parseGatewayModel(value) {
|
|
|
188
189
|
capabilities: modelCapabilitiesFromWire(value),
|
|
189
190
|
});
|
|
190
191
|
}
|
|
192
|
+
export function parseGatewayCatalogModel(value) {
|
|
193
|
+
if (!isRecord(value) || typeof value.id !== 'string' || !value.id)
|
|
194
|
+
return undefined;
|
|
195
|
+
return compactObject({
|
|
196
|
+
id: value.id,
|
|
197
|
+
displayName: stringValue(value.displayName) ?? stringValue(value.display_name) ?? stringValue(value.name),
|
|
198
|
+
provider: stringValue(value.provider) ?? stringValue(value.providerId) ?? stringValue(value.owned_by),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
191
201
|
function modalitiesFromWire(value, direction) {
|
|
192
202
|
if (!isRecord(value))
|
|
193
203
|
return undefined;
|
|
@@ -226,11 +236,8 @@ function modelCapabilitiesFromWire(value) {
|
|
|
226
236
|
return capabilities.length > 0 ? capabilities : undefined;
|
|
227
237
|
}
|
|
228
238
|
function isPlatformModelId(modelId) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|| normalized === 'linx-lite'
|
|
232
|
-
|| normalized === 'undefineds/linx'
|
|
233
|
-
|| normalized === 'undefineds/linx-lite';
|
|
239
|
+
return Object.keys(PLATFORM_MODEL_ROLES)
|
|
240
|
+
.some(role => matchesPlatformModelRole(modelId, role));
|
|
234
241
|
}
|
|
235
242
|
function providerValue(value) {
|
|
236
243
|
if (typeof value !== 'string')
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AiConnectionsRequestError, assertProvider, compactObject, isDefined, isRecord, parseAuthorizationMethodsSummary, parseConnectAttempt, parseCredential, parseCustomModelList, parseGatewayKeyRecord, parseGatewayModel, parseModelDiscovery, parseProviderCredentialSummary, parseProviderSummaries, readJson, sanitizePublicObject, } from './normalize.js';
|
|
1
|
+
import { AiConnectionsRequestError, assertProvider, compactObject, isDefined, isRecord, parseAuthorizationMethodsSummary, parseConnectAttempt, parseCredential, parseCustomModelList, parseGatewayKeyRecord, parseGatewayModel, parseGatewayCatalogModel, parseModelDiscovery, parseProviderCredentialSummary, parseProviderSummaries, readJson, sanitizePublicObject, } from './normalize.js';
|
|
2
2
|
/**
|
|
3
3
|
* The HTTP client itself: one factory over the Pod base URL and an authenticated
|
|
4
4
|
* fetch, with every response normalised by `normalize.ts`.
|
|
@@ -37,8 +37,9 @@ export function createAiConnectionsClient({ webId, podBaseUrl, authenticatedFetc
|
|
|
37
37
|
const payload = await request(`${providerPath(provider)}${path}`, method, body, { provider });
|
|
38
38
|
return parseConnectAttempt(payload, provider);
|
|
39
39
|
};
|
|
40
|
+
const requestGatewayModels = () => request('/v1/models', 'GET');
|
|
40
41
|
const listGatewayModels = async () => {
|
|
41
|
-
const payload = await
|
|
42
|
+
const payload = await requestGatewayModels();
|
|
42
43
|
return Array.isArray(payload.data)
|
|
43
44
|
? payload.data.map(parseGatewayModel).filter(isDefined)
|
|
44
45
|
: [];
|
|
@@ -72,6 +73,13 @@ export function createAiConnectionsClient({ webId, podBaseUrl, authenticatedFetc
|
|
|
72
73
|
},
|
|
73
74
|
listModels: listGatewayModels,
|
|
74
75
|
listGatewayModels,
|
|
76
|
+
async listGatewayCatalogModels() {
|
|
77
|
+
const payload = await requestGatewayModels();
|
|
78
|
+
if (!isRecord(payload) || !Array.isArray(payload.data)) {
|
|
79
|
+
throw new Error('AI Connection returned an invalid Gateway model catalog response');
|
|
80
|
+
}
|
|
81
|
+
return payload.data.map(parseGatewayCatalogModel).filter(isDefined);
|
|
82
|
+
},
|
|
75
83
|
async listGatewayKeys() {
|
|
76
84
|
const payload = await request('/api/ai/gateway/keys', 'GET');
|
|
77
85
|
return Array.isArray(payload.data)
|
|
@@ -106,6 +106,12 @@ export interface AiGatewayModel extends AiConnectionsModelSelection {
|
|
|
106
106
|
outputModalities?: string[];
|
|
107
107
|
capabilities?: string[];
|
|
108
108
|
}
|
|
109
|
+
/** Public Gateway directory entries can belong to a platform or an unknown provider. */
|
|
110
|
+
export interface AiGatewayCatalogModel {
|
|
111
|
+
id: string;
|
|
112
|
+
displayName?: string;
|
|
113
|
+
provider?: string;
|
|
114
|
+
}
|
|
109
115
|
/**
|
|
110
116
|
* What the provider's own model list said a model is.
|
|
111
117
|
*
|
|
@@ -238,6 +244,8 @@ export interface CreateApiKeyCredentialInput {
|
|
|
238
244
|
compatibility?: 'auto' | 'openai' | 'anthropic';
|
|
239
245
|
}
|
|
240
246
|
export interface UpdateProviderCredentialInput {
|
|
247
|
+
/** Nonempty replacement only; omission preserves the existing secret. */
|
|
248
|
+
apiKey?: string;
|
|
241
249
|
expectedVersion: number;
|
|
242
250
|
label?: string;
|
|
243
251
|
enabled?: boolean;
|
|
@@ -273,6 +281,8 @@ export interface AiConnectionsClient {
|
|
|
273
281
|
listModels(): Promise<AiGatewayModel[]>;
|
|
274
282
|
/** Active Gateway routing projection, independent of a host's Pod catalog. */
|
|
275
283
|
listGatewayModels?(): Promise<AiGatewayModel[]>;
|
|
284
|
+
/** Complete Gateway directory, including platform routing roles and unattributed entries. */
|
|
285
|
+
listGatewayCatalogModels?(): Promise<AiGatewayCatalogModel[]>;
|
|
276
286
|
listGatewayKeys(): Promise<GatewayKeyRecord[]>;
|
|
277
287
|
createGatewayKey(input: {
|
|
278
288
|
name: string;
|
|
@@ -115,6 +115,7 @@ export interface AiConnectionsPodStore {
|
|
|
115
115
|
saveOAuthCredential?(provider: string, input: AiConnectionsOAuthCredential): Promise<unknown>;
|
|
116
116
|
updateOAuthCredential?(provider: string, credentialId: string, expectedVersion: number, input: AiConnectionsOAuthCredential): Promise<unknown>;
|
|
117
117
|
updateProviderCredential?(provider: string, credentialId: string, input: {
|
|
118
|
+
apiKey?: string;
|
|
118
119
|
expectedVersion: number;
|
|
119
120
|
label?: string;
|
|
120
121
|
enabled?: boolean;
|
|
@@ -216,6 +216,7 @@ function rowBelongsToDocument(descriptor, documentUrl, podUrl, row) {
|
|
|
216
216
|
|
|
217
217
|
// src/mapping.ts
|
|
218
218
|
var XSD = "http://www.w3.org/2001/XMLSchema#";
|
|
219
|
+
var ABSOLUTE_URI = /^[a-z][a-z0-9+.-]*:/iu;
|
|
219
220
|
function tableColumnPredicates(table) {
|
|
220
221
|
const mapping = table.getMapping();
|
|
221
222
|
const predicates = new Map;
|
|
@@ -403,6 +404,47 @@ function descriptorRowToColumnValues(descriptor, table, row, options) {
|
|
|
403
404
|
function isUriArrayField(field) {
|
|
404
405
|
return field.array === true && field.type === "uri";
|
|
405
406
|
}
|
|
407
|
+
function normalizeUriArray(value, document) {
|
|
408
|
+
return (Array.isArray(value) ? value : [value]).filter((item) => typeof item === "string" && item.length > 0).map((item) => ABSOLUTE_URI.test(item) ? item : new URL(item, document).toString());
|
|
409
|
+
}
|
|
410
|
+
function normalizeMutationRow(descriptor, table, row, options) {
|
|
411
|
+
const normalized = { ...row };
|
|
412
|
+
const record = descriptorRowToColumnValues(descriptor, table, row, { resourceId: options.resourceId });
|
|
413
|
+
const bindings = fieldBindings(descriptor, table);
|
|
414
|
+
const tables = new Set([table]);
|
|
415
|
+
for (const candidate of Object.values(options.database.schema ?? {})) {
|
|
416
|
+
if (candidate && typeof candidate === "object" && "config" in candidate && "columns" in candidate) {
|
|
417
|
+
tables.add(candidate);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const tableNameRegistry = new Map;
|
|
421
|
+
const tableRegistry = new Map;
|
|
422
|
+
for (const entry of tables) {
|
|
423
|
+
tableNameRegistry.set(entry.config.name, entry);
|
|
424
|
+
const type = entry.getType();
|
|
425
|
+
tableRegistry.set(type, [...tableRegistry.get(type) ?? [], entry]);
|
|
426
|
+
}
|
|
427
|
+
for (const [field, binding] of bindings) {
|
|
428
|
+
const value = normalized[field];
|
|
429
|
+
if (binding.descriptor.type !== "uri" || value === undefined || value === null)
|
|
430
|
+
continue;
|
|
431
|
+
if (binding.descriptor.array) {
|
|
432
|
+
normalized[field] = normalizeUriArray(value, options.document);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
if (!binding.column || typeof value !== "string" || !value || ABSOLUTE_URI.test(value))
|
|
436
|
+
continue;
|
|
437
|
+
const resolver = options.database.getDialect().getUriResolver();
|
|
438
|
+
normalized[field] = resolver.resolveLink(value, table.columns[binding.column], {
|
|
439
|
+
baseUri: options.podUrl,
|
|
440
|
+
tableRegistry,
|
|
441
|
+
tableNameRegistry,
|
|
442
|
+
currentTable: table,
|
|
443
|
+
record
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return normalized;
|
|
447
|
+
}
|
|
406
448
|
|
|
407
449
|
// src/read.ts
|
|
408
450
|
async function selectSubjectRows(table, database) {
|
|
@@ -609,21 +651,14 @@ function reconcilePendingWrites(pending, next, hashOf, writeOnly = EMPTY_WRITE_O
|
|
|
609
651
|
}
|
|
610
652
|
}
|
|
611
653
|
var EMPTY_WRITE_ONLY = new Set;
|
|
612
|
-
function absoluteUri(value, base) {
|
|
613
|
-
if (typeof value !== "string" || value.length === 0)
|
|
614
|
-
return;
|
|
615
|
-
if (/^[a-z][a-z0-9+.-]*:/iu.test(value))
|
|
616
|
-
return value;
|
|
617
|
-
return new URL(value, base).toString();
|
|
618
|
-
}
|
|
619
654
|
async function writeField(context, fieldName, options) {
|
|
620
655
|
const field = context.descriptor.fields[fieldName];
|
|
621
656
|
if (!field || !isUriArrayField(field)) {
|
|
622
657
|
throw new PodCollectionError("write_field_not_uri_array", `field ${fieldName} is not array+uri; it must go through the ORM`);
|
|
623
658
|
}
|
|
624
659
|
const documentUrl = documentOfIri(context.document);
|
|
625
|
-
const previous = (
|
|
626
|
-
const next = (
|
|
660
|
+
const previous = normalizeUriArray(options.previous, documentUrl);
|
|
661
|
+
const next = normalizeUriArray(options.next, documentUrl);
|
|
627
662
|
const triples = (iris) => iris.map((iri) => `<${options.subjectIri}> <${field.predicate}> <${iri}> .`).join(`
|
|
628
663
|
`);
|
|
629
664
|
const operations = [
|
|
@@ -669,10 +704,16 @@ function createMutationHandlers(context) {
|
|
|
669
704
|
return {
|
|
670
705
|
onInsert: async ({ transaction }) => {
|
|
671
706
|
for (const mutation of transaction.mutations) {
|
|
672
|
-
const row = mutation.modified;
|
|
673
707
|
const key = String(mutation.key);
|
|
674
708
|
const resourceId = resourceIdForRow(descriptor, context.document, context.podUrl, key);
|
|
675
709
|
const subjectIri = subjectIriForRow(descriptor, context.document, key);
|
|
710
|
+
const row = normalizeMutationRow(descriptor, table, mutation.modified, {
|
|
711
|
+
database: context.database,
|
|
712
|
+
podUrl: context.podUrl,
|
|
713
|
+
document: context.document,
|
|
714
|
+
resourceId
|
|
715
|
+
});
|
|
716
|
+
Object.assign(mutation.modified, row);
|
|
676
717
|
pending.register({
|
|
677
718
|
key,
|
|
678
719
|
intent: row,
|
|
@@ -693,10 +734,16 @@ function createMutationHandlers(context) {
|
|
|
693
734
|
for (const mutation of transaction.mutations) {
|
|
694
735
|
const key = String(mutation.key);
|
|
695
736
|
const original = mutation.original;
|
|
696
|
-
const modified = mutation.modified;
|
|
697
737
|
const changes = mutation.changes;
|
|
698
738
|
const resourceId = resourceIdForRow(descriptor, context.document, context.podUrl, key);
|
|
699
739
|
const subjectIri = subjectIriForRow(descriptor, context.document, key);
|
|
740
|
+
const modified = normalizeMutationRow(descriptor, table, mutation.modified, {
|
|
741
|
+
database: context.database,
|
|
742
|
+
podUrl: context.podUrl,
|
|
743
|
+
document: context.document,
|
|
744
|
+
resourceId
|
|
745
|
+
});
|
|
746
|
+
Object.assign(mutation.modified, modified);
|
|
700
747
|
const beforeHash = original && Object.keys(original).length > 0 ? hashOf(original) : undefined;
|
|
701
748
|
pending.register({
|
|
702
749
|
key,
|
|
@@ -706,13 +753,13 @@ function createMutationHandlers(context) {
|
|
|
706
753
|
});
|
|
707
754
|
await runWrite(key, async () => {
|
|
708
755
|
const columnChanges = {};
|
|
709
|
-
for (const
|
|
756
|
+
for (const field of Object.keys(changes)) {
|
|
710
757
|
if (uriArrayFieldNames.has(field))
|
|
711
758
|
continue;
|
|
712
759
|
const column = columnOf(field);
|
|
713
760
|
if (column === undefined)
|
|
714
761
|
continue;
|
|
715
|
-
columnChanges[column] =
|
|
762
|
+
columnChanges[column] = modified[field];
|
|
716
763
|
}
|
|
717
764
|
if (Object.keys(columnChanges).length > 0) {
|
|
718
765
|
const updated = await context.database.updateById(table, resourceId, columnChanges);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyPodTable } from '@undefineds.co/drizzle-solid';
|
|
1
|
+
import type { AnyPodTable, SolidDatabase } from '@undefineds.co/drizzle-solid';
|
|
2
2
|
import type { PodModelDescriptor, PodModelFieldDescriptor } from '@undefineds.co/models';
|
|
3
3
|
import type { PodSubjectRow, RowOf } from './types.js';
|
|
4
4
|
/** RDF term(只覆盖本层会产生的两种)。 */
|
|
@@ -74,5 +74,20 @@ export declare function descriptorRowToColumnValues(descriptor: PodModelDescript
|
|
|
74
74
|
}): Record<string, unknown>;
|
|
75
75
|
/** 该字段是否走 §4.1 的 PATCH 旁路:`array: true` + `type: 'uri'`。 */
|
|
76
76
|
export declare function isUriArrayField(field: PodModelFieldDescriptor): boolean;
|
|
77
|
+
/** URI arrays use the existing document-relative PATCH representation. */
|
|
78
|
+
export declare function normalizeUriArray(value: unknown, document: string): string[];
|
|
79
|
+
/**
|
|
80
|
+
* The pending intent must use the same URI representation as the ORM's RDF read.
|
|
81
|
+
* Resolve declared scalar links with the current database's public resolver/schema;
|
|
82
|
+
* array links retain writeField's document-relative PATCH semantics.
|
|
83
|
+
*/
|
|
84
|
+
export declare function normalizeMutationRow<D extends PodModelDescriptor, R extends object>(descriptor: D, table: AnyPodTable, row: R, options: {
|
|
85
|
+
database: Pick<SolidDatabase, 'getDialect'> & {
|
|
86
|
+
schema?: Record<string, unknown>;
|
|
87
|
+
};
|
|
88
|
+
podUrl: string;
|
|
89
|
+
document: string;
|
|
90
|
+
resourceId: string;
|
|
91
|
+
}): R;
|
|
77
92
|
/** descriptor 里所有走 PATCH 旁路的字段。 */
|
|
78
93
|
export declare function uriArrayFields(descriptor: PodModelDescriptor): string[];
|
|
@@ -19,6 +19,7 @@ import { rowKeyOf, rowKeyVariable } from './layout.js';
|
|
|
19
19
|
* 交给 drizzle-solid 自己格式化。
|
|
20
20
|
*/
|
|
21
21
|
const XSD = 'http://www.w3.org/2001/XMLSchema#';
|
|
22
|
+
const ABSOLUTE_URI = /^[a-z][a-z0-9+.-]*:/iu;
|
|
22
23
|
/** 表列 → 谓词(跳过 `@id` 虚列)。 */
|
|
23
24
|
export function tableColumnPredicates(table) {
|
|
24
25
|
const mapping = table.getMapping();
|
|
@@ -279,6 +280,56 @@ export function descriptorRowToColumnValues(descriptor, table, row, options) {
|
|
|
279
280
|
export function isUriArrayField(field) {
|
|
280
281
|
return field.array === true && field.type === 'uri';
|
|
281
282
|
}
|
|
283
|
+
/** URI arrays use the existing document-relative PATCH representation. */
|
|
284
|
+
export function normalizeUriArray(value, document) {
|
|
285
|
+
return (Array.isArray(value) ? value : [value])
|
|
286
|
+
.filter((item) => typeof item === 'string' && item.length > 0)
|
|
287
|
+
.map(item => ABSOLUTE_URI.test(item) ? item : new URL(item, document).toString());
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* The pending intent must use the same URI representation as the ORM's RDF read.
|
|
291
|
+
* Resolve declared scalar links with the current database's public resolver/schema;
|
|
292
|
+
* array links retain writeField's document-relative PATCH semantics.
|
|
293
|
+
*/
|
|
294
|
+
export function normalizeMutationRow(descriptor, table, row, options) {
|
|
295
|
+
const normalized = { ...row };
|
|
296
|
+
const record = descriptorRowToColumnValues(descriptor, table, row, { resourceId: options.resourceId });
|
|
297
|
+
const bindings = fieldBindings(descriptor, table);
|
|
298
|
+
// Registries are derived only from the actual schema registered with this DB.
|
|
299
|
+
const tables = new Set([table]);
|
|
300
|
+
for (const candidate of Object.values(options.database.schema ?? {})) {
|
|
301
|
+
if (candidate && typeof candidate === 'object' && 'config' in candidate && 'columns' in candidate) {
|
|
302
|
+
tables.add(candidate);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const tableNameRegistry = new Map();
|
|
306
|
+
const tableRegistry = new Map();
|
|
307
|
+
for (const entry of tables) {
|
|
308
|
+
tableNameRegistry.set(entry.config.name, entry);
|
|
309
|
+
const type = entry.getType();
|
|
310
|
+
tableRegistry.set(type, [...(tableRegistry.get(type) ?? []), entry]);
|
|
311
|
+
}
|
|
312
|
+
for (const [field, binding] of bindings) {
|
|
313
|
+
const value = normalized[field];
|
|
314
|
+
if (binding.descriptor.type !== 'uri' || value === undefined || value === null)
|
|
315
|
+
continue;
|
|
316
|
+
if (binding.descriptor.array) {
|
|
317
|
+
normalized[field] = normalizeUriArray(value, options.document);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
if (!binding.column || typeof value !== 'string' || !value || ABSOLUTE_URI.test(value))
|
|
321
|
+
continue;
|
|
322
|
+
const resolver = options.database.getDialect().getUriResolver();
|
|
323
|
+
normalized[field] = resolver.resolveLink(value, table.columns[binding.column], {
|
|
324
|
+
baseUri: options.podUrl,
|
|
325
|
+
tableRegistry,
|
|
326
|
+
tableNameRegistry,
|
|
327
|
+
currentTable: table,
|
|
328
|
+
record,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return normalized;
|
|
332
|
+
}
|
|
282
333
|
/** descriptor 里所有走 PATCH 旁路的字段。 */
|
|
283
334
|
export function uriArrayFields(descriptor) {
|
|
284
335
|
return Object.entries(descriptor.fields)
|