devframe 0.1.19 → 0.1.21

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 (78) hide show
  1. package/README.md +2 -2
  2. package/dist/{_shared-BcPXmcjq.mjs → _shared-iZy45oG3.mjs} +2 -2
  3. package/dist/adapters/build.d.mts +3 -3
  4. package/dist/adapters/build.mjs +12 -9
  5. package/dist/adapters/cli.d.mts +1 -1
  6. package/dist/adapters/cli.mjs +2 -2
  7. package/dist/adapters/dev.d.mts +2 -2
  8. package/dist/adapters/dev.mjs +1 -1
  9. package/dist/adapters/embedded.d.mts +2 -2
  10. package/dist/adapters/embedded.mjs +1 -1
  11. package/dist/adapters/mcp.d.mts +1 -1
  12. package/dist/adapters/mcp.mjs +6 -2
  13. package/dist/adapters/vite.d.mts +5 -4
  14. package/dist/adapters/vite.mjs +4 -3
  15. package/dist/client/index.d.mts +51 -145
  16. package/dist/client/index.mjs +1664 -2
  17. package/dist/constants.d.mts +12 -15
  18. package/dist/constants.mjs +12 -29
  19. package/dist/context-BJ4r2SmR.mjs +1051 -0
  20. package/dist/context-internal-Dx_NoSv1.mjs +199 -0
  21. package/dist/context-internal-saIAfNVw.d.mts +51 -0
  22. package/dist/{dev-Djy5hbgs.mjs → dev-CdAy400a.mjs} +7 -6
  23. package/dist/{devtool-CIqONpYU.d.mts → devtool-CNvLs2_Y.d.mts} +367 -530
  24. package/dist/diagnostics-DxPnRoXO.mjs +51 -0
  25. package/dist/host-h3-9jeHcltx.mjs +24 -0
  26. package/dist/{human-id-CHS0s28X.mjs → human-id-BLoGo_e5.mjs} +0 -1
  27. package/dist/{index-BwrLhNmz.d.mts → index-CuLRIMto.d.mts} +1 -1
  28. package/dist/index.d.mts +4 -5
  29. package/dist/index.mjs +1 -4
  30. package/dist/internal/index.d.mts +16 -0
  31. package/dist/internal/index.mjs +3 -0
  32. package/dist/node/index.d.mts +45 -140
  33. package/dist/node/index.mjs +7 -6
  34. package/dist/recipes/open-helpers.d.mts +1 -1
  35. package/dist/recipes/open-helpers.mjs +3 -1
  36. package/dist/rpc/index.d.mts +2 -2
  37. package/dist/rpc/index.mjs +2 -2
  38. package/dist/rpc/transports/ws-client.d.mts +1 -1
  39. package/dist/rpc/transports/ws-client.mjs +1 -1
  40. package/dist/rpc/transports/ws-server.d.mts +1 -1
  41. package/dist/rpc/transports/ws-server.mjs +1 -1
  42. package/dist/{rpc-D7LxIh8t.mjs → rpc-INbWfHoX.mjs} +1 -1
  43. package/dist/{serialization-BN2ZQEE9.mjs → serialization-DwKi05Pn.mjs} +1 -1
  44. package/dist/{server-CaUeMcAh.d.mts → server-BAqOajx_.d.mts} +1 -1
  45. package/dist/{server-DrBxa6ZV.mjs → server-CBsxXIH5.mjs} +27 -2
  46. package/dist/{static-dump-cyGfMmRf.mjs → static-dump-D5VH8Iqk.mjs} +1 -1
  47. package/dist/types/index.d.mts +4 -4
  48. package/dist/utils/events.d.mts +1 -1
  49. package/dist/utils/human-id.mjs +1 -1
  50. package/dist/utils/shared-state.d.mts +2 -2
  51. package/dist/utils/shared-state.mjs +1 -1
  52. package/dist/utils/state.d.mts +2 -1
  53. package/dist/utils/state.mjs +1 -1
  54. package/dist/utils/streaming-channel.d.mts +2 -0
  55. package/dist/utils/streaming-channel.mjs +255 -0
  56. package/dist/utils/when.d.mts +401 -2
  57. package/dist/{ws-client-DOYwYluO.d.mts → ws-client-CjPuPFbA.d.mts} +1 -1
  58. package/dist/{ws-server-DHMeNOY_.d.mts → ws-server-C7LnhOHi.d.mts} +13 -1
  59. package/package.json +10 -21
  60. package/skills/devframe/README.md +13 -0
  61. package/skills/devframe/SKILL.md +406 -0
  62. package/skills/devframe/templates/counter-devtool.ts +24 -0
  63. package/skills/devframe/templates/spa-devtool.ts +28 -0
  64. package/skills/devframe/templates/vite-client.ts +11 -0
  65. package/dist/adapters/kit.d.mts +0 -23
  66. package/dist/adapters/kit.mjs +0 -16
  67. package/dist/client-C6w_NshP.mjs +0 -1569
  68. package/dist/context-BsNZMCnr.mjs +0 -6827
  69. package/dist/helpers/nuxt/index.d.mts +0 -46
  70. package/dist/helpers/nuxt/index.mjs +0 -58
  71. package/dist/helpers/nuxt/runtime/plugin.client.d.mts +0 -8
  72. package/dist/helpers/nuxt/runtime/plugin.client.mjs +0 -12
  73. package/dist/host-h3-w3c0t8ZO.mjs +0 -18
  74. package/dist/immer-HjMAm3b6.mjs +0 -894
  75. package/dist/main-CuGOfqoX.mjs +0 -601
  76. package/dist/when-BAE663Hv.d.mts +0 -401
  77. package/dist/{open-JfacLHs0.mjs → open-Dede_w9r.mjs} +4 -4
  78. /package/dist/{types-gWbe-b2R.d.mts → types-C5OVe4AC.d.mts} +0 -0
