dsh-graft-plugin 0.2.0 → 0.2.1

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 CHANGED
@@ -135,6 +135,7 @@ All optional, all on the plugin's row in your profile's `cordis.patch.yml`:
135
135
  |---|---|
136
136
  | `0.1.2-rc.1` – `0.1.0-rc.7` | tested, full feature set — the `graft viz` tab via `dsh-better-sidebar` |
137
137
  | `0.1.5-rc.2` / `0.1.5` | tested — chip, tools, `/graft` and auto-rebuild all work; on `0.1.5` the `graft viz` button opens the graph in the built-in right sidebar |
138
+ | `0.2.0-rc.2` | needs plugin `0.2.1`+ — earlier releases fail to activate (see Troubleshooting) |
138
139
 
139
140
  - Node 22+
140
141
  - the `graft` CLI on PATH (`npm install -g @nanonets/graft`)
@@ -168,6 +169,8 @@ registering "dsh-graft-plugin" via __ModuleLoader__.load
168
169
 
169
170
  **No chip appears and dsh started without errors.** The plugin is in the profile's `package.json` `dependencies` but not in `dsh.profile.bundles` — which happens under pnpm 10+ when the `add` ends non-zero on `ERR_PNPM_IGNORED_BUILDS`; dsh skips the bundle row in that case and starts up looking healthy. Fix: `pnpm approve-builds` in the profile directory (tick `dsh-graft-plugin`), then re-run `dsh plugin --profile web add dsh-graft-plugin`. If `pnpm approve-builds` reports nothing awaiting approval, check `pnpm-workspace.yaml` for an unanswered `dsh-graft-plugin` entry (pnpm's approval placeholder) and set its value to `true` first.
170
171
 
172
+ **On dsh 0.2 the plugin fails to load with `strict codec has no create() factory`, and dsh offers to remove it.** dsh 0.2 requires every strict remote codec to carry a `create()` schema factory; plugin releases before `0.2.1` only had the 0.1 `schema` key. Update to `0.2.1` or later, which carries both and so loads on 0.1 and 0.2.
173
+
171
174
  **`/graft` says the graft CLI is not on PATH.** The postinstall was blocked or skipped (see Install): `npm install -g @nanonets/graft`.
172
175
 
173
176
  ## Licence
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-graft-plugin",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Shows which graft index a dsh session is serving and whether it still matches the code, and gives the model graft_ask, graft_grep, graft_callers, graft_skeleton and graft_map as native tools.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -256,6 +256,42 @@ check('that dependency is soft too', clientSource.includes('ctx.inject(["sidebar
256
256
  check('the body reads the session from either host', clientSource.includes('props?.sessionId ?? props?.scope?.sessionId'))
257
257
  check('the browser tab survives only as the last resort', clientSource.includes('neither sidebar'))
258
258
 
259
+ // The remote descriptors must survive dsh 0.2's typert codec validation, which
260
+ // runs inside `ctx.remote.$mount`: a strict codec needs a typeSymbol and a
261
+ // `create()` factory returning a schema with `parse`. The 0.1 shape (`schema`
262
+ // only) threw "strict codec has no create() factory", rejected apply(), and
263
+ // took the whole plugin down. The client may only use strict codecs for
264
+ // parameters. The descriptor block is lifted out of client.js and evaluated,
265
+ // so this tests the shipped objects, not a copy.
266
+ console.log('\nremote descriptors (dsh 0.2 codec contract)')
267
+ const descriptorBlock = /function identity\(value\) \{[\s\S]*?\r?\n const CONTRIBUTION = \{[\s\S]*?\r?\n \};/.exec(clientSource)?.[0]
268
+ check('descriptor block found in client.js', descriptorBlock !== undefined)
269
+ const CONTRIBUTION = descriptorBlock ? new Function(`${descriptorBlock}; return CONTRIBUTION;`)() : { descriptors: [] }
270
+ function codecProblem(codec, { parameter }) {
271
+ if (!codec || typeof codec !== 'object') return 'missing codec'
272
+ if (codec.mode === 'src-json') return parameter ? 'client parameters must be strict' : undefined
273
+ if (codec.mode !== 'strict') return `unknown mode ${codec.mode}`
274
+ if (typeof codec.typeSymbol !== 'string' || codec.typeSymbol === '') return 'empty typeSymbol'
275
+ if (typeof codec.create !== 'function') return 'strict codec has no create() factory'
276
+ const schema = codec.create()
277
+ if (typeof schema?.parse !== 'function') return 'create() returned no parse()'
278
+ if (codec.create() !== schema) return 'create() is not memoized'
279
+ // dsh 0.1 read `schema` directly; keep it so older hosts still load.
280
+ if (typeof codec.schema?.parse !== 'function') return 'no 0.1 schema fallback'
281
+ return undefined
282
+ }
283
+ check('declares the status and viz descriptors', CONTRIBUTION.descriptors.map((d) => d.method).join() === 'status,viz')
284
+ for (const d of CONTRIBUTION.descriptors) {
285
+ for (const p of d.parameters) {
286
+ const problem = codecProblem(p.codec, { parameter: true })
287
+ check(`${d.id} parameter ${p.name} codec is valid`, problem === undefined, problem)
288
+ }
289
+ const problem = codecProblem(d.result, { parameter: false })
290
+ check(`${d.id} result codec is valid`, problem === undefined, problem)
291
+ const sample = { ok: true, value: { root: '/r', nodeCount: 1 } }
292
+ check(`${d.id} result decodes as a pass-through`, d.result.create().parse(sample) === sample)
293
+ }
294
+
259
295
  await rm(fixture, { recursive: true, force: true })
260
296
 
261
297
  console.log(failures === 0 ? '\nALL CHECKS PASSED' : `\n${failures} CHECK(S) FAILED`)
package/src/client.js CHANGED
@@ -50,7 +50,21 @@ window.__ModuleLoader__.load({
50
50
  function identity(value) {
51
51
  return value;
52
52
  }
53
- const CODEC = { mode: "strict", typeSymbol: "graft-status/json", schema: { parse: identity } };
53
+ /*
54
+ * Pass-through codec carrying both codec shapes: dsh 0.2's typert
55
+ * validator requires a strict codec to expose a `create()` factory
56
+ * returning the schema (it throws "strict codec has no create() factory"
57
+ * otherwise, failing `$mount` and with it the whole plugin), while dsh 0.1
58
+ * read `schema` directly. The host still runs assertJsonValue on every
59
+ * decoded value, so identity is safe.
60
+ */
61
+ const IDENTITY_SCHEMA = { parse: identity };
62
+ const CODEC = {
63
+ mode: "strict",
64
+ typeSymbol: "graft-status/json",
65
+ schema: IDENTITY_SCHEMA,
66
+ create: () => IDENTITY_SCHEMA,
67
+ };
54
68
  const descriptor = (method, names) => ({
55
69
  id: "graft-status#graftStatus/" + method,
56
70
  service: "graftStatus",
@@ -58,7 +72,7 @@ window.__ModuleLoader__.load({
58
72
  method,
59
73
  invocation: { kind: "direct" },
60
74
  parameters: names.map((name) => ({ name, wire: name, source: "json", codec: CODEC })),
61
- result: { mode: "strict", typeSymbol: "graft-status/json", schema: { parse: identity } },
75
+ result: CODEC,
62
76
  sourceLocation: { file: "graft-status/client.js", line: 1, column: 1 },
63
77
  });
64
78
  const CONTRIBUTION = {