devframe 0.1.21 → 0.1.22

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.
Files changed (89) hide show
  1. package/README.md +41 -18
  2. package/dist/{_shared-iZy45oG3.mjs → _shared-DcJX9-Az.mjs} +2 -2
  3. package/dist/adapters/build.d.mts +5 -5
  4. package/dist/adapters/build.mjs +13 -12
  5. package/dist/adapters/cli.d.mts +2 -2
  6. package/dist/adapters/cli.mjs +5 -5
  7. package/dist/adapters/dev.d.mts +18 -9
  8. package/dist/adapters/dev.mjs +1 -1
  9. package/dist/adapters/embedded.d.mts +5 -5
  10. package/dist/adapters/embedded.mjs +2 -2
  11. package/dist/adapters/mcp.d.mts +3 -3
  12. package/dist/adapters/mcp.mjs +5 -5
  13. package/dist/adapters/vite.d.mts +45 -12
  14. package/dist/adapters/vite.mjs +81 -18
  15. package/dist/chunk-e9Ob2GDo.mjs +26 -0
  16. package/dist/client/index.d.mts +10 -5
  17. package/dist/client/index.mjs +19 -10
  18. package/dist/colors-Bl4W18Mh.mjs +123 -0
  19. package/dist/{context-internal-saIAfNVw.d.mts → context-DBd794Rn.d.mts} +2 -2
  20. package/dist/{context-BJ4r2SmR.mjs → context-DUibxhrR.mjs} +14 -31
  21. package/dist/{dev-CdAy400a.mjs → dev-BGpTpYiR.mjs} +19 -20
  22. package/dist/{devtool-CNvLs2_Y.d.mts → devframe-DulcIxwu.d.mts} +49 -58
  23. package/dist/{diagnostics-DxPnRoXO.mjs → diagnostics-BFhNNmST.mjs} +7 -2
  24. package/dist/hash-BHZbo80D.mjs +126 -0
  25. package/dist/{human-id-BLoGo_e5.mjs → human-id-adtUWQLr.mjs} +12 -3
  26. package/dist/{index-CuLRIMto.d.mts → index-DB2Ie0Or.d.mts} +2 -3
  27. package/dist/index.d.mts +4 -5
  28. package/dist/index.mjs +2 -5
  29. package/dist/launch-editor-KA_21J1T.mjs +581 -0
  30. package/dist/node/auth.d.mts +44 -0
  31. package/dist/node/auth.mjs +55 -0
  32. package/dist/node/index.d.mts +10 -51
  33. package/dist/node/index.mjs +6 -62
  34. package/dist/{internal/index.d.mts → node/internal.d.mts} +5 -5
  35. package/dist/node/internal.mjs +50 -0
  36. package/dist/{open-Dede_w9r.mjs → open-DmFp0qZA.mjs} +12 -3
  37. package/dist/recipes/open-helpers.d.mts +4 -9
  38. package/dist/recipes/open-helpers.mjs +7 -12
  39. package/dist/revoke-DIMz1r7-.mjs +35 -0
  40. package/dist/rpc/index.d.mts +3 -3
  41. package/dist/rpc/index.mjs +4 -4
  42. package/dist/rpc/transports/ws-client.d.mts +1 -1
  43. package/dist/rpc/transports/ws-client.mjs +2 -1
  44. package/dist/rpc/transports/ws-server.d.mts +1 -1
  45. package/dist/rpc/transports/ws-server.mjs +2 -1
  46. package/dist/{rpc-INbWfHoX.mjs → rpc-CkZuoz-m.mjs} +3 -2
  47. package/dist/{serialization-DwKi05Pn.mjs → serialization-CwO31axa.mjs} +1 -2
  48. package/dist/{server-CBsxXIH5.mjs → server-ma8-ZVyX.mjs} +1 -1
  49. package/dist/{server-BAqOajx_.d.mts → server-zKEDxgqD.d.mts} +4 -3
  50. package/dist/shared-state-DCDs0e7y.mjs +928 -0
  51. package/dist/{static-dump-D5VH8Iqk.mjs → static-dump-D7AvjrgL.mjs} +1 -1
  52. package/dist/{context-internal-Dx_NoSv1.mjs → storage-CNC38eU-.mjs} +4 -86
  53. package/dist/structured-clone-DcDc2Dds.mjs +221 -0
  54. package/dist/types/index.d.mts +4 -4
  55. package/dist/types/index.mjs +11 -2
  56. package/dist/utils/colors.d.mts +28 -0
  57. package/dist/utils/colors.mjs +2 -0
  58. package/dist/utils/events.d.mts +1 -1
  59. package/dist/utils/hash.d.mts +7 -0
  60. package/dist/utils/hash.mjs +2 -0
  61. package/dist/utils/human-id.d.mts +6 -8
  62. package/dist/utils/human-id.mjs +1 -2
  63. package/dist/utils/launch-editor.d.mts +13 -0
  64. package/dist/utils/launch-editor.mjs +2 -0
  65. package/dist/utils/open.d.mts +16 -0
  66. package/dist/utils/open.mjs +2 -0
  67. package/dist/utils/serve-static.d.mts +30 -0
  68. package/dist/utils/serve-static.mjs +152 -0
  69. package/dist/utils/shared-state.d.mts +1 -1
  70. package/dist/utils/shared-state.mjs +1 -35
  71. package/dist/utils/streaming-channel.d.mts +1 -1
  72. package/dist/utils/structured-clone.d.mts +21 -0
  73. package/dist/utils/structured-clone.mjs +2 -0
  74. package/dist/utils/when.d.mts +32 -49
  75. package/dist/utils/when.mjs +20 -2
  76. package/dist/{ws-client-CjPuPFbA.d.mts → ws-client-Bavoya6-.d.mts} +1 -1
  77. package/dist/{ws-server-C7LnhOHi.d.mts → ws-server-DjvlwLuM.d.mts} +1 -1
  78. package/package.json +23 -10
  79. package/skills/devframe/SKILL.md +45 -23
  80. package/skills/devframe/templates/{counter-devtool.ts → counter-devframe.ts} +4 -4
  81. package/skills/devframe/templates/{spa-devtool.ts → spa-devframe.ts} +3 -3
  82. package/skills/devframe/templates/vite-client.ts +2 -2
  83. package/dist/internal/index.mjs +0 -3
  84. package/dist/utils/state.d.mts +0 -50
  85. package/dist/utils/state.mjs +0 -26
  86. package/dist/{define-Bb4zh-Dc.mjs → define-CW9gLnyG.mjs} +0 -0
  87. package/dist/{host-h3-9jeHcltx.mjs → host-h3-Dz8YnFgu.mjs} +1 -1
  88. /package/dist/{transports-DSV5_5QM.mjs → transports-BnCs4rj9.mjs} +0 -0
  89. /package/dist/{types-C5OVe4AC.d.mts → types-4rdUEi2R.d.mts} +0 -0
