@dsh-plugin/dsh-loader 1.0.0 → 1.1.0-dev.32278873528
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/README.md +251 -226
- package/{README.zh-CN.md → README.zh_CN.md} +227 -202
- package/bin/dshloader.mjs +42 -42
- package/cordis.patch.yml +8 -8
- package/dist/adapters/dsh-1-x.d.ts +23 -0
- package/dist/adapters/dsh-1-x.js +223 -0
- package/dist/adapters/dsh-1-x.js.map +1 -0
- package/dist/adapters/index.d.ts +22 -0
- package/dist/adapters/index.js +57 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/api.d.ts +13 -0
- package/dist/api.js +46 -0
- package/dist/api.js.map +1 -0
- package/dist/client.d.ts +157 -0
- package/dist/client.js +295 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +89 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +35 -0
- package/dist/registry.js +229 -0
- package/dist/registry.js.map +1 -0
- package/dist/services/services.d.ts +4 -0
- package/dist/services/services.js +28 -0
- package/dist/services/services.js.map +1 -0
- package/dist/services/settings.d.ts +41 -0
- package/dist/services/settings.js +161 -0
- package/dist/services/settings.js.map +1 -0
- package/dist/services/web.d.ts +10 -0
- package/dist/services/web.js +63 -0
- package/dist/services/web.js.map +1 -0
- package/dist/setup.d.ts +32 -0
- package/dist/setup.js +119 -0
- package/dist/setup.js.map +1 -0
- package/dist/types.d.ts +111 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/{src → dist}/version.js +5 -5
- package/dist/version.js.map +1 -0
- package/lib/client.js +283 -0
- package/lib/client.js.map +1 -0
- package/package.json +74 -59
- package/src/stable/agent.d.ts +1 -1
- package/src/stable/agent.js +2 -2
- package/src/stable/llm.d.ts +1 -1
- package/src/stable/llm.js +2 -2
- package/src/stable/runtime.d.ts +1 -1
- package/src/stable/runtime.js +5 -5
- package/src/stable/schema-form.d.ts +1 -1
- package/src/stable/schema-form.js +2 -2
- package/src/stable/settings.d.ts +1 -1
- package/src/stable/settings.js +2 -2
- package/src/stable/tools.d.ts +1 -1
- package/src/stable/tools.js +5 -5
- package/src/stable/ui-primitives.d.ts +4 -4
- package/src/stable/ui-primitives.js +6 -6
- package/src/stable/ui-settings.d.ts +6 -6
- package/src/stable/ui-settings.js +4 -4
- package/src/stable/ui-slots.d.ts +1 -1
- package/src/stable/ui-slots.js +2 -2
- package/src/stable/web-react.d.ts +1 -1
- package/src/stable/web-react.js +2 -2
- package/src/adapters/dsh-1-x.js +0 -236
- package/src/adapters/index.js +0 -59
- package/src/api.js +0 -56
- package/src/client.js +0 -330
- package/src/index.js +0 -105
- package/src/registry.js +0 -242
- package/src/services/services.js +0 -28
- package/src/services/settings.js +0 -174
- package/src/services/web.js +0 -71
- package/src/setup.mjs +0 -125
package/src/adapters/index.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// Adapter registration (design.md §7.4). Imports every host adapter and
|
|
2
|
-
// registers it on the provided AdapterRegistry. Kept as a function so tests
|
|
3
|
-
// can build a fresh registry per case.
|
|
4
|
-
import * as dsh1x from './dsh-1-x.js';
|
|
5
|
-
|
|
6
|
-
/** All host adapter factories, in registration order. */
|
|
7
|
-
export const hostAdapters = [
|
|
8
|
-
{ supports: dsh1x.supports, name: dsh1x.name, create: dsh1x.create },
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
/** Register every built-in host adapter onto a registry. */
|
|
12
|
-
export function registerHostAdapters(registry) {
|
|
13
|
-
for (const factory of hostAdapters) registry.register(factory);
|
|
14
|
-
return registry;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Client adapter metadata for dsh 1.x (consumed by src/client.js).
|
|
18
|
-
//
|
|
19
|
-
// packageAliases serves TWO roles:
|
|
20
|
-
// 1. **Stable name → real name** (primary): plugins import from stable
|
|
21
|
-
// names like '@dshloader/ui-primitives' and the adapter maps them to
|
|
22
|
-
// the real dsh package name for this version. When dsh renames a
|
|
23
|
-
// package, only the adapter changes — plugin source and bundle stay
|
|
24
|
-
// the same.
|
|
25
|
-
// 2. **Old real name → new real name** (fallback): if a plugin bundle
|
|
26
|
-
// was built before adopting stable names and still has
|
|
27
|
-
// `require('@deepseek-ai/dsh-client-ui-primitives')` baked in, the
|
|
28
|
-
// adapter can map the old real name to the new real name as a
|
|
29
|
-
// transition measure.
|
|
30
|
-
export const clientAdapters = [
|
|
31
|
-
{
|
|
32
|
-
supports: dsh1x.supports,
|
|
33
|
-
name: dsh1x.name,
|
|
34
|
-
moduleAliases: {
|
|
35
|
-
// deep source import that breaks when dsh ships no `src/` (fix 1).
|
|
36
|
-
'@deepseek-ai/dsh-client-runtime/src/client/sessions/context-provenance.ts':
|
|
37
|
-
'@deepseek-ai/dsh-client-runtime/client',
|
|
38
|
-
// stable module name (design.md §3.3.3).
|
|
39
|
-
'dsh/runtime/context-provenance': '@deepseek-ai/dsh-client-runtime/client',
|
|
40
|
-
},
|
|
41
|
-
// Stable package names → real dsh package names for dsh 1.x.
|
|
42
|
-
// Plugins import from @dsh-plugin/dsh-loader/* subpaths (e.g.
|
|
43
|
-
// '@dsh-plugin/dsh-loader/ui-primitives'); the __ModuleLoader__
|
|
44
|
-
// wrapper (installed by installClient) maps them to the real dsh
|
|
45
|
-
// package before hitting the module table. When dsh renames a
|
|
46
|
-
// package, only this table changes — plugin source and bundle stay
|
|
47
|
-
// the same.
|
|
48
|
-
packageAliases: {
|
|
49
|
-
// Client UI component libraries
|
|
50
|
-
'@dsh-plugin/dsh-loader/ui-primitives': '@deepseek-ai/dsh-client-ui-primitives',
|
|
51
|
-
'@dsh-plugin/dsh-loader/ui-slots': '@deepseek-ai/dsh-client-ui-slots',
|
|
52
|
-
'@dsh-plugin/dsh-loader/web-react': '@deepseek-ai/dsh-client-web-react',
|
|
53
|
-
'@dsh-plugin/dsh-loader/schema-form': '@deepseek-ai/dsh-client-schema-form',
|
|
54
|
-
'@dsh-plugin/dsh-loader/ui-settings': '@deepseek-ai/dsh-client-ui-settings/client',
|
|
55
|
-
// Client runtime
|
|
56
|
-
'@dsh-plugin/dsh-loader/runtime': '@deepseek-ai/dsh-client-runtime/client',
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
];
|
package/src/api.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// DshLoaderHostAPI construction (design.md §4.1 / §4.4).
|
|
2
|
-
//
|
|
3
|
-
// `createHostAPI` builds the `ctx.dshLoader` object exposed to other plugins.
|
|
4
|
-
// Each capability (settings / web / services) is constructed from the active
|
|
5
|
-
// adapter's overrides when present, otherwise from the default stable impl in
|
|
6
|
-
// src/services/*.js. The adapter's `apply()` is invoked separately by the
|
|
7
|
-
// bundle entry (src/index.js) so service aliases / bridge routes register via
|
|
8
|
-
// cordis effects and auto-recycle on fiber unload (design.md §4.4).
|
|
9
|
-
import { LOADER_VERSION } from './version.js';
|
|
10
|
-
import { createSettingsAPI } from './services/settings.js';
|
|
11
|
-
import { createWebAPI } from './services/web.js';
|
|
12
|
-
import { createServicesAPI } from './services/services.js';
|
|
13
|
-
import { installHostPackageAliases } from './adapters/dsh-1-x.js';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {{
|
|
17
|
-
* ctx: object,
|
|
18
|
-
* dshVersion: string,
|
|
19
|
-
* factory: { supports: string, name: string, create: Function },
|
|
20
|
-
* adapter?: object,
|
|
21
|
-
* exposeAllNamespaces?: boolean,
|
|
22
|
-
* whitelist?: Set<string>,
|
|
23
|
-
* hostPackageAliases?: Record<string, string>,
|
|
24
|
-
* }} opts
|
|
25
|
-
*/
|
|
26
|
-
export function createHostAPI({ ctx, dshVersion, factory, adapter, exposeAllNamespaces = false, whitelist, hostPackageAliases = {} }) {
|
|
27
|
-
const base = {
|
|
28
|
-
version: LOADER_VERSION,
|
|
29
|
-
dshVersion,
|
|
30
|
-
adapterVersion: factory.supports,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// Adapters may override any capability (design.md §4.4 HostAdapter.{settings,
|
|
34
|
-
// web, services}); otherwise the default stable impl is used.
|
|
35
|
-
const settings = adapter?.settings ?? createSettingsAPI({ ctx, exposeAllNamespaces, whitelist });
|
|
36
|
-
const web = adapter?.web ?? createWebAPI({ ctx });
|
|
37
|
-
const services = adapter?.services ?? createServicesAPI({ ctx });
|
|
38
|
-
|
|
39
|
-
// Runtime host package-name alias registration. The adapter's apply()
|
|
40
|
-
// installs the static set at boot; this method lets plugins add more
|
|
41
|
-
// aliases at runtime (e.g. for packages the adapter didn't know about).
|
|
42
|
-
const runtimeHostAliases = new Map(Object.entries(hostPackageAliases));
|
|
43
|
-
const registerPackageAlias = (oldName, newName) => {
|
|
44
|
-
runtimeHostAliases.set(oldName, newName);
|
|
45
|
-
// Re-install the hook with the updated map.
|
|
46
|
-
installHostPackageAliases(Object.fromEntries(runtimeHostAliases));
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
...base,
|
|
51
|
-
settings,
|
|
52
|
-
web,
|
|
53
|
-
services,
|
|
54
|
-
registerPackageAlias,
|
|
55
|
-
};
|
|
56
|
-
}
|
package/src/client.js
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
// dshloader client bundle entry (design.md §3.5 / §4.3 / §7.3).
|
|
2
|
-
//
|
|
3
|
-
// Loaded in the `immediately` prefetch tier (package.json `dsh.client`).
|
|
4
|
-
// Responsibilities:
|
|
5
|
-
// 1. Mount `window.__dshLoader__` with `require` / `registerModuleAlias` /
|
|
6
|
-
// `rpc.settings.*`.
|
|
7
|
-
// 2. Register module-alias factories via `window.__ModuleLoader__.load` so
|
|
8
|
-
// deep source imports (fix 1) and stable module names resolve to the
|
|
9
|
-
// public runtime client entry.
|
|
10
|
-
// 3. When `exposeAllNamespaces` is on, install a fetch interceptor that
|
|
11
|
-
// merges non-whitelisted namespaces into `/api/settings.describe` and
|
|
12
|
-
// routes non-whitelisted writes through the host bridge, echoing the
|
|
13
|
-
// request `rpcId` in the `{ type: 'server-response', rpcId, result }`
|
|
14
|
-
// envelope (fix 5, path 2 — mirrors dsh-upstream-fixes/lib/client.js).
|
|
15
|
-
//
|
|
16
|
-
// The module exports an `installClient` function for testability and runs an
|
|
17
|
-
// IIFE at the bottom for the real browser bundle.
|
|
18
|
-
import { LOADER_VERSION, LOG_PREFIX } from './version.js';
|
|
19
|
-
import { clientAdapters } from './adapters/index.js';
|
|
20
|
-
import { BRIDGE_PREFIX } from './adapters/dsh-1-x.js';
|
|
21
|
-
|
|
22
|
-
export class ModuleNotFoundError extends Error {
|
|
23
|
-
constructor(specifier) {
|
|
24
|
-
super(`${LOG_PREFIX} module not found: ${specifier}`);
|
|
25
|
-
this.name = 'ModuleNotFoundError';
|
|
26
|
-
this.specifier = specifier;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Pick the client adapter for the given dsh version. v1 ships a single
|
|
32
|
-
* client adapter (dsh 1.x); when more are added, swap this for a semver-based
|
|
33
|
-
* selection mirroring the host AdapterRegistry.
|
|
34
|
-
*/
|
|
35
|
-
function pickClientAdapter(dshVersion) {
|
|
36
|
-
return clientAdapters[0];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Build the `window.__dshLoader__` API object.
|
|
41
|
-
* @param {{
|
|
42
|
-
* dshVersion?: string,
|
|
43
|
-
* adapterVersion?: string,
|
|
44
|
-
* moduleAliases?: Record<string,string>,
|
|
45
|
-
* requireImpl?: (spec: string) => any,
|
|
46
|
-
* fetchBridge?: { describe: () => Promise<any>, write: (mode:string, payload:object) => Promise<any> },
|
|
47
|
-
* clientCtx?: object, // cordis client context (for services.get)
|
|
48
|
-
* }} opts
|
|
49
|
-
*/
|
|
50
|
-
export function createClientAPI(opts = {}) {
|
|
51
|
-
const aliases = new Map(Object.entries(opts.moduleAliases ?? {}));
|
|
52
|
-
const requireImpl = opts.requireImpl ?? ((spec) => {
|
|
53
|
-
throw new ModuleNotFoundError(spec);
|
|
54
|
-
});
|
|
55
|
-
const clientCtx = opts.clientCtx;
|
|
56
|
-
// Reuse the Map from installClient when provided so registerPackageAlias
|
|
57
|
-
// mutations are visible to the __ModuleLoader__ wrapper. Otherwise create
|
|
58
|
-
// a fresh Map from a plain object.
|
|
59
|
-
const packageAliases = opts.packageAliases instanceof Map
|
|
60
|
-
? opts.packageAliases
|
|
61
|
-
: new Map(Object.entries(opts.packageAliases ?? {}));
|
|
62
|
-
|
|
63
|
-
const api = {
|
|
64
|
-
version: LOADER_VERSION,
|
|
65
|
-
dshVersion: opts.dshVersion,
|
|
66
|
-
adapterVersion: opts.adapterVersion,
|
|
67
|
-
|
|
68
|
-
require(specifier) {
|
|
69
|
-
if (typeof specifier !== 'string') throw new ModuleNotFoundError(String(specifier));
|
|
70
|
-
const target = aliases.get(specifier);
|
|
71
|
-
if (target === undefined) {
|
|
72
|
-
throw new ModuleNotFoundError(specifier);
|
|
73
|
-
}
|
|
74
|
-
return requireImpl(target);
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
registerModuleAlias(alias, target) {
|
|
78
|
-
aliases.set(alias, target);
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Register a package-name alias for the client module loader's
|
|
83
|
-
* require(). When a plugin's bundle calls require('@old/pkg-name'),
|
|
84
|
-
* the loader's wrapped require remaps it to '@new/pkg-name' before
|
|
85
|
-
* hitting the module table. This lets dsh rename client packages
|
|
86
|
-
* across versions without forcing plugin bundles to rebuild.
|
|
87
|
-
*/
|
|
88
|
-
registerPackageAlias(oldName, newName) {
|
|
89
|
-
packageAliases.set(oldName, newName);
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Read a client-side cordis service by name.
|
|
94
|
-
* Proxies to `clientCtx.get(name)` — the same ctx.get plugins use
|
|
95
|
-
* inside their client `apply(ctx)`, but exposed through the stable
|
|
96
|
-
* dshloader surface so plugins don't depend on the cordis context
|
|
97
|
-
* shape directly.
|
|
98
|
-
*
|
|
99
|
-
* Only available when dshloader's client apply() received a ctx
|
|
100
|
-
* (cordis client boot). Returns undefined when ctx is not wired.
|
|
101
|
-
*/
|
|
102
|
-
services: {
|
|
103
|
-
get(name) {
|
|
104
|
-
if (clientCtx === undefined || typeof clientCtx.get !== 'function') return undefined;
|
|
105
|
-
return clientCtx.get(name);
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
rpc: opts.fetchBridge
|
|
110
|
-
? {
|
|
111
|
-
settings: {
|
|
112
|
-
describe: () => opts.fetchBridge.describe(),
|
|
113
|
-
update: (ns, section) => opts.fetchBridge.write('update', { ns, section }),
|
|
114
|
-
replace: (ns, section) => opts.fetchBridge.write('replace', { ns, section }),
|
|
115
|
-
mutate: (ns, ops) => opts.fetchBridge.write('mutate', { ns, ops }),
|
|
116
|
-
},
|
|
117
|
-
}
|
|
118
|
-
: undefined,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
return api;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Install dshloader into a browser-like environment.
|
|
126
|
-
* @param {{
|
|
127
|
-
* window?: any,
|
|
128
|
-
* dshVersion?: string,
|
|
129
|
-
* exposeAllNamespaces?: boolean,
|
|
130
|
-
* requireImpl?: (spec: string) => any,
|
|
131
|
-
* hostBridgePrefix?: string,
|
|
132
|
-
* clientCtx?: object, // cordis client context (passed from apply(ctx))
|
|
133
|
-
* }} [opts]
|
|
134
|
-
*/
|
|
135
|
-
export function installClient(opts = {}) {
|
|
136
|
-
const win = opts.window ?? (typeof window !== 'undefined' ? window : undefined);
|
|
137
|
-
if (win === undefined) return undefined;
|
|
138
|
-
|
|
139
|
-
const dshVersion = opts.dshVersion ?? win.__DSHLOADER_VERSION__ ?? undefined;
|
|
140
|
-
const adapter = pickClientAdapter(dshVersion);
|
|
141
|
-
const moduleAliases = { ...(adapter.moduleAliases ?? {}) };
|
|
142
|
-
// Merge adapter-declared package aliases with any passed via opts (opts
|
|
143
|
-
// win on conflict, so tests / runtime can override adapter defaults).
|
|
144
|
-
const packageAliases = new Map(Object.entries(adapter.packageAliases ?? {}));
|
|
145
|
-
if (opts.packageAliases) {
|
|
146
|
-
for (const [k, v] of Object.entries(opts.packageAliases)) packageAliases.set(k, v);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// fetch bridge (only meaningful when exposeAllNamespaces is on).
|
|
150
|
-
const exposeAllNamespaces = Boolean(
|
|
151
|
-
opts.exposeAllNamespaces ?? win.__DSHLOADER_CONFIG__?.exposeAllNamespaces,
|
|
152
|
-
);
|
|
153
|
-
const bridgePrefix = opts.hostBridgePrefix ?? BRIDGE_PREFIX;
|
|
154
|
-
|
|
155
|
-
const fetchBridge = exposeAllNamespaces
|
|
156
|
-
? {
|
|
157
|
-
describe: () => win.fetch(`${bridgePrefix}/settings/describe`, { headers: { accept: 'application/json' } }).then((r) => r.json()),
|
|
158
|
-
write: (mode, payload) =>
|
|
159
|
-
win.fetch(`${bridgePrefix}/settings/${mode}`, {
|
|
160
|
-
method: 'POST',
|
|
161
|
-
headers: { 'content-type': 'application/json' },
|
|
162
|
-
body: JSON.stringify(payload),
|
|
163
|
-
}).then((r) => r.json()),
|
|
164
|
-
}
|
|
165
|
-
: undefined;
|
|
166
|
-
|
|
167
|
-
const api = createClientAPI({
|
|
168
|
-
dshVersion,
|
|
169
|
-
adapterVersion: adapter.supports,
|
|
170
|
-
moduleAliases,
|
|
171
|
-
packageAliases,
|
|
172
|
-
requireImpl: opts.requireImpl ?? ((spec) => win.__dshNativeRequire__?.(spec)),
|
|
173
|
-
fetchBridge,
|
|
174
|
-
clientCtx: opts.clientCtx,
|
|
175
|
-
});
|
|
176
|
-
win.__dshLoader__ = api;
|
|
177
|
-
|
|
178
|
-
// Wrap __ModuleLoader__.load so every factory's require() function
|
|
179
|
-
// applies package-name aliases before hitting the module table. This
|
|
180
|
-
// must happen BEFORE registering module-alias factories below, because
|
|
181
|
-
// those factories also receive the wrapped require.
|
|
182
|
-
const loader = win.__ModuleLoader__;
|
|
183
|
-
if (loader && typeof loader.load === 'function') {
|
|
184
|
-
const originalLoad = loader.load.bind(loader);
|
|
185
|
-
loader.load = function dshloaderLoad(handoff) {
|
|
186
|
-
const wrappedFactory = (require) => {
|
|
187
|
-
const aliasedRequire = (spec) => {
|
|
188
|
-
const mapped = packageAliases.get(spec);
|
|
189
|
-
return require(mapped ?? spec);
|
|
190
|
-
};
|
|
191
|
-
return handoff.factory(aliasedRequire);
|
|
192
|
-
};
|
|
193
|
-
return originalLoad({ id: handoff.id, factory: wrappedFactory });
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Register module-alias factories with the client module loader (fix 1).
|
|
198
|
-
if (loader && typeof loader.load === 'function') {
|
|
199
|
-
for (const [aliasId, target] of Object.entries(moduleAliases)) {
|
|
200
|
-
loader.load({
|
|
201
|
-
id: aliasId,
|
|
202
|
-
factory: (require) => {
|
|
203
|
-
const mod = require(target);
|
|
204
|
-
// Preserve the deep import's expected named export shape.
|
|
205
|
-
if (aliasId.endsWith('context-provenance.ts') || aliasId.endsWith('context-provenance')) {
|
|
206
|
-
return { contextProvenance: mod.contextProvenance };
|
|
207
|
-
}
|
|
208
|
-
return mod;
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (exposeAllNamespaces) {
|
|
215
|
-
installSettingsFetchInterceptor(win, bridgePrefix);
|
|
216
|
-
console.warn(`${LOG_PREFIX} exposeAllNamespaces enabled: bypassing official settings whitelist`);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return api;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Fetch interceptor for settings namespace whitelist bypass (fix 5, path 2).
|
|
224
|
-
* Mirrors dsh-upstream-fixes/lib/client.js (127-223).
|
|
225
|
-
*
|
|
226
|
-
* - /api/settings.describe: merge non-whitelisted namespaces from the host
|
|
227
|
-
* bridge into the official response (rpcId/official fields preserved).
|
|
228
|
-
* - /api/settings.{update,mutate,replace}: route writes for namespaces the
|
|
229
|
-
* official proxy does NOT expose through the host bridge, and rebuild the
|
|
230
|
-
* response envelope as `{ type: 'server-response', rpcId, result }` so the
|
|
231
|
-
* official client can correlate the response with the request.
|
|
232
|
-
*/
|
|
233
|
-
export function installSettingsFetchInterceptor(win, bridgePrefix = BRIDGE_PREFIX) {
|
|
234
|
-
if (typeof win.fetch !== 'function') return () => {};
|
|
235
|
-
const originalFetch = win.fetch;
|
|
236
|
-
/** Namespaces the official proxy itself exposed (learned from describe). */
|
|
237
|
-
const officialExposed = new Set();
|
|
238
|
-
|
|
239
|
-
win.fetch = async function dshloaderFetch(input, init) {
|
|
240
|
-
let pathname = '';
|
|
241
|
-
try {
|
|
242
|
-
pathname = new URL(typeof input === 'string' ? input : input.url, win.location?.origin ?? 'http://localhost').pathname;
|
|
243
|
-
} catch {
|
|
244
|
-
pathname = String(input).split('?')[0];
|
|
245
|
-
}
|
|
246
|
-
const method = (init?.method ?? 'GET').toUpperCase();
|
|
247
|
-
|
|
248
|
-
// describe: merge bridge namespaces into the official response.
|
|
249
|
-
if (pathname === '/api/settings.describe') {
|
|
250
|
-
const response = await originalFetch(input, init);
|
|
251
|
-
try {
|
|
252
|
-
const body = await response.clone().json();
|
|
253
|
-
const namespaces = body?.result?.value?.namespaces;
|
|
254
|
-
if (!Array.isArray(namespaces)) return response;
|
|
255
|
-
for (const row of namespaces) {
|
|
256
|
-
if (typeof row?.ns === 'string') officialExposed.add(row.ns);
|
|
257
|
-
}
|
|
258
|
-
const extra = await originalFetch(`${bridgePrefix}/settings/describe`, {
|
|
259
|
-
headers: { accept: 'application/json' },
|
|
260
|
-
});
|
|
261
|
-
const extraBody = await extra.json();
|
|
262
|
-
if (extraBody?.ok !== true || !Array.isArray(extraBody.namespaces)) return response;
|
|
263
|
-
const seen = new Set(namespaces.map((row) => row.ns));
|
|
264
|
-
const merged = [...namespaces, ...extraBody.namespaces.filter((row) => !seen.has(row.ns))];
|
|
265
|
-
return new win.Response(
|
|
266
|
-
JSON.stringify({ ...body, result: { ...body.result, value: { ...body.result.value, namespaces: merged } } }),
|
|
267
|
-
{ status: 200, headers: { 'content-type': 'application/json' } },
|
|
268
|
-
);
|
|
269
|
-
} catch {
|
|
270
|
-
return response;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// writes: route non-whitelisted namespaces through the bridge, echo rpcId.
|
|
275
|
-
if (
|
|
276
|
-
(pathname === '/api/settings.update' || pathname === '/api/settings.mutate' || pathname === '/api/settings.replace') &&
|
|
277
|
-
method === 'POST'
|
|
278
|
-
) {
|
|
279
|
-
let rpcId = null;
|
|
280
|
-
let payload = null;
|
|
281
|
-
try {
|
|
282
|
-
const parsed = JSON.parse(String(init?.body ?? '{}'));
|
|
283
|
-
rpcId = parsed.rpcId;
|
|
284
|
-
payload = parsed.payload;
|
|
285
|
-
} catch {
|
|
286
|
-
/* fall through to the original endpoint */
|
|
287
|
-
}
|
|
288
|
-
const ns = typeof payload?.ns === 'string' ? payload.ns : '';
|
|
289
|
-
if (rpcId !== null && ns !== '' && officialExposed.size > 0 && !officialExposed.has(ns)) {
|
|
290
|
-
try {
|
|
291
|
-
const mode = pathname.slice('/api/settings.'.length);
|
|
292
|
-
const res = await originalFetch(`${bridgePrefix}/settings/${mode}`, {
|
|
293
|
-
method: 'POST',
|
|
294
|
-
headers: { 'content-type': 'application/json' },
|
|
295
|
-
body: JSON.stringify(payload),
|
|
296
|
-
});
|
|
297
|
-
const body = await res.json();
|
|
298
|
-
if (body?.result) {
|
|
299
|
-
return new win.Response(
|
|
300
|
-
JSON.stringify({ type: 'server-response', rpcId, result: body.result }),
|
|
301
|
-
{ status: 200, headers: { 'content-type': 'application/json' } },
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
} catch {
|
|
305
|
-
/* fall back to the original endpoint */
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return originalFetch(input, init);
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
return () => {
|
|
314
|
-
win.fetch = originalFetch;
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// ── cordis client-plugin entry ─────────────────────────────────────────
|
|
319
|
-
// dsh's client boot applies every registered bundle as a cordis plugin:
|
|
320
|
-
// the module must expose `apply` (third-party plugins / dsh-client-runtime
|
|
321
|
-
// all do `exports.apply = apply`). Without it the client boot fails with
|
|
322
|
-
// "invalid plugin, expect function or object with an \"apply\" method".
|
|
323
|
-
// `installClient` mounts window.__dshLoader__, registers module aliases,
|
|
324
|
-
// wires the client cordis ctx for `services.get`, and (when opted in)
|
|
325
|
-
// installs the settings fetch interceptor.
|
|
326
|
-
export const name = '@dsh-plugin/dsh-loader'
|
|
327
|
-
export const inject = []
|
|
328
|
-
export function apply(ctx) {
|
|
329
|
-
if (typeof window !== 'undefined') installClient({ window, clientCtx: ctx });
|
|
330
|
-
}
|
package/src/index.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
// dshloader host bundle entry (design.md §7.3 / §4.4).
|
|
2
|
-
//
|
|
3
|
-
// Exports the cordis function-plugin shape (`name`, `inject`, `apply`) and
|
|
4
|
-
// wires the adapter registry → version detection → stable API onto
|
|
5
|
-
// `ctx.dshLoader`. Service aliases / bridge routes register through
|
|
6
|
-
// ctx.reflect.provide / ctx.effect so cordis auto-recycles them on fiber
|
|
7
|
-
// unload — no manual dispose is required for v1's covered capabilities.
|
|
8
|
-
import { LOADER_VERSION, LOG_PREFIX } from './version.js';
|
|
9
|
-
import { readFileSync } from 'node:fs';
|
|
10
|
-
import { join, resolve } from 'node:path';
|
|
11
|
-
import { AdapterRegistry, detectDshVersion, UnsupportedDshVersionError } from './registry.js';
|
|
12
|
-
import { registerHostAdapters } from './adapters/index.js';
|
|
13
|
-
import { createHostAPI } from './api.js';
|
|
14
|
-
|
|
15
|
-
export const name = '@dsh-plugin/dsh-loader';
|
|
16
|
-
// dshloader itself depends on `webServer` so its fiber only activates once
|
|
17
|
-
// the real web server is provided — the alias then points `httpServer` at it.
|
|
18
|
-
export const inject = ['webServer'];
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Read the profile-level dshloader config (exposeAllNamespaces etc.).
|
|
22
|
-
* Sources, in priority order:
|
|
23
|
-
* 1. process.env.DSHLOADER_EXPOSE_ALL_SETTINGS=1
|
|
24
|
-
* 2. profile package.json `dsh.dshloader.exposeAllNamespaces`
|
|
25
|
-
* 3. profile package.json `dshLoader.settings.exposeAllNamespaces`
|
|
26
|
-
*/
|
|
27
|
-
export function readLoaderConfig({ profileDir } = {}) {
|
|
28
|
-
const envOn = process.env.DSHLOADER_EXPOSE_ALL_SETTINGS === '1' || process.env.DSHLOADER_EXPOSE_ALL_SETTINGS === 'true';
|
|
29
|
-
let pkgOn = false;
|
|
30
|
-
try {
|
|
31
|
-
// Best-effort: read the profile manifest if reachable via cwd.
|
|
32
|
-
const dir = profileDir ?? join(process.env.DSH_HOME ?? '', 'profiles', 'web');
|
|
33
|
-
const manifest = JSON.parse(readFileSync(join(resolve(dir), 'package.json'), 'utf8'));
|
|
34
|
-
pkgOn = Boolean(
|
|
35
|
-
manifest.dsh?.dshloader?.exposeAllNamespaces ??
|
|
36
|
-
manifest.dshLoader?.settings?.exposeAllNamespaces,
|
|
37
|
-
);
|
|
38
|
-
} catch {
|
|
39
|
-
/* ignore */
|
|
40
|
-
}
|
|
41
|
-
return { exposeAllNamespaces: envOn || pkgOn };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Build the registry + select an adapter for the current dsh version, without
|
|
46
|
-
* touching the cordis context. Exported for tests and the `info` CLI command.
|
|
47
|
-
*/
|
|
48
|
-
export function selectAdapter({ dshVersion, registry } = {}) {
|
|
49
|
-
const reg = registry ?? registerHostAdapters(new AdapterRegistry());
|
|
50
|
-
const version = dshVersion ?? detectDshVersion();
|
|
51
|
-
if (version === undefined) {
|
|
52
|
-
throw new UnsupportedDshVersionError(
|
|
53
|
-
`${LOG_PREFIX} could not detect dsh version; set DSHLOADER_DSH_VERSION or install @deepseek-ai/dsh`,
|
|
54
|
-
{ kind: 'too-new' },
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
const { factory, mode } = reg.select(version);
|
|
58
|
-
return { registry: reg, factory, mode, dshVersion: version };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Apply the selected adapter onto a cordis context and expose the stable API.
|
|
63
|
-
* @param {object} ctx cordis context
|
|
64
|
-
* @param {{ dshVersion?: string, config?: object, registry?: AdapterRegistry }} [opts]
|
|
65
|
-
* @returns {Promise<{ api: object, factory: object, mode: string, dshVersion: string }>}
|
|
66
|
-
*/
|
|
67
|
-
export async function applyAdapter(ctx, opts = {}) {
|
|
68
|
-
const config = opts.config ?? readLoaderConfig();
|
|
69
|
-
const selection = selectAdapter({ dshVersion: opts.dshVersion, registry: opts.registry });
|
|
70
|
-
const { factory, mode, dshVersion } = selection;
|
|
71
|
-
|
|
72
|
-
const adapter = factory.create(ctx, config);
|
|
73
|
-
await adapter.apply?.();
|
|
74
|
-
|
|
75
|
-
const api = createHostAPI({
|
|
76
|
-
ctx,
|
|
77
|
-
dshVersion,
|
|
78
|
-
factory,
|
|
79
|
-
adapter,
|
|
80
|
-
exposeAllNamespaces: config.exposeAllNamespaces,
|
|
81
|
-
hostPackageAliases: config.hostPackageAliases,
|
|
82
|
-
});
|
|
83
|
-
ctx.reflect.provide('dshLoader', api);
|
|
84
|
-
|
|
85
|
-
console.log(`${LOG_PREFIX} loaded adapter ${factory.name} for dsh ${dshVersion} (mode: ${mode})`);
|
|
86
|
-
console.log(`${LOG_PREFIX} registered stable API: settings, web, services`);
|
|
87
|
-
if (config.exposeAllNamespaces) {
|
|
88
|
-
console.warn(
|
|
89
|
-
`${LOG_PREFIX} exposeAllNamespaces enabled: bypassing official settings whitelist`,
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return { api, factory, mode, dshVersion };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** cordis function-plugin entry point. */
|
|
97
|
-
export async function apply(ctx) {
|
|
98
|
-
if (process.env.DSHLOADER_DISABLE === '1' || process.env.DSHLOADER_DISABLE === 'true') {
|
|
99
|
-
console.log(`${LOG_PREFIX} disabled by env, skipping`);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
await applyAdapter(ctx);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export { LOADER_VERSION };
|