@@ -1,2 +1,2 @@
1
- import { C as ImmutableSet, D as createSharedState, E as SharedStateOptions, S as ImmutableObject, T as SharedStateEvents, b as ImmutableArray, k as Patch, w as SharedState, x as ImmutableMap, y as Immutable } from "../devtool-CIqONpYU.mjs";
2
- export { Immutable, ImmutableArray, ImmutableMap, ImmutableObject, ImmutableSet, SharedState, SharedStateEvents, SharedStateOptions, Patch as SharedStatePatch, createSharedState };
1
+ import { A as ImmutableArray, F as SharedStateEvents, I as SharedStateOptions, L as SharedStatePatch, M as ImmutableObject, N as ImmutableSet, P as SharedState, R as createSharedState, j as ImmutableMap, k as Immutable } from "../devtool-CNvLs2_Y.mjs";
2
+ export { Immutable, ImmutableArray, ImmutableMap, ImmutableObject, ImmutableSet, SharedState, SharedStateEvents, SharedStateOptions, SharedStatePatch, createSharedState };
@@ -1,6 +1,6 @@
1
1
  import { createEventEmitter } from "./events.mjs";
2
- import { i as produceWithPatches, n as enablePatches, r as produce, t as applyPatches } from "../immer-HjMAm3b6.mjs";
3
2
  import { nanoid } from "./nanoid.mjs";
3
+ import { applyPatches, enablePatches, produce, produceWithPatches } from "immer";
4
4
  //#region src/utils/shared-state.ts