@@ -1,36 +1,2 @@
1
- import { createEventEmitter } from "./events.mjs";
2
- import { nanoid } from "./nanoid.mjs";
3
- import { applyPatches, enablePatches, produce, produceWithPatches } from "immer";
4
- //#region src/utils/shared-state.ts
5
- function createSharedState(options) {
6
- const { enablePatches: enablePatches$1 = false } = options;
7
- const events = createEventEmitter();
8
- let state = options.initialValue;
9
- const syncIds = /* @__PURE__ */ new Set();
10
- return {
11
- on: events.on,
12
- value: () => state,
13
- patch: (patches, syncId = nanoid()) => {
14
- if (syncIds.has(syncId)) return;
15
- enablePatches();
16
- state = applyPatches(state, patches);
17
- syncIds.add(syncId);
18
- events.emit("updated", state, void 0, syncId);
19
- },
20
- mutate: (fn, syncId = nanoid()) => {
21
- if (syncIds.has(syncId)) return;
22
- syncIds.add(syncId);
23
- if (enablePatches$1) {
24
- const [newState, patches] = produceWithPatches(state, fn);
25
- state = newState;
26
- events.emit("updated", state, patches, syncId);
27
- } else {
28
- state = produce(state, fn);
29
- events.emit("updated", state, void 0, syncId);
30
- }
31
- },
32
- syncIds
33
- };
34
- }
35
- //#endregion
1
+ import { t as createSharedState } from "../shared-state-DCDs0e7y.mjs";
36
2
  export { createSharedState };
@@ -1,2 +1,2 @@
1
- import { C as StreamErrorPayload, D as createStreamReader, E as StreamSinkEvents, O as createStreamSink, S as CreateStreamSinkOptions, T as StreamSink, b as BufferedChunk, w as StreamReader, x as CreateStreamReaderOptions } from "../devtool-CNvLs2_Y.mjs";
1
+ import { A as CreateStreamSinkOptions, F as createStreamReader, I as createStreamSink, M as StreamReader, N as StreamSink, O as BufferedChunk, P as StreamSinkEvents, j as StreamErrorPayload, k as CreateStreamReaderOptions } from "../devframe-DulcIxwu.mjs";
2
2
  export { BufferedChunk, CreateStreamReaderOptions, CreateStreamSinkOptions, StreamErrorPayload, StreamReader, StreamSink, StreamSinkEvents, createStreamReader, createStreamSink };
@@ -0,0 +1,21 @@
1
+ //#region src/utils/structured-clone.d.ts
2
+ /**
3
+ * Serialize a structured-cloneable value (`Map`, `Set`, `Date`, `BigInt`,
4
+ * cycles, class instances, …) into a JSON-safe records array.
5
+ */
6
+ declare function structuredCloneSerialize(value: unknown): unknown[];
7
+ /**
8
+ * Inverse of {@link structuredCloneSerialize}.
9
+ */
10
+ declare function structuredCloneDeserialize<T = unknown>(value: unknown[]): T;
11
+ /**
12
+ * Serialize a structured-cloneable value to a single string. Equivalent
13
+ * to `JSON.stringify(structuredCloneSerialize(value))`.
14
+ */
15
+ declare function structuredCloneStringify(value: unknown): string;
16
+ /**
17
+ * Inverse of {@link structuredCloneStringify}.
18
+ */
19
+ declare function structuredCloneParse<T = unknown>(value: string): T;
20
+ //#endregion
21
+ export { structuredCloneDeserialize, structuredCloneParse, structuredCloneSerialize, structuredCloneStringify };
@@ -0,0 +1,2 @@
1
+ import { i as structuredCloneStringify, n as structuredCloneParse, r as structuredCloneSerialize, t as structuredCloneDeserialize } from "../structured-clone-DcDc2Dds.mjs";
2
+ export { structuredCloneDeserialize, structuredCloneParse, structuredCloneSerialize, structuredCloneStringify };
@@ -325,50 +325,8 @@ type ValidateExpression<S extends string, T> = string extends S ? S : Tokenize<S
325
325
  * defineCommand({ name: 'x', title: 'X', when: 'typo' })
326
326
  * // ^^^^^ type error
327
327
  */
328
- type WhenExpression<T, S extends string> = S & ValidateExpression<S, T>; //#endregion
328
+ type WhenExpression$1<T, S extends string> = S & ValidateExpression<S, T>; //#endregion
329
329
  //#region src/index.d.ts
