dsh-plugin-toolkit 0.9.3 → 0.9.4

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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.9.4
2
+
3
+ - The client module now declares `inject: ['slots', 'locale']` and guards every
4
+ service it reads. Without the list the loader calls `apply()` before those
5
+ services exist; the throw fails the **whole web boot**, and the app then reports
6
+ that third-party plugins are unsupported and starts with them disabled. That is
7
+ what one unguarded `ctx.locale.register` did to a real profile.
8
+ - `verify` now calls the client module's `apply()` with a bare context and refuses
9
+ a face without an `inject` list, so this class of failure is caught before
10
+ install instead of at boot.
11
+ - `init` scaffolds the same guards, for the same reason.
12
+
1
13
  ## 0.9.3
2
14
 
3
15
  - The panel needed one more thing than `dsh.client`: the host resolves the bundle
package/client.js CHANGED
@@ -137,25 +137,43 @@ window.__ModuleLoader__.load({
137
137
  }
138
138
 
139
139
  function apply(ctx) {
140
- injectStyles(document);
141
- ctx.locale.register(NS, COPY);
142
- ctx.inject(['sidebarRightTabs', 'sidebarRight'], (tabCtx) => {
143
- if (tabCtx.slots === undefined || tabCtx.sidebarRightTabs === undefined) return;
144
- tabCtx.effect(() => tabCtx.slots.inject('sidebar.right.pane.tab', () => tabCtx.slots.register({
145
- name: 'sidebar.right.pane.tab',
146
- key: NS,
147
- locale: NS,
148
- }, Panel)));
149
- tabCtx.effect(() => tabCtx.sidebarRightTabs.register({
150
- id: NS,
151
- kind: KIND,
152
- priority: 'extension',
153
- title: () => 'Plugin doctor',
154
- }));
155
- });
140
+ // A panel that cannot find its services must degrade, not fail the boot:
141
+ // an exception here fails the whole web boot and takes every other plugin
142
+ // down with it. Every service below is optional for that reason.
143
+ try {
144
+ injectStyles(typeof document === 'undefined' ? undefined : document);
145
+ } catch {
146
+ // No document: the styles are cosmetic.
147
+ }
148
+ try {
149
+ if (ctx.locale !== undefined && typeof ctx.locale.register === 'function') ctx.locale.register(NS, COPY);
150
+ } catch {
151
+ // The panel keeps its English strings.
152
+ }
153
+ try {
154
+ if (typeof ctx.inject !== 'function') return;
155
+ ctx.inject(['sidebarRightTabs', 'sidebarRight'], (tabCtx) => {
156
+ if (tabCtx === undefined || tabCtx.slots === undefined || tabCtx.sidebarRightTabs === undefined) return;
157
+ tabCtx.effect(() => tabCtx.slots.inject('sidebar.right.pane.tab', () => tabCtx.slots.register({
158
+ name: 'sidebar.right.pane.tab',
159
+ key: NS,
160
+ locale: NS,
161
+ }, Panel)));
162
+ tabCtx.effect(() => tabCtx.sidebarRightTabs.register({
163
+ id: NS,
164
+ kind: KIND,
165
+ priority: 'extension',
166
+ title: () => 'Plugin doctor',
167
+ }));
168
+ });
169
+ } catch {
170
+ // No sidebar: the plugin still loads, it just has no place to show.
171
+ }
156
172
  }
157
173
 
158
- module.exports = { apply, name: 'dsh-plugin-toolkit' };
174
+ // The services the tab body reads. Declaring them lets the loader wait for
175
+ // them instead of calling apply() before they exist.
176
+ module.exports = { inject: ['slots', 'locale'], apply, name: 'dsh-plugin-toolkit' };
159
177
  return module.exports;
160
178
  },
161
179
  });
package/lib/init.mjs CHANGED
@@ -86,10 +86,18 @@ function client({ name, title }) {
86
86
  }
87
87
 
88
88
  return {
89
+ // The services this panel reads. Declaring them makes the loader wait,
90
+ // and guarding them keeps a missing service from failing the whole boot.
91
+ inject: ['slots', 'locale'],
89
92
  apply(ctx) {
90
- ctx.locale.register(NS, COPY);
93
+ try {
94
+ if (ctx.locale !== undefined && typeof ctx.locale.register === 'function') ctx.locale.register(NS, COPY);
95
+ } catch {
96
+ // keep the English strings
97
+ }
98
+ if (typeof ctx.inject !== 'function') return undefined;
91
99
  const dispose = ctx.inject(['sidebarRightTabs', 'sidebarRight'], (tabCtx) => {
92
- if (tabCtx.slots === undefined) return;
100
+ if (tabCtx === undefined || tabCtx.slots === undefined) return;
93
101
  const tr = tabCtx.locale.bind(NS);
94
102
  const offType = tabCtx.sidebarRightTabs.register({
95
103
  id: NS,
package/lib/verify.mjs CHANGED
@@ -165,7 +165,30 @@ export async function verify({ dir, mount = true }) {
165
165
  // running out of npx's cache, whose dependencies are not the panel's.
166
166
  const panel = await mountClient({ file: entryFile, resolveFrom: dir });
167
167
  const text = panel.text();
168
+ // The web boot calls the module's apply() before every service exists. An
169
+ // exception there fails the whole boot and takes the other plugins down
170
+ // with it, so check the face and the bare-context call here.
171
+ const { loadBundle } = await import('./testkit/index.mjs');
172
+ const face = loadBundle(entryFile);
173
+ const faceProblems = [];
174
+ if (Array.isArray(face.inject) === false) {
175
+ faceProblems.push('the client module declares no inject list, so the loader calls apply() before its services exist');
176
+ }
177
+ try {
178
+ face.apply({});
179
+ } catch (error) {
180
+ faceProblems.push(`apply() throws when the services are absent, which fails the web boot: ${error.message}`);
181
+ }
168
182
  await panel.unmount();
183
+ if (faceProblems.length > 0) {
184
+ return {
185
+ ok: false,
186
+ stage: 'mount',
187
+ failures: faceProblems.map((message) => ({ check: 'boot', message })),
188
+ files: packed.files,
189
+ installed,
190
+ };
191
+ }
169
192
  return { ok: true, stage: 'mount', failures: [], files: packed.files, size: packed.size, installed, text };
170
193
  } catch (error) {
171
194
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-toolkit",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Preflight, release and test DeepSeek Harness plugins: checks what the host needs, waits for npm to actually serve the version, and keeps the market index honest.",
5
5
  "type": "module",
6
6
  "bin": {