5
5
  function createSharedState(options) {
6
6
  const { enablePatches: enablePatches$1 = false } = options;
@@ -1,4 +1,5 @@
1
- import { O as Objectish, Zt as EventEmitter, k as Patch } from "../devtool-CIqONpYU.mjs";
1
+ import { ct as EventEmitter } from "../devtool-CNvLs2_Y.mjs";
2
+ import { Objectish, Patch } from "immer";
2
3
 
3
4
  //#region src/utils/state.d.ts
4
5
  type ImmutablePrimitive = undefined | null | boolean | string | number | Function;
@@ -1,5 +1,5 @@
1
1
  import { createEventEmitter } from "./events.mjs";
2
- import { i as produceWithPatches, r as produce, t as applyPatches } from "../immer-HjMAm3b6.mjs";
2
+ import { applyPatches, produce, produceWithPatches } from "immer";
3
3
  //#region src/utils/state.ts
4
4
  function createSharedState(options) {
5
5
  const { enablePatches = false } = options;
@@ -0,0 +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";
2
+ export { BufferedChunk, CreateStreamReaderOptions, CreateStreamSinkOptions, StreamErrorPayload, StreamReader, StreamSink, StreamSinkEvents, createStreamReader, createStreamSink };
@@ -0,0 +1,255 @@
1
+ import { createEventEmitter } from "./events.mjs";
2
+ import { nanoid } from "./nanoid.mjs";
3
+ //#region src/utils/streaming-channel.ts
4
+ const DEFAULT_HIGH_WATER_MARK = 256;
5
+ var StreamClosedError = class extends Error {
6
+ name = "StreamClosedError";
7
+ };
8
+ /**
9
+ * Build a server-side stream sink. RPC-agnostic — the RPC host wires
10
+ * `events.on('chunk' | 'end')` to broadcast, and reads `buffer` to replay
11
+ * for late or reconnecting subscribers.
12
+ */
13
+ function createStreamSink(options = {}) {
14
+ const id = options.id ?? nanoid();
15
+ const replayWindow = Math.max(0, options.replayWindow ?? 0);
16
+ const events = createEventEmitter();
17
+ const controller = new AbortController();
18
+ const buffer = [];
19
+ let closed = false;
20
+ let lastSeq = 0;
21
+ function write(chunk) {
22
+ if (closed) throw new StreamClosedError(`Cannot write to a closed stream "${id}"`);
23
+ lastSeq += 1;
24
+ if (replayWindow > 0) {
25
+ buffer.push({
26
+ seq: lastSeq,
27
+ chunk
28
+ });
29
+ if (buffer.length > replayWindow) buffer.splice(0, buffer.length - replayWindow);
30
+ }
31
+ events.emit("chunk", lastSeq, chunk);
32
+ }
33
+ function error(reason) {
34
+ if (closed) return;
35
+ closed = true;
36
+ const payload = toErrorPayload(reason);
37
+ controller.abort(reason);
38
+ events.emit("end", payload);
39
+ }
40
+ function close() {
41
+ if (closed) return;
42
+ closed = true;
43
+ if (!controller.signal.aborted) controller.abort("stream closed");
44
+ events.emit("end", void 0);
45
+ }
46
+ function abort(reason) {
47
+ if (closed) return;
48
+ if (!controller.signal.aborted) controller.abort(reason ?? "aborted");
49
+ }
50
+ const writable = new WritableStream({
51
+ write(chunk) {
52
+ write(chunk);
53
+ },
54
+ close() {
55
+ close();
56
+ },
57
+ abort(reason) {
58
+ error(reason);
59
+ }
60
+ });
61
+ return {
62
+ id,
63
+ signal: controller.signal,
64
+ get closed() {
65
+ return closed;
66
+ },
67
+ get lastSeq() {
68
+ return lastSeq;
69
+ },
70
+ write,
71
+ error,
72
+ close,
73
+ abort,
74
+ writable,
75
+ events,
76
+ buffer
77
+ };
78
+ }
79
+ /**
80
+ * Build a client-side stream reader. RPC-agnostic — the RPC host calls
81
+ * `_push(seq, chunk)` on each incoming chunk and `_end(error?)` on the
82
+ * terminal frame. Consumers iterate with `for await` or pipe `readable`.
83
+ */
84
+ function createStreamReader(options = {}) {
85
+ const id = options.id ?? nanoid();
86
+ const highWaterMark = Math.max(1, options.highWaterMark ?? DEFAULT_HIGH_WATER_MARK);
87
+ const queue = [];
88
+ let lastSeenSeq = 0;
89
+ let done = false;
90
+ let cancelled = false;
91
+ let endError;
92
+ let pending;
93
+ let pullController;
94
+ let readableInstance;
95
+ function drainNext() {
96
+ if (!pending) return;
97
+ if (queue.length > 0) {
98
+ const value = queue.shift();
99
+ const r = pending;
100
+ pending = void 0;
101
+ r.resolve({
102
+ value,
103
+ done: false
104
+ });
105
+ return;
106
+ }
107
+ if (done) {
108
+ const r = pending;
109
+ pending = void 0;
110
+ if (endError) {
111
+ const err = new Error(endError.message);
112
+ err.name = endError.name;
113
+ r.reject(err);
114
+ } else r.resolve({
115
+ value: void 0,
116
+ done: true
117
+ });
118
+ }
119
+ }
120
+ function feedReadable() {
121
+ if (!pullController) return;
122
+ while (queue.length > 0) {
123
+ const v = queue.shift();
124
+ try {
125
+ pullController.enqueue(v);
126
+ } catch {
127
+ break;
128
+ }
129
+ }
130
+ if (done && pullController) {
131
+ try {
132
+ if (endError) {
133
+ const err = new Error(endError.message);
134
+ err.name = endError.name;
135
+ pullController.error(err);
136
+ } else pullController.close();
137
+ } catch {}
138
+ pullController = void 0;
139
+ }
140
+ }
141
+ function push(seq, chunk) {
142
+ if (done || cancelled) return;
143
+ if (seq <= lastSeenSeq) return;
144
+ lastSeenSeq = seq;
145
+ queue.push(chunk);
146
+ if (queue.length > highWaterMark) {
147
+ const overflow = queue.length - highWaterMark;
148
+ queue.splice(0, overflow);
149
+ options.onOverflow?.(overflow);
150
+ }
151
+ drainNext();
152
+ if (readableInstance) feedReadable();
153
+ }
154
+ function end(error) {
155
+ if (done) return;
156
+ done = true;
157
+ endError = error;
158
+ drainNext();
159
+ if (readableInstance) feedReadable();
160
+ }
161
+ function cancel() {
162
+ if (cancelled || done) return;
163
+ cancelled = true;
164
+ options.onCancel?.();
165
+ end(void 0);
166
+ }
167
+ function getReadable() {
168
+ if (readableInstance) return readableInstance;
169
+ readableInstance = new ReadableStream({
170
+ start(controller) {
171
+ pullController = controller;
172
+ feedReadable();
173
+ },
174
+ cancel() {
175
+ cancel();
176
+ }
177
+ });
178
+ return readableInstance;
179
+ }
180
+ return {
181
+ id,
182
+ get cancelled() {
183
+ return cancelled;
184
+ },
185
+ get done() {
186
+ return done;
187
+ },
188
+ get lastSeenSeq() {
189
+ return lastSeenSeq;
190
+ },
191
+ get readable() {
192
+ return getReadable();
193
+ },
194
+ cancel,
195
+ _push: push,
196
+ _end: end,
197
+ [Symbol.asyncIterator]() {
198
+ return {
199
+ next() {
200
+ if (queue.length > 0) return Promise.resolve({
201
+ value: queue.shift(),
202
+ done: false
203
+ });
204
+ if (done) {
205
+ if (endError) {
206
+ const err = new Error(endError.message);
207
+ err.name = endError.name;
208
+ return Promise.reject(err);
209
+ }
210
+ return Promise.resolve({
211
+ value: void 0,
212
+ done: true
213
+ });
214
+ }
215
+ return new Promise((resolve, reject) => {
216
+ pending = {
217
+ resolve,
218
+ reject
219
+ };
220
+ });
221
+ },
222
+ return() {
223
+ cancel();
224
+ return Promise.resolve({
225
+ value: void 0,
226
+ done: true
227
+ });
228
+ }
229
+ };
230
+ }
231
+ };
232
+ }
233
+ function toErrorPayload(reason) {
234
+ if (reason instanceof Error) return {
235
+ name: reason.name || "Error",
236
+ message: reason.message
237
+ };
238
+ if (typeof reason === "string") return {
239
+ name: "Error",
240
+ message: reason
241
+ };
242
+ try {
243
+ return {
244
+ name: "Error",
245
+ message: JSON.stringify(reason)
246
+ };
247
+ } catch {
248
+ return {
249
+ name: "Error",
250
+ message: String(reason)
251
+ };
252
+ }
253
+ }
254
+ //#endregion
255
+ export { createStreamReader, createStreamSink };
@@ -1,2 +1,401 @@
1
- import { i as resolveContextValue, n as WhenExpression, r as evaluateWhen, t as WhenContext } from "../when-BAE663Hv.mjs";
2
- export { WhenContext, WhenExpression, evaluateWhen, resolveContextValue };
1
+ //#region ../../../node_modules/.pnpm/whenexpr@0.1.2/node_modules/whenexpr/dist/index.d.mts
2
+ //#region src/paths.d.ts
3
+ /**
4
+ * Union of valid context paths for `T` — both flat keys (`keyof T`) and
5
+ * `.`/`:`-separated nested paths. Mirrors the runtime `resolve()` lookup:
6
+ * first a flat literal key, then a segmented path.
7
+ *
8
+ * @example
9
+ * type P = ContextPaths<{ editor: { lang: string }, debug: boolean }>
10
+ * // 'editor' | 'debug' | 'editor.lang' | 'editor:lang'
11
+ */
12
+ type ContextPaths<T, P extends string = ''> = { [K in keyof T & string]: `${P}${K}` | (T[K] extends readonly unknown[] ? never : T[K] extends ((...args: never[]) => unknown) ? never : T[K] extends object ? ContextPaths<T[K], `${P}${K}.`> | ContextPaths<T[K], `${P}${K}:`> : never) }[keyof T & string]; //#endregion
13
+ //#region src/types.d.ts
14
+ /**
15
+ * Branded error type surfaced at the call site of `evaluateWhen` when the
16
+ * expression string fails static validation. The embedded message appears in
17
+ * the TS error tooltip because `WhenExpressionError<M>` is not assignable from a
18
+ * plain string literal.
19
+ */
20
+ type WhenExpressionError<M extends string> = string & {
21
+ readonly __whenExpressionError: M;
22
+ };
23
+ type Ws = ' ' | '\t' | '\n' | '\r';
24
+ type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
25
+ type Lower = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
26
+ type Upper = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z';
27
+ type IdentStart = Lower | Upper | '_' | '$';
28
+ type IdentCont = IdentStart | Digit | '.' | ':';
29
+ type Op = '===' | '!==' | '==' | '!=' | '<=' | '>=' | '&&' | '||' | '<' | '>' | '+' | '-' | '*' | '/' | '%' | '!';
30
+ type Tok = {
31
+ k: 'num';
32
+ v: string;
33
+ } | {
34
+ k: 'str';
35
+ v: string;
36
+ } | {
37
+ k: 'id';
38
+ v: string;
39
+ } | {
40
+ k: 'bool';
41
+ v: 'true' | 'false';
42
+ } | {
43
+ k: 'op';
44
+ v: Op;
45
+ } | {
46
+ k: 'lp';
47
+ } | {
48
+ k: 'rp';
49
+ } | {
50
+ k: 'eof';
51
+ };
52
+ interface TokErr {
53
+ __err: string;
54
+ }
55
+ type Tokenize<S extends string, Acc extends Tok[] = []> = S extends '' ? [...Acc, {
56
+ k: 'eof';
57
+ }] : S extends `${Ws}${infer R}` ? Tokenize<R, Acc> : S extends `===${infer R}` ? Tokenize<R, [...Acc, {
58
+ k: 'op';
59
+ v: '===';
60
+ }]> : S extends `!==${infer R}` ? Tokenize<R, [...Acc, {
61
+ k: 'op';
62
+ v: '!==';
63
+ }]> : S extends `==${infer R}` ? Tokenize<R, [...Acc, {
64
+ k: 'op';
65
+ v: '==';
66
+ }]> : S extends `!=${infer R}` ? Tokenize<R, [...Acc, {
67
+ k: 'op';
68
+ v: '!=';
69
+ }]> : S extends `<=${infer R}` ? Tokenize<R, [...Acc, {
70
+ k: 'op';
71
+ v: '<=';
72
+ }]> : S extends `>=${infer R}` ? Tokenize<R, [...Acc, {
73
+ k: 'op';
74
+ v: '>=';
75
+ }]> : S extends `&&${infer R}` ? Tokenize<R, [...Acc, {
76
+ k: 'op';
77
+ v: '&&';
78
+ }]> : S extends `||${infer R}` ? Tokenize<R, [...Acc, {
79
+ k: 'op';
80
+ v: '||';
81
+ }]> : S extends `<${infer R}` ? Tokenize<R, [...Acc, {
82
+ k: 'op';
83
+ v: '<';
84
+ }]> : S extends `>${infer R}` ? Tokenize<R, [...Acc, {
85
+ k: 'op';
86
+ v: '>';
87
+ }]> : S extends `+${infer R}` ? Tokenize<R, [...Acc, {
88
+ k: 'op';
89
+ v: '+';
90
+ }]> : S extends `-${infer R}` ? Tokenize<R, [...Acc, {
91
+ k: 'op';
92
+ v: '-';
93
+ }]> : S extends `*${infer R}` ? Tokenize<R, [...Acc, {
94
+ k: 'op';
95
+ v: '*';
96
+ }]> : S extends `/${infer R}` ? Tokenize<R, [...Acc, {
97
+ k: 'op';
98
+ v: '/';
99
+ }]> : S extends `%${infer R}` ? Tokenize<R, [...Acc, {
100
+ k: 'op';
101
+ v: '%';
102
+ }]> : S extends `!${infer R}` ? Tokenize<R, [...Acc, {
103
+ k: 'op';
104
+ v: '!';
105
+ }]> : S extends `(${infer R}` ? Tokenize<R, [...Acc, {
106
+ k: 'lp';
107
+ }]> : S extends `)${infer R}` ? Tokenize<R, [...Acc, {
108
+ k: 'rp';
109
+ }]> : S extends `"${infer R}` ? ConsumeStr<R, '', '"', Acc> : S extends `'${infer R}` ? ConsumeStr<R, '', '\'', Acc> : S extends `${infer C extends Digit}${infer R}` ? ConsumeNum<R, C, Acc> : S extends `${infer C extends IdentStart}${infer R}` ? ConsumeIdent<R, C, Acc> : {
110
+ __err: `Unexpected character in "${S}"`;
111
+ };
112
+ type ConsumeNum<S extends string, N extends string, Acc extends Tok[]> = S extends `${infer C extends Digit}${infer R}` ? ConsumeNum<R, `${N}${C}`, Acc> : S extends `.${infer C extends Digit}${infer R}` ? ConsumeNumFrac<R, `${N}.${C}`, Acc> : Tokenize<S, [...Acc, {
113
+ k: 'num';
114
+ v: N;
115
+ }]>;
116
+ type ConsumeNumFrac<S extends string, N extends string, Acc extends Tok[]> = S extends `${infer C extends Digit}${infer R}` ? ConsumeNumFrac<R, `${N}${C}`, Acc> : Tokenize<S, [...Acc, {
117
+ k: 'num';
118
+ v: N;
119
+ }]>;
120
+ type ConsumeIdent<S extends string, N extends string, Acc extends Tok[]> = S extends `${infer C extends IdentCont}${infer R}` ? ConsumeIdent<R, `${N}${C}`, Acc> : Tokenize<S, [...Acc, ClassifyIdent<N>]>;
121
+ type ClassifyIdent<N extends string> = N extends 'true' ? {
122
+ k: 'bool';
123
+ v: 'true';
124
+ } : N extends 'false' ? {
125
+ k: 'bool';
126
+ v: 'false';
127
+ } : {
128
+ k: 'id';
129
+ v: N;
130
+ };
131
+ type ConsumeStr<S extends string, V extends string, Q extends '"' | '\'', Acc extends Tok[]> = S extends `${Q}${infer R}` ? Tokenize<R, [...Acc, {
132
+ k: 'str';
133
+ v: V;
134
+ }]> : S extends `\\${infer C}${infer R}` ? ConsumeStr<R, `${V}${C}`, Q, Acc> : S extends `${infer C}${infer R}` ? ConsumeStr<R, `${V}${C}`, Q, Acc> : {
135
+ __err: 'Unterminated string literal';
136
+ };
137
+ type BinOp = '||' | '&&' | '==' | '!=' | '===' | '!==' | '<' | '>' | '<=' | '>=' | '+' | '-' | '*' | '/' | '%';
138
+ type UnOp = '!' | '-' | '+';
139
+ interface LitNode {
140
+ t: 'lit';
141
+ v: string;
142
+ }
143
+ interface KeyNode {
144
+ t: 'key';
145
+ v: string;
146
+ }
147
+ interface UnNode {
148
+ t: 'un';
149
+ op: UnOp;
150
+ x: Ast;
151
+ }
152
+ interface BinNode {
153
+ t: 'bin';
154
+ op: BinOp;
155
+ l: Ast;
156
+ r: Ast;
157
+ }
158
+ type Ast = LitNode | KeyNode | UnNode | BinNode;
159
+ type Parse<Toks extends Tok[]> = ParseOr<Toks>;
160
+ type ParseOr<T extends Tok[]> = OrTail<ParseAnd<T>>;
161
+ type OrTail<R> = R extends [infer L extends Ast, infer Rest extends Tok[]] ? Rest extends [{
162
+ k: 'op';
163
+ v: '||';
164
+ }, ...infer After extends Tok[]] ? ParseAnd<After> extends [infer Rh extends Ast, infer Next extends Tok[]] ? OrTail<[{
165
+ t: 'bin';
166
+ op: '||';
167
+ l: L;
168
+ r: Rh;
169
+ }, Next]> : 'err' : R : R;
170
+ type ParseAnd<T extends Tok[]> = AndTail<ParseEq<T>>;
171
+ type AndTail<R> = R extends [infer L extends Ast, infer Rest extends Tok[]] ? Rest extends [{
172
+ k: 'op';
173
+ v: '&&';
174
+ }, ...infer After extends Tok[]] ? ParseEq<After> extends [infer Rh extends Ast, infer Next extends Tok[]] ? AndTail<[{
175
+ t: 'bin';
176
+ op: '&&';
177
+ l: L;
178
+ r: Rh;
179
+ }, Next]> : 'err' : R : R;
180
+ type ParseEq<T extends Tok[]> = EqTail<ParseRel<T>>;
181
+ type EqTail<R> = R extends [infer L extends Ast, infer Rest extends Tok[]] ? Rest extends [{
182
+ k: 'op';
183
+ v: infer O extends '==' | '!=';
184
+ }, ...infer After extends Tok[]] ? ParseValueRhs<After> extends [infer Rh extends Ast, infer Next extends Tok[]] ? EqTail<[{
185
+ t: 'bin';
186
+ op: O;
187
+ l: L;
188
+ r: Rh;
189
+ }, Next]> : 'err' : Rest extends [{
190
+ k: 'op';
191
+ v: infer O extends '===' | '!==';
192
+ }, ...infer After extends Tok[]] ? ParseRel<After> extends [infer Rh extends Ast, infer Next extends Tok[]] ? EqTail<[{
193
+ t: 'bin';
194
+ op: O;
195
+ l: L;
196
+ r: Rh;
197
+ }, Next]> : 'err' : R : R;
198
+ type ParseValueRhs<T extends Tok[]> = T extends [{
199
+ k: 'id';
200
+ v: infer V extends string;
201
+ }, ...infer R extends Tok[]] ? [{
202
+ t: 'lit';
203
+ v: V;
204
+ }, R] : T extends [{
205
+ k: 'str';
206
+ v: infer V extends string;
207
+ }, ...infer R extends Tok[]] ? [{
208
+ t: 'lit';
209
+ v: V;
210
+ }, R] : T extends [{
211
+ k: 'num';
212
+ v: infer V extends string;
213
+ }, ...infer R extends Tok[]] ? [{
214
+ t: 'lit';
215
+ v: V;
216
+ }, R] : T extends [{
217
+ k: 'bool';
218
+ v: infer V extends string;
219
+ }, ...infer R extends Tok[]] ? [{
220
+ t: 'lit';
221
+ v: V;
222
+ }, R] : 'err';
223
+ type ParseRel<T extends Tok[]> = RelTail<ParseAdd<T>>;
224
+ type RelTail<R> = R extends [infer L extends Ast, infer Rest extends Tok[]] ? Rest extends [{
225
+ k: 'op';
226
+ v: infer O extends '<=' | '>=' | '<' | '>';
227
+ }, ...infer After extends Tok[]] ? ParseAdd<After> extends [infer Rh extends Ast, infer Next extends Tok[]] ? RelTail<[{
228
+ t: 'bin';
229
+ op: O;
230
+ l: L;
231
+ r: Rh;
232
+ }, Next]> : 'err' : R : R;
233
+ type ParseAdd<T extends Tok[]> = AddTail<ParseMul<T>>;
234
+ type AddTail<R> = R extends [infer L extends Ast, infer Rest extends Tok[]] ? Rest extends [{
235
+ k: 'op';
236
+ v: infer O extends '+' | '-';
237
+ }, ...infer After extends Tok[]] ? ParseMul<After> extends [infer Rh extends Ast, infer Next extends Tok[]] ? AddTail<[{
238
+ t: 'bin';
239
+ op: O;
240
+ l: L;
241
+ r: Rh;
242
+ }, Next]> : 'err' : R : R;
243
+ type ParseMul<T extends Tok[]> = MulTail<ParseUnary<T>>;
244
+ type MulTail<R> = R extends [infer L extends Ast, infer Rest extends Tok[]] ? Rest extends [{
245
+ k: 'op';
246
+ v: infer O extends '*' | '/' | '%';
247
+ }, ...infer After extends Tok[]] ? ParseUnary<After> extends [infer Rh extends Ast, infer Next extends Tok[]] ? MulTail<[{
248
+ t: 'bin';
249
+ op: O;
250
+ l: L;
251
+ r: Rh;
252
+ }, Next]> : 'err' : R : R;
253
+ type ParseUnary<T extends Tok[]> = T extends [{
254
+ k: 'op';
255
+ v: infer O extends UnOp;
256
+ }, ...infer Rest extends Tok[]] ? ParseUnary<Rest> extends [infer X extends Ast, infer Next extends Tok[]] ? [{
257
+ t: 'un';
258
+ op: O;
259
+ x: X;
260
+ }, Next] : 'err' : ParsePrimary<T>;
261
+ type ParsePrimary<T extends Tok[]> = T extends [{
262
+ k: 'num';
263
+ v: infer V extends string;
264
+ }, ...infer R extends Tok[]] ? [{
265
+ t: 'lit';
266
+ v: V;
267
+ }, R] : T extends [{
268
+ k: 'str';
269
+ v: infer V extends string;
270
+ }, ...infer R extends Tok[]] ? [{
271
+ t: 'lit';
272
+ v: V;
273
+ }, R] : T extends [{
274
+ k: 'bool';
275
+ v: infer V extends string;
276
+ }, ...infer R extends Tok[]] ? [{
277
+ t: 'lit';
278
+ v: V;
279
+ }, R] : T extends [{
280
+ k: 'id';
281
+ v: infer V extends string;
282
+ }, ...infer R extends Tok[]] ? [{
283
+ t: 'key';
284
+ v: V;
285
+ }, R] : T extends [{
286
+ k: 'lp';
287
+ }, ...infer Rest extends Tok[]] ? ParseOr<Rest> extends [infer N extends Ast, infer After extends Tok[]] ? After extends [{
288
+ k: 'rp';
289
+ }, ...infer Next extends Tok[]] ? [N, Next] : 'err' : 'err' : 'err';
290
+ type CollectKeys<N> = N extends KeyNode ? N['v'] : N extends UnNode ? CollectKeys<N['x']> : N extends {
291
+ t: 'bin';
292
+ op: '==' | '!=';
293
+ l: infer L;
294
+ } ? CollectKeys<L> : N extends BinNode ? CollectKeys<N['l']> | CollectKeys<N['r']> : never;
295
+ /**
296
+ * Validate a when-clause expression string `S` against context type `T`.
297
+ * Returns `S` on success, or a `WhenExpressionError<Msg>` branded error string
298
+ * that surfaces at the call site with a human-readable message.
299
+ *
300
+ * - Dynamic `string` expressions skip all validation.
301
+ * - Wide context types (`object`, `any`, `unknown`, `Record<string, unknown>`,
302
+ * empty `{}`, etc.) still run **syntax** checking but skip **key** checking.
303
+ * - Specific context types run full validation (syntax + keys).
304
+ */
305
+ type ValidateExpression<S extends string, T> = string extends S ? S : Tokenize<S> extends infer Toks ? Toks extends TokErr ? WhenExpressionError<`Syntax error: ${Toks['__err']}`> : Toks extends Tok[] ? Parse<Toks> extends [infer N extends Ast, [{
306
+ k: 'eof';
307
+ }]] ? [keyof T] extends [never] ? S : string extends keyof T ? S : Exclude<CollectKeys<N>, ContextPaths<T>> extends infer Missing ? [Missing] extends [never] ? S : WhenExpressionError<`Unknown context key: "${Extract<Missing, string>}"`> : never : WhenExpressionError<'Syntax error in expression'> : never : never;
308
+ /**
309
+ * A when-clause expression string validated against context type `T`.
310
+ * Use inside your own typed define helpers to have TypeScript check `when`
311
+ * fields at the call site.
312
+ *
313
+ * @example
314
+ * interface AppCtx { mode: 'dev' | 'prod', debug: boolean }
315
+ *
316
+ * function defineCommand<const W extends string>(cmd: {
317
+ * name: string
318
+ * title: string
319
+ * when?: WhenExpression<AppCtx, W>
320
+ * }): typeof cmd {
321
+ * return cmd
322
+ * }
323
+ *
324
+ * defineCommand({ name: 'x', title: 'X', when: 'debug && mode == dev' })
325
+ * defineCommand({ name: 'x', title: 'X', when: 'typo' })
326
+ * // ^^^^^ type error
327
+ */
328
+ type WhenExpression<T, S extends string> = S & ValidateExpression<S, T>; //#endregion
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
+ //#endregion
373
+ //#region src/utils/when.d.ts
374
+ /**
375
+ * Context object for evaluating `when` expressions.
376
+ *
377
+ * Built-in variables:
378
+ * - `clientType` — `'embedded' | 'standalone'`
379
+ * - `dockOpen` — whether the dock panel is open
380
+ * - `paletteOpen` — whether the command palette is open
381
+ * - `dockSelectedId` — ID of the selected dock entry (empty string if none)
382
+ *
383
+ * Plugins can add namespaced variables using dot or colon separators:
384
+ * - `vite.mode`, `vite:mode` — stored as `{ 'vite.mode': 'development' }` or nested `{ vite: { mode: 'development' } }`
385
+ *
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.
391
+ */
392
+ interface WhenContext {
393
+ clientType: 'embedded' | 'standalone';
394
+ dockOpen: boolean;
395
+ paletteOpen: boolean;
396
+ dockSelectedId: string;
397
+ /** Allow custom context variables from plugins */
398
+ [key: string]: unknown;
399
+ }
400
+ //#endregion
401
+ export { WhenContext, type WhenExpression, evaluateWhen, resolveContextValue };