330
- interface EvaluateOptions {
331
- /**
332
- * If `true`, throw when the expression references a context key that does
333
- * not exist. Defaults to `false`, where unknown keys evaluate to `undefined`.
334
- */
335
- strict?: boolean;
336
- }
337
- /**
338
- * Evaluate a when-clause expression string against a context object.
339
- * Equivalent to `evaluate(parse(expression), ctx, options)`.
340
- *
341
- * Supports a JS-expression subset:
342
- * - Literals: booleans, numbers, strings (quoted with `"` or `'`)
343
- * - Identifiers: bare keys, including namespaced (`vite.mode`, `vite:buildMode`)
344
- * - Logical: `&&`, `||`, `!`
345
- * - Equality: `==`, `!=` (string comparison, bare identifier RHS treated as string),
346
- * `===`, `!==` (JS strict equality with full expression RHS)
347
- * - Relational: `<`, `<=`, `>`, `>=`
348
- * - Arithmetic: `+`, `-`, `*`, `/`, `%`, unary `-` / `+`
349
- * - Grouping: `(` … `)`
350
- *
351
- * When `ctx` has a specific type with known keys, the expression string is
352
- * **statically validated** — unknown context keys and syntax errors surface
353
- * as TypeScript errors at the call site. Pass `ctx` typed as
354
- * `Record<string, unknown>`, or the expression as `string`, to opt out of
355
- * static checking (e.g. for dynamic expressions).
356
- *
357
- * With `{ strict: true }`, referencing an unknown context key at runtime
358
- * throws. Short-circuit evaluation still applies — keys not reached are not
359
- * checked.
360
- */
361
- declare function evaluateWhen<T extends object, const E extends string>(expression: E & ValidateExpression<E, T>, ctx: T, options?: EvaluateOptions): boolean;
362
- /**
363
- * Resolve a context value by key. Supports namespaced keys with `.` or `:` separators.
364
- *
365
- * Lookup order:
366
- * 1. Exact match — `ctx['plugin.mode']` or `ctx['plugin:mode']`
367
- * 2. Nested path — e.g. `plugin.mode` falls back to `ctx.plugin?.mode`
368
- *
369
- * Returns `undefined` for unknown keys.
370
- */
371
- declare function resolveContextValue<T extends Record<string, unknown>>(key: string, ctx: T): unknown; //#endregion
372
330
  //#endregion
373
331
  //#region src/utils/when.d.ts
374
332
  /**
@@ -383,11 +341,9 @@ declare function resolveContextValue<T extends Record<string, unknown>>(key: str
383
341
  * Plugins can add namespaced variables using dot or colon separators:
384
342
  * - `vite.mode`, `vite:mode` — stored as `{ 'vite.mode': 'development' }` or nested `{ vite: { mode: 'development' } }`
385
343
  *
386
- * The expression language including all operators and precedence is provided by
387
- * the [`whenexpr`](https://github.com/antfu/whenexpr) package. In addition to the
388
- * VS-Code-style `!`/`==`/`!=`/`&&`/`||` operators, parentheses, strict equality
389
- * (`===`/`!==`), relational (`<`/`<=`/`>`/`>=`), arithmetic (`+ - * / %`), and
390
- * number/string literals are supported.
344
+ * Supported operators: `!`, `==`, `!=`, `===`, `!==`, `<`, `<=`, `>`, `>=`,
345
+ * `&&`, `||`, `+`, `-`, `*`, `/`, `%`. Parentheses, number and string literals
346
+ * are also supported.
391
347
  */
392
348
  interface WhenContext {
393
349
  clientType: 'embedded' | 'standalone';
@@ -397,5 +353,32 @@ interface WhenContext {
397
353
  /** Allow custom context variables from plugins */
398
354
  [key: string]: unknown;
399
355
  }
356
+ /**
357
+ * A statically-validated `when` expression string. Used inside `define*`
358
+ * helpers to surface unknown context keys and syntax errors as TypeScript
359
+ * errors at the call site.
360
+ */
361
+ type WhenExpression<T, S extends string> = WhenExpression$1<T, S>;
362
+ /** Options for `evaluateWhen`. */
363
+ interface EvaluateWhenOptions {
364
+ /**
365
+ * Throw when the expression references a context key that does not exist.
366
+ *
367
+ * @default false
368
+ */
369
+ strict?: boolean;
370
+ }
371
+ /**
372
+ * Evaluate a when-clause expression string against a context object.
373
+ *
374
+ * @example
375
+ * evaluateWhen('dockOpen && clientType == embedded', ctx)
376
+ */
377
+ declare function evaluateWhen<T extends object, const E extends string>(expression: E & WhenExpression$1<T, E>, context: T, options?: EvaluateWhenOptions): boolean;
378
+ /**
379
+ * Resolve a context value by key. Supports namespaced keys with `.` or `:`
380
+ * separators. Returns `undefined` for unknown keys.
381
+ */
382
+ declare function resolveContextValue<T extends Record<string, unknown>>(key: string, context: T): unknown;
400
383
  //#endregion
401
- export { WhenContext, type WhenExpression, evaluateWhen, resolveContextValue };
384
+ export { EvaluateWhenOptions, WhenContext, type WhenExpression, evaluateWhen, resolveContextValue };
@@ -369,7 +369,7 @@ function runBinary(node, ctx, strict) {
369
369
  * throws. Short-circuit evaluation still applies — keys not reached are not
370
370
  * checked.
371
371
  */
372
- function evaluateWhen(expression, ctx, options) {
372
+ function evaluateWhen$1(expression, ctx, options) {
373
373
  return evaluate(parse(expression), ctx, options);
374
374
  }
375
375
  /**
@@ -381,7 +381,7 @@ function evaluateWhen(expression, ctx, options) {
381
381
  *
382
382
  * Returns `undefined` for unknown keys.
383
383
  */
384
- function resolveContextValue(key, ctx) {
384
+ function resolveContextValue$1(key, ctx) {
385
385
  return resolve(key, ctx).value;
386
386
  }
387
387
  function lookup(key, ctx, strict) {
@@ -421,4 +421,22 @@ function resolve(key, ctx) {
421
421
  };
422
422
  }
423
423
  //#endregion
424
+ //#region src/utils/when.ts
425
+ /**
426
+ * Evaluate a when-clause expression string against a context object.
427
+ *
428
+ * @example
429
+ * evaluateWhen('dockOpen && clientType == embedded', ctx)
430
+ */
431
+ function evaluateWhen(expression, context, options) {
432
+ return evaluateWhen$1(expression, context, options);
433
+ }
434
+ /**
435
+ * Resolve a context value by key. Supports namespaced keys with `.` or `:`
436
+ * separators. Returns `undefined` for unknown keys.
437
+ */
438
+ function resolveContextValue(key, context) {
439
+ return resolveContextValue$1(key, context);
440
+ }
441
+ //#endregion
424
442
  export { evaluateWhen, resolveContextValue };
@@ -1,4 +1,4 @@
1
- import { g as RpcFunctionDefinitionAny } from "./types-C5OVe4AC.mjs";
1
+ import { g as RpcFunctionDefinitionAny } from "./types-4rdUEi2R.mjs";
2
2
  import { ChannelOptions } from "birpc";
3
3
 
4
4
  //#region src/rpc/transports/ws-client.d.ts
@@ -1,4 +1,4 @@
1
- import { g as RpcFunctionDefinitionAny } from "./types-C5OVe4AC.mjs";
1
+ import { g as RpcFunctionDefinitionAny } from "./types-4rdUEi2R.mjs";
2
2
  import { BirpcGroup, ChannelOptions } from "birpc";
3
3
  import { ServerOptions } from "node:https";
4
4
  import { WebSocket, WebSocketServer } from "ws";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "devframe",
3
3
  "type": "module",
4
- "version": "0.1.21",
4
+ "version": "0.1.22",
5
5
  "description": "Framework for building generic DevTools",
6
6
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -28,8 +28,9 @@
28
28
  "./adapters/vite": "./dist/adapters/vite.mjs",
29
29
  "./client": "./dist/client/index.mjs",
30
30
  "./constants": "./dist/constants.mjs",
31
- "./internal": "./dist/internal/index.mjs",
32
31
  "./node": "./dist/node/index.mjs",
32
+ "./node/auth": "./dist/node/auth.mjs",
33
+ "./node/internal": "./dist/node/internal.mjs",
33
34
  "./recipes/open-helpers": "./dist/recipes/open-helpers.mjs",
34
35
  "./rpc": "./dist/rpc/index.mjs",
35
36
  "./rpc/client": "./dist/rpc/client.mjs",
@@ -37,13 +38,18 @@
37
38
  "./rpc/transports/ws-client": "./dist/rpc/transports/ws-client.mjs",
38
39
  "./rpc/transports/ws-server": "./dist/rpc/transports/ws-server.mjs",
39
40
  "./types": "./dist/types/index.mjs",
41
+ "./utils/colors": "./dist/utils/colors.mjs",
40
42
  "./utils/events": "./dist/utils/events.mjs",
43
+ "./utils/hash": "./dist/utils/hash.mjs",
41
44
  "./utils/human-id": "./dist/utils/human-id.mjs",
45
+ "./utils/launch-editor": "./dist/utils/launch-editor.mjs",
42
46
  "./utils/nanoid": "./dist/utils/nanoid.mjs",
47
+ "./utils/open": "./dist/utils/open.mjs",
43
48
  "./utils/promise": "./dist/utils/promise.mjs",
49
+ "./utils/serve-static": "./dist/utils/serve-static.mjs",
44
50
  "./utils/shared-state": "./dist/utils/shared-state.mjs",
45
- "./utils/state": "./dist/utils/state.mjs",
46
51
  "./utils/streaming-channel": "./dist/utils/streaming-channel.mjs",
52
+ "./utils/structured-clone": "./dist/utils/structured-clone.mjs",
47
53
  "./utils/when": "./dist/utils/when.mjs",
48
54
  "./package.json": "./package.json"
49
55
  },
@@ -62,43 +68,50 @@
62
68
  },
63
69
  "dependencies": {
64
70
  "@valibot/to-json-schema": "^1.7.0",
65
- "ansis": "^4.2.0",
66
71
  "birpc": "^4.0.0",
67
72
  "cac": "^7.0.0",
68
73
  "h3": "^1.15.11",
69
- "immer": "^11.1.7",
70
- "launch-editor": "^2.13.2",
71
74
  "logs-sdk": "^0.0.6",
72
- "ohash": "^2.0.11",
75
+ "mrmime": "^2.0.1",
73
76
  "pathe": "^2.0.3",
74
- "sirv": "^3.0.2",
75
- "structured-clone-es": "^2.0.0",
76
77
  "valibot": "^1.4.0",
77
78
  "ws": "^8.20.0"
78
79
  },
79
80
  "devDependencies": {
80
81
  "@modelcontextprotocol/sdk": "^1.29.0",
82
+ "ansis": "^4.2.0",
83
+ "immer": "^11.1.8",
81
84
  "launch-editor": "^2.13.2",
82
- "tsdown": "^0.21.10",
85
+ "ohash": "^2.0.11",
86
+ "open": "^11.0.0",
87
+ "structured-clone-es": "^2.0.0",
88
+ "tsdown": "^0.22.0",
83
89
  "whenexpr": "^0.1.2"
84
90
  },
85
91
  "inlinedDependencies": {
92
+ "ansis": "4.2.0",
86
93
  "bundle-name": "4.1.0",
87
94
  "default-browser": "5.4.0",
88
95
  "default-browser-id": "5.0.1",
89
96
  "define-lazy-prop": "3.0.0",
90
97
  "get-port-please": "3.2.0",
91
98
  "human-id": "4.1.3",
99
+ "immer": "11.1.8",
92
100
  "is-docker": "3.0.0",
93
101
  "is-in-ssh": "1.0.0",
94
102
  "is-inside-container": "1.0.0",
95
103
  "is-wsl": "3.1.0",
104
+ "launch-editor": "2.13.2",
96
105
  "obug": "2.1.1",
106
+ "ohash": "2.0.11",
97
107
  "open": "11.0.0",
98
108
  "p-limit": "7.3.0",
99
109
  "perfect-debounce": "2.1.0",
110
+ "picocolors": "1.1.1",
100
111
  "powershell-utils": "0.1.0",
101
112
  "run-applescript": "7.1.0",
113
+ "shell-quote": "1.8.3",
114
+ "structured-clone-es": "2.0.0",
102
115
  "ua-parser-modern": "0.1.1",
103
116
  "whenexpr": "0.1.2",
104
117
  "wsl-utils": "0.3.1",
@@ -3,21 +3,21 @@ name: devframe
3
3
  description: >
4
4
  Use when building one devtool integration with devframe — the
5
5
  portable, framework-neutral container for a single tool. Covers
6
- DevtoolDefinition, picking the right deployment adapter
6
+ DevframeDefinition, picking the right deployment adapter
7
7
  (cli / build / spa / vite / embedded / mcp), designing RPC
8
8
  contracts, exposing an agent-native surface over MCP, and wiring
9
9
  the author's SPA client. Hub-only concerns (docks, terminals,
10
10
  commands, the unified messages dock) belong to
11
11
  `@vitejs/devtools-kit` — see the `vite-devtools-kit` skill for
12
- those. Triggers on `devframe` imports, `defineDevtool`,
13
- `createCli`, `createMcpServer`, `connectDevtool`, and on
12
+ those. Triggers on `devframe` imports, `defineDevframe`,
13
+ `createCli`, `createMcpServer`, `connectDevframe`, and on
14
14
  migrations of existing inspectors (eslint-config-inspector,
15
15
  unocss-inspector, node-modules-inspector-style tools) to devframe.
16
16
  ---
17
17
 
18
18
  # devframe skill
19
19
 
20
- **Devframe is the container for one devtool integration, portable across viewers.** A devtool built on devframe is a single `DevtoolDefinition` plus an author-provided SPA — the same definition deploys as a standalone CLI, a static report, an embedded SPA, an MCP server, or as a dock entry inside the Vite DevTools hub via `createPluginFromDevframe`.
20
+ **Devframe is the container for one devtool integration, portable across viewers.** A devtool built on devframe is a single `DevframeDefinition` plus an author-provided SPA — the same definition deploys as a standalone CLI, a static report, an embedded SPA, an MCP server, or as a dock entry inside the Vite DevTools hub via `createPluginFromDevframe`.
21
21
 
22
22
  Devframe deliberately stops at the boundary of one tool. Anything that only matters across multiple integrations — docks, terminals, command palette, cross-tool toasts — lives in `@vitejs/devtools-kit`, the hub layer. `devframe` must not depend on Vite, any `@vitejs/*` package, or hub-only concepts; it's the lowest-level layer in the monorepo.
23
23
 
@@ -25,7 +25,7 @@ Full reference: [devfra.me/](https://devfra.me/).
25
25
 
26
26
  ## When to use devframe
27
27
 
28
- All adapter factories share the shape `createXxx(devtoolDef, options?)`.
28
+ All adapter factories share the shape `createXxx(devframeDef, options?)`.
29
29
 
30
30
  | Author goal | Factory | Entry |
31
31
  |-------------|---------|-------|
@@ -37,14 +37,14 @@ All adapter factories share the shape `createXxx(devtoolDef, options?)`.
37
37
  | Register dynamically at runtime | `createEmbedded(def, { ctx })` | `devframe/adapters/embedded` |
38
38
  | Expose to coding agents (MCP) | `createMcpServer(def, options?)` | `devframe/adapters/mcp` *(experimental)* |
39
39
 
40
- The same `DevtoolDefinition` runs under every adapter — pick based on deployment, not on what the tool does.
40
+ The same `DevframeDefinition` runs under every adapter — pick based on deployment, not on what the tool does.
41
41
 
42
- ## Minimum viable devtool
42
+ ## Minimum viable devframe
43
43
 
44
44
  ```ts
45
- import { defineDevtool, defineRpcFunction } from 'devframe'
45
+ import { defineDevframe, defineRpcFunction } from 'devframe'
46
46
 
47
- export default defineDevtool({
47
+ export default defineDevframe({
48
48
  id: 'my-inspector',
49
49
  name: 'My Inspector',
50
50
  icon: 'ph:magnifying-glass-duotone',
@@ -61,16 +61,16 @@ export default defineDevtool({
61
61
 
62
62
  `setup(ctx)` registers RPC functions, shared state, diagnostics, and any other devframe-level wiring. It does **not** receive `docks` / `terminals` / `messages` / `commands` — those are hub features. When mounted into Vite DevTools via `createPluginFromDevframe(d)`, the kit auto-derives an iframe dock entry from `id` / `name` / `icon` / `basePath`; for richer hub-side behaviour (custom-render, terminals, palette commands) pass `options.setup` to `createPluginFromDevframe`.
63
63
 
64
- See `templates/counter-devtool.ts` for a runnable counter example, `templates/spa-devtool.ts` for an SPA-ready shape, and `templates/vite-client.ts` for the author's client entry.
64
+ See `templates/counter-devframe.ts` for a runnable counter example, `templates/spa-devframe.ts` for an SPA-ready shape, and `templates/vite-client.ts` for the author's client entry.
65
65
 
66
66
  ## Namespacing
67
67
 
68
- **Always prefix** RPC names, dock IDs, command IDs, shared-state keys, and agent tool IDs with the devtool `id`:
68
+ **Always prefix** RPC names, dock IDs, command IDs, shared-state keys, and agent tool IDs with the devframe `id`:
69
69
 
70
70
  ```ts
71
71
  'my-inspector:get-modules' // ✓
72
72
  'my-inspector:state' // ✓
73
- 'get-modules' // ✗ — may collide with other devtools sharing the host
73
+ 'get-modules' // ✗ — may collide with other devframes sharing the host
74
74
  ```
75
75
 
76
76
  ## DevToolsNodeContext at a glance
@@ -86,7 +86,7 @@ See `templates/counter-devtool.ts` for a runnable counter example, `templates/sp
86
86
  | `ctx.host` | Runtime abstraction — `mountStatic`, `resolveOrigin`, `getStorageDir` |
87
87
  | `ctx.mode` | `'dev'` or `'build'` — gate setup work per runtime |
88
88
 
89
- > Hub-only hosts (`ctx.docks`, `ctx.terminals`, `ctx.messages`, `ctx.commands`) only exist when the devtool is mounted into Vite DevTools via `createPluginFromDevframe`. See the [`vite-devtools-kit` skill](../../skills/vite-devtools-kit) for those.
89
+ > Hub-only hosts (`ctx.docks`, `ctx.terminals`, `ctx.messages`, `ctx.commands`, `ctx.createJsonRenderer`) only exist when the devframe is mounted into Vite DevTools via `createPluginFromDevframe`. See the [`vite-devtools-kit` skill](../../skills/vite-devtools-kit) for those.
90
90
 
91
91
  ## RPC contracts
92
92
 
@@ -267,7 +267,7 @@ The kit auto-derives an iframe dock entry from `id` / `name` / `icon` / `basePat
267
267
 
268
268
  ## When clauses
269
269
 
270
- Gate kit-side dock / command visibility with VS Code-style expressions evaluated by the external `whenexpr` package. The runtime + types ship from `devframe/utils/when`, but the consumers (`when` field on docks and commands) live in the kit:
270
+ Gate kit-side dock / command visibility with VS Code-style expressions. The runtime + types ship bundled from `devframe/utils/when` no separate install. The consumers (`when` field on docks and commands) live in the kit:
271
271
 
272
272
  ```ts
273
273
  when: 'clientType == embedded'
@@ -275,7 +275,7 @@ when: 'dockOpen && !paletteOpen'
275
275
  when: 'my-inspector.ready && count >= 10'
276
276
  ```
277
277
 
278
- Built-in context: `clientType` (`'embedded' | 'standalone'`), `dockOpen`, `paletteOpen`, `dockSelectedId`. Plugins can add namespaced keys (`.` or `:` separators). Both the types (`WhenExpression<Ctx, S>`) and runtime (`evaluateWhen`, `resolveContextValue`) are re-exported from `devframe/utils/when`.
278
+ Built-in context: `clientType` (`'embedded' | 'standalone'`), `dockOpen`, `paletteOpen`, `dockSelectedId`. Plugins can add namespaced keys (`.` or `:` separators). Both the types (`WhenExpression<Ctx, S>`) and runtime (`evaluateWhen`, `resolveContextValue`) come from `devframe/utils/when`.
279
279
 
280
280
  ## Agent-native surface (experimental)
281
281
 
@@ -319,25 +319,25 @@ Expose via MCP:
319
319
  ```ts
320
320
  import { createMcpServer } from 'devframe/adapters/mcp'
321
321
 
322
- await createMcpServer(devtool, { transport: 'stdio' })
322
+ await createMcpServer(devframe, { transport: 'stdio' })
323
323
  ```
324
324
 
325
- `@modelcontextprotocol/sdk` is a peer dependency. The CLI adapter also exposes `my-devtool mcp` — route host logs to stderr (stdout is the MCP transport). Safety classifications (`'read' | 'action' | 'destructive'`) drive MCP hint annotations that agent clients use to prompt for confirmation.
325
+ `@modelcontextprotocol/sdk` is a peer dependency. The CLI adapter also exposes `my-devframe mcp` — route host logs to stderr (stdout is the MCP transport). Safety classifications (`'read' | 'action' | 'destructive'`) drive MCP hint annotations that agent clients use to prompt for confirmation.
326
326
 
327
327
  ## Author SPA
328
328
 
329
329
  Authors bring their own SPA (any framework or plain HTML). Client entry:
330
330
 
331
331
  ```ts
332
- import { connectDevtool } from 'devframe/client'
332
+ import { connectDevframe } from 'devframe/client'
333
333
 
334
- const rpc = await connectDevtool()
334
+ const rpc = await connectDevframe()
335
335
  // await rpc.ensureTrusted() // WS mode only — blocks until server accepts
336
336
 
337
337
  const data = await rpc.call('my-inspector:get-stats', { limit: 10 })
338
338
  ```
339
339
 
340
- `connectDevtool` auto-detects the backend via `/.devtools/.connection.json`:
340
+ `connectDevframe` auto-detects the backend via `/.devtools/.connection.json`:
341
341
 
342
342
  - **websocket** (dev mode) — full read/write, requires auth handshake. Listen for token updates on the `vite-devtools-auth` BroadcastChannel.
343
343
  - **static** (build / spa output) — read-only, resolves calls from the baked RPC dump.
@@ -366,7 +366,7 @@ At runtime, static clients look up the argument hash in the dump; misses resolve
366
366
 
367
367
  ## CLI adapter subcommands
368
368
 
369
- `createCli(devtool).parse()` gives the tool four subcommands out of the box:
369
+ `createCli(devframe).parse()` gives the tool four subcommands out of the box:
370
370
 
371
371
  | Subcommand | Action |
372
372
  |------------|--------|
@@ -377,23 +377,45 @@ At runtime, static clients look up the argument hash in the dump; misses resolve
377
377
 
378
378
  **Bring your own CLI framework?** `createCli` is just a cac wrapper around three peer factories — `createDevServer` (`devframe/adapters/dev`), `createBuild` (`devframe/adapters/build`), and `createMcpServer` (`devframe/adapters/mcp`). Use them directly with commander/yargs/oclif when `createCli`'s baked-in command structure doesn't fit. `createDevServer` returns a `StartedServer` handle (`origin`, `port`, `app`, `wss`, `close()`) so you can wire SIGINT / hot-reload teardown into the surrounding program. `parseCliFlags(schema, raw)` and `defineCliFlags(...)` (both from `devframe/adapters/cli`) validate an arbitrary flag bag against a `CliFlagsSchema` — typed flags aren't tied to cac.
379
379
 
380
+ ## Bundled utilities
381
+
382
+ Devframe re-exports a curated set of helpers under `devframe/utils/*`. They are bundled — never add the underlying packages to a devtool's own `package.json`:
383
+
384
+ | Import | Wraps | Use for |
385
+ |--------|-------|---------|
386
+ | `colors` from `devframe/utils/colors` | `ansis` | Terminal ANSI colors (`c.red`, `c.green`, tagged templates) |
387
+ | `open` from `devframe/utils/open` | `open` | Open URLs / files in the OS default handler |
388
+ | `launchEditor` from `devframe/utils/launch-editor` | `launch-editor` | Open `file:line:column` in the user's editor (optional `editor` arg) |
389
+ | `hash` from `devframe/utils/hash` | `ohash` | Stable structural hash — cache keys, dedup |
390
+ | `structuredClone{Serialize,Deserialize,Stringify,Parse}` from `devframe/utils/structured-clone` | `structured-clone-es` | JSON-safe round-trip of `Map` / `Set` / `Date` / `BigInt` / cycles |
391
+ | `humanId` from `devframe/utils/human-id` | `human-id` | Human-readable IDs (`bright-orange-tiger`) |
392
+ | `nanoid` from `devframe/utils/nanoid` | (vendored) | URL-safe random IDs |
393
+ | `promiseWithResolver` from `devframe/utils/promise` | — | Externally-controlled `Promise` |
394
+ | `createEventEmitter` from `devframe/utils/events` | — | Typed event bus |
395
+ | `createSharedState` from `devframe/utils/shared-state` | (immer internal) | Immutable state container (see `ctx.rpc.sharedState`) |
396
+ | `createStreamSink` / `createStreamReader` from `devframe/utils/streaming-channel` | — | Low-level streaming primitives |
397
+ | `evaluateWhen` / `WhenExpression` from `devframe/utils/when` | `whenexpr` | When-clause expressions |
398
+
399
+ For "open file in editor" + "reveal in finder", prefer the prebuilt `openHelpers` RPC recipe — it wires the two utilities into named RPC functions ready to register.
400
+
380
401
  ## Testing
381
402
 
382
403
  - Unit-test host classes with fake contexts.
383
- - Run `templates/counter-devtool.ts` under each adapter for integration coverage.
404
+ - Run `templates/counter-devframe.ts` under each adapter for integration coverage.
384
405
  - Snapshot the build-static RPC dump (`<outDir>/.devtools/.rpc-dump/index.json`) to catch accidental drift in `static` function outputs.
385
406
 
386
407
  ## Further reading
387
408
 
388
409
  Devframe-level pages (one-tool, portable surface):
389
410
 
390
- - [Devtool Definition](https://devfra.me/devtool-definition) — fields, runtime flags, multi-adapter wiring
411
+ - [Devframe Definition](https://devfra.me/devframe-definition) — fields, runtime flags, multi-adapter wiring
391
412
  - [Adapters](https://devfra.me/adapters) — full reference for all deployment adapters
392
413
  - [RPC](https://devfra.me/rpc) — types, schema, broadcasts, dumps
393
414
  - [Shared State](https://devfra.me/shared-state) — patches, events, client-side mutation
394
415
  - [Streaming](https://devfra.me/streaming) — chunked feeds, uploads, replay, Web/Node Streams interop
395
416
  - [When Clauses](https://devfra.me/when-clauses) — syntax, context, type-safe wrappers
396
417
  - [Structured Diagnostics](https://devfra.me/diagnostics) — coded errors via `ctx.diagnostics`, register custom codes
418
+ - [Utilities](https://devfra.me/utilities) — bundled `devframe/utils/*` helpers (colors, hash, launchEditor, structured-clone, …)
397
419
  - [Client](https://devfra.me/client) — auth handshake, modes, discovery
398
420
  - [Agent-Native](https://devfra.me/agent-native) — agent field, tools/resources, MCP + Claude Desktop
399
421
 
@@ -1,11 +1,11 @@
1
- // Smallest possible devtool. The dock entry is auto-derived from
1
+ // Smallest possible devframe. The dock entry is auto-derived from
2
2
  // `id` / `name` / `icon` when this definition is mounted into Vite
3
- // DevTools via `createPluginFromDevframe(devtool)`.
4
- import { defineDevtool, defineRpcFunction } from 'devframe'
3
+ // DevTools via `createPluginFromDevframe(devframe)`.
4
+ import { defineDevframe, defineRpcFunction } from 'devframe'
5
5
 
6
6
  let counter = 0
7
7
 
8
- export default defineDevtool({
8
+ export default defineDevframe({
9
9
  id: 'counter',
10
10
  name: 'Counter',
11
11
  icon: 'ph:counter-duotone',
@@ -1,10 +1,10 @@
1
- // Devtool with setupBrowser + SPA query-loader — deployable as a static site.
1
+ // Devframe with setupBrowser + SPA query-loader — deployable as a static site.
2
2
  // When mounted into Vite DevTools via `createPluginFromDevframe`, the kit
3
3
  // auto-derives an iframe dock from `id` / `name` / `icon`.
4
- import { defineDevtool, defineRpcFunction } from 'devframe'
4
+ import { defineDevframe, defineRpcFunction } from 'devframe'
5
5
  import * as v from 'valibot'
6
6
 
7
- export default defineDevtool({
7
+ export default defineDevframe({
8
8
  id: 'my-inspector',
9
9
  name: 'My Inspector',
10
10
  icon: 'ph:magnifying-glass-duotone',
@@ -1,8 +1,8 @@
1
1
  // Minimum SPA client that talks to a devframe-hosted RPC.
2
- import { connectDevtool } from 'devframe/client'
2
+ import { connectDevframe } from 'devframe/client'
3
3
 
4
4
  async function main() {
5
- const rpc = await connectDevtool()
5
+ const rpc = await connectDevframe()
6
6
  // The method names below are just examples — replace with your own.
7
7
  const data = await rpc.call('my-inspector:getStats' as any)
8
8
  document.getElementById('root')!.textContent = JSON.stringify(data)
@@ -1,3 +0,0 @@
1
- import { n as internalContextMap, t as getInternalContext } from "../context-internal-Dx_NoSv1.mjs";
2
- import { n as resolveBasePath, t as normalizeBasePath } from "../_shared-iZy45oG3.mjs";
3
- export { getInternalContext, internalContextMap, normalizeBasePath, resolveBasePath };
@@ -1,50 +0,0 @@
1
- import { ct as EventEmitter } from "../devtool-CNvLs2_Y.mjs";
2
- import { Objectish, Patch } from "immer";
3
-
4
- //#region src/utils/state.d.ts
5
- type ImmutablePrimitive = undefined | null | boolean | string | number | Function;
6
- type Immutable<T> = T extends ImmutablePrimitive ? T : T extends Array<infer U> ? ImmutableArray<U> : T extends Map<infer K, infer V> ? ImmutableMap<K, V> : T extends Set<infer M> ? ImmutableSet<M> : ImmutableObject<T>;
7
- type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
8
- type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;
9
- type ImmutableSet<T> = ReadonlySet<Immutable<T>>;
10
- type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]> };
11
- /**
12
- * State host that is immutable by default with explicit mutate.
13
- */
14
- interface SharedState<T> {
15
- /**
16
- * Get the current state. Immutable.
17
- */
18
- get: () => Immutable<T>;
19
- /**
20
- * Subscribe to state changes.
21
- */
22
- on: EventEmitter<SharedStateEvents<T>>['on'];
23
- /**
24
- * Mutate the state.
25
- */
26
- mutate: (fn: (state: T) => void) => void;
27
- /**
28
- * Apply patches to the state.
29
- */
30
- patch: (patches: Patch[]) => void;
31
- }
32
- interface SharedStateEvents<T> {
33
- updated: (state: T) => void;
34
- patches: (patches: Patch[]) => void;
35
- }
36
- interface SharedStateOptions<T> {
37
- /**
38
- * Initial state.
39
- */
40
- initialState: T;
41
- /**
42
- * Enable patches.
43
- *
44
- * @default false
45
- */
46
- enablePatches?: boolean;
47
- }
48
- declare function createSharedState<T extends Objectish>(options: SharedStateOptions<T>): SharedState<T>;
49
- //#endregion
50
- export { Immutable, ImmutableArray, ImmutableMap, ImmutableObject, ImmutableSet, SharedState, SharedStateEvents, SharedStateOptions, createSharedState };
@@ -1,26 +0,0 @@
1
- import { createEventEmitter } from "./events.mjs";
2
- import { applyPatches, produce, produceWithPatches } from "immer";
3
- //#region src/utils/state.ts
4
- function createSharedState(options) {
5
- const { enablePatches = false } = options;
6
- const events = createEventEmitter();
7
- let state = options.initialState;
8
- return {
9
- on: events.on,
10
- get: () => state,
11
- patch: (patches) => {
12
- state = applyPatches(state, patches);
13
- events.emit("updated", state);
14
- },
15
- mutate: (fn) => {
16
- if (enablePatches) {
17
- const [newState, patches] = produceWithPatches(state, fn);
18
- state = newState;
19
- events.emit("patches", patches);
20
- } else state = produce(state, fn);
21
- events.emit("updated", state);
22
- }
23
- };
24
- }
25
- //#endregion
26
- export { createSharedState };