devframe 0.1.20 → 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.
- package/README.md +41 -18
- package/dist/{_shared-S-Ujqz_L.mjs → _shared-DcJX9-Az.mjs} +4 -4
- package/dist/adapters/build.d.mts +7 -7
- package/dist/adapters/build.mjs +15 -14
- package/dist/adapters/cli.d.mts +2 -2
- package/dist/adapters/cli.mjs +5 -5
- package/dist/adapters/dev.d.mts +18 -9
- package/dist/adapters/dev.mjs +1 -1
- package/dist/adapters/embedded.d.mts +6 -6
- package/dist/adapters/embedded.mjs +3 -3
- package/dist/adapters/mcp.d.mts +3 -3
- package/dist/adapters/mcp.mjs +6 -5
- package/dist/adapters/vite.d.mts +46 -12
- package/dist/adapters/vite.mjs +81 -17
- package/dist/chunk-e9Ob2GDo.mjs +26 -0
- package/dist/client/index.d.mts +19 -144
- package/dist/client/index.mjs +20 -20
- package/dist/colors-Bl4W18Mh.mjs +123 -0
- package/dist/constants.d.mts +12 -15
- package/dist/constants.mjs +12 -29
- package/dist/context-DBd794Rn.d.mts +51 -0
- package/dist/context-DUibxhrR.mjs +1034 -0
- package/dist/{dev-B8i_CBlQ.mjs → dev-BGpTpYiR.mjs} +20 -21
- package/dist/{devtool-Bm6zZAw5.d.mts → devframe-DulcIxwu.d.mts} +59 -569
- package/dist/diagnostics-BFhNNmST.mjs +56 -0
- package/dist/hash-BHZbo80D.mjs +126 -0
- package/dist/{human-id-CHS0s28X.mjs → human-id-adtUWQLr.mjs} +12 -4
- package/dist/{index-BCo03GEF.d.mts → index-DB2Ie0Or.d.mts} +2 -3
- package/dist/index.d.mts +4 -6
- package/dist/index.mjs +2 -8
- package/dist/launch-editor-KA_21J1T.mjs +581 -0
- package/dist/node/auth.d.mts +44 -0
- package/dist/node/auth.mjs +55 -0
- package/dist/node/index.d.mts +14 -187
- package/dist/node/index.mjs +6 -61
- package/dist/node/internal.d.mts +16 -0
- package/dist/node/internal.mjs +50 -0
- package/dist/{open-BtOOEldu.mjs → open-DmFp0qZA.mjs} +15 -6
- package/dist/recipes/open-helpers.d.mts +4 -9
- package/dist/recipes/open-helpers.mjs +7 -12
- package/dist/revoke-DIMz1r7-.mjs +35 -0
- package/dist/rpc/index.d.mts +3 -3
- package/dist/rpc/index.mjs +4 -4
- package/dist/rpc/transports/ws-client.d.mts +1 -1
- package/dist/rpc/transports/ws-client.mjs +2 -1
- package/dist/rpc/transports/ws-server.d.mts +1 -1
- package/dist/rpc/transports/ws-server.mjs +2 -1
- package/dist/{rpc-INbWfHoX.mjs → rpc-CkZuoz-m.mjs} +3 -2
- package/dist/{serialization-DwKi05Pn.mjs → serialization-CwO31axa.mjs} +1 -2
- package/dist/{server-qRRM8t3v.mjs → server-ma8-ZVyX.mjs} +1 -1
- package/dist/{server-DksyT-um.d.mts → server-zKEDxgqD.d.mts} +4 -3
- package/dist/shared-state-DCDs0e7y.mjs +928 -0
- package/dist/{static-dump-C1aVSwxY.mjs → static-dump-D7AvjrgL.mjs} +1 -1
- package/dist/storage-CNC38eU-.mjs +117 -0
- package/dist/structured-clone-DcDc2Dds.mjs +221 -0
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +11 -2
- package/dist/utils/colors.d.mts +28 -0
- package/dist/utils/colors.mjs +2 -0
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/hash.d.mts +7 -0
- package/dist/utils/hash.mjs +2 -0
- package/dist/utils/human-id.d.mts +6 -8
- package/dist/utils/human-id.mjs +1 -2
- package/dist/utils/launch-editor.d.mts +13 -0
- package/dist/utils/launch-editor.mjs +2 -0
- package/dist/utils/open.d.mts +16 -0
- package/dist/utils/open.mjs +2 -0
- package/dist/utils/serve-static.d.mts +30 -0
- package/dist/utils/serve-static.mjs +152 -0
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/shared-state.mjs +1 -35
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/utils/structured-clone.d.mts +21 -0
- package/dist/utils/structured-clone.mjs +2 -0
- package/dist/utils/when.d.mts +384 -2
- package/dist/utils/when.mjs +20 -2
- package/dist/{ws-client-DQySVfF_.d.mts → ws-client-Bavoya6-.d.mts} +1 -1
- package/dist/{ws-server-VQqESKAs.d.mts → ws-server-DjvlwLuM.d.mts} +1 -1
- package/package.json +23 -13
- package/skills/devframe/SKILL.md +93 -117
- package/skills/devframe/templates/{counter-devtool.ts → counter-devframe.ts} +5 -10
- package/skills/devframe/templates/{spa-devtool.ts → spa-devframe.ts} +5 -10
- package/skills/devframe/templates/vite-client.ts +2 -2
- package/dist/adapters/kit.d.mts +0 -23
- package/dist/adapters/kit.mjs +0 -16
- package/dist/context-BfvbAyGk.mjs +0 -7215
- package/dist/main-DpINGndA.mjs +0 -601
- package/dist/utils/state.d.mts +0 -50
- package/dist/utils/state.mjs +0 -26
- package/dist/when-aBBXAEh5.d.mts +0 -401
- package/dist/{define-Bb4zh-Dc.mjs → define-CW9gLnyG.mjs} +0 -0
- package/dist/{host-h3-BMvrFzIJ.mjs → host-h3-Dz8YnFgu.mjs} +2 -2
- /package/dist/{transports-BPUzHhI2.mjs → transports-BnCs4rj9.mjs} +0 -0
- /package/dist/{types-ag029cAe.d.mts → types-4rdUEi2R.d.mts} +0 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { extname, join, normalize, resolve, sep } from "pathe";
|
|
3
|
+
import { defineEventHandler, sendStream, setResponseHeader, setResponseStatus } from "h3";
|
|
4
|
+
import { stat } from "node:fs/promises";
|
|
5
|
+
import { lookup } from "mrmime";
|
|
6
|
+
//#region src/utils/serve-static.ts
|
|
7
|
+
const HTML_EXTENSIONS = [".html", ".htm"];
|
|
8
|
+
async function statFile(abs) {
|
|
9
|
+
try {
|
|
10
|
+
const s = await stat(abs);
|
|
11
|
+
if (!s.isFile()) return null;
|
|
12
|
+
return {
|
|
13
|
+
abs,
|
|
14
|
+
size: s.size,
|
|
15
|
+
mtime: s.mtime
|
|
16
|
+
};
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function resolveTarget(absDir, urlPath, indexNames, single) {
|
|
22
|
+
let cleaned;
|
|
23
|
+
try {
|
|
24
|
+
cleaned = decodeURIComponent(urlPath || "/");
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
cleaned = cleaned.replace(/[?#].*$/, "");
|
|
29
|
+
if (cleaned.endsWith("/")) cleaned = cleaned.slice(0, -1);
|
|
30
|
+
if (cleaned.startsWith("/")) cleaned = cleaned.slice(1);
|
|
31
|
+
const abs = normalize(join(absDir, cleaned));
|
|
32
|
+
if (abs !== absDir && !abs.startsWith(absDir + sep)) return null;
|
|
33
|
+
const direct = await statFile(abs);
|
|
34
|
+
if (direct) return direct;
|
|
35
|
+
try {
|
|
36
|
+
if ((await stat(abs)).isDirectory()) for (const name of indexNames) {
|
|
37
|
+
const candidate = await statFile(join(abs, name));
|
|
38
|
+
if (candidate) return candidate;
|
|
39
|
+
}
|
|
40
|
+
} catch {}
|
|
41
|
+
if (!extname(cleaned)) for (const ext of HTML_EXTENSIONS) {
|
|
42
|
+
const candidate = await statFile(abs + ext);
|
|
43
|
+
if (candidate) return candidate;
|
|
44
|
+
}
|
|
45
|
+
const fallbackIndex = indexNames[0];
|
|
46
|
+
if (single && fallbackIndex && !/\.[a-z0-9]+$/i.test(cleaned)) {
|
|
47
|
+
const indexFile = await statFile(join(absDir, fallbackIndex));
|
|
48
|
+
if (indexFile) return indexFile;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
function contentTypeFor(abs) {
|
|
53
|
+
const type = lookup(abs);
|
|
54
|
+
if (!type) return "application/octet-stream";
|
|
55
|
+
if (type === "text/html") return "text/html; charset=utf-8";
|
|
56
|
+
return type;
|
|
57
|
+
}
|
|
58
|
+
function setStaticHeaders(res, file) {
|
|
59
|
+
res.setHeader("Content-Type", contentTypeFor(file.abs));
|
|
60
|
+
res.setHeader("Content-Length", file.size);
|
|
61
|
+
res.setHeader("Last-Modified", file.mtime.toUTCString());
|
|
62
|
+
res.setHeader("Cache-Control", "no-store");
|
|
63
|
+
}
|
|
64
|
+
function normalizeOptions(options) {
|
|
65
|
+
return {
|
|
66
|
+
indexNames: options?.indexNames ?? ["index.html"],
|
|
67
|
+
single: options?.single ?? true
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* h3 event handler that serves files from `dir` with SPA fallback.
|
|
72
|
+
*
|
|
73
|
+
* Drop-in replacement for `fromNodeMiddleware(sirv(dir, { dev: true, single: true }))`
|
|
74
|
+
* when the surrounding server is an h3 app — no `Cache-Control` beyond
|
|
75
|
+
* `no-store`, `Content-Type` resolved via `mrmime`, and a miss with no
|
|
76
|
+
* file extension falls back to `<dir>/index.html` so client-side routing
|
|
77
|
+
* works.
|
|
78
|
+
*/
|
|
79
|
+
function serveStaticHandler(dir, options) {
|
|
80
|
+
const absDir = resolve(dir);
|
|
81
|
+
const opts = normalizeOptions(options);
|
|
82
|
+
return defineEventHandler(async (event) => {
|
|
83
|
+
const method = event.node.req.method;
|
|
84
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
85
|
+
setResponseStatus(event, 405);
|
|
86
|
+
setResponseHeader(event, "Allow", "GET, HEAD");
|
|
87
|
+
return "";
|
|
88
|
+
}
|
|
89
|
+
const file = await resolveTarget(absDir, event.node.req.url ?? "/", opts.indexNames, opts.single);
|
|
90
|
+
if (!file) {
|
|
91
|
+
setResponseStatus(event, 404);
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
setStaticHeaders(event.node.res, file);
|
|
95
|
+
if (method === "HEAD") {
|
|
96
|
+
event.node.res.end();
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
return sendStream(event, createReadStream(file.abs));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Connect/Express-style Node middleware variant of {@link serveStaticHandler}.
|
|
104
|
+
*
|
|
105
|
+
* Use when mounting onto `viteServer.middlewares.use(base, …)` or any other
|
|
106
|
+
* Connect stack — avoids forcing the host package to depend on h3 just to
|
|
107
|
+
* adapt an event handler back into Node middleware.
|
|
108
|
+
*/
|
|
109
|
+
function serveStaticNodeMiddleware(dir, options) {
|
|
110
|
+
const absDir = resolve(dir);
|
|
111
|
+
const opts = normalizeOptions(options);
|
|
112
|
+
return (req, res, next) => {
|
|
113
|
+
(async () => {
|
|
114
|
+
const method = req.method;
|
|
115
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
116
|
+
if (next) {
|
|
117
|
+
next();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
res.statusCode = 405;
|
|
121
|
+
res.setHeader("Allow", "GET, HEAD");
|
|
122
|
+
res.end();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const file = await resolveTarget(absDir, req.url ?? "/", opts.indexNames, opts.single);
|
|
126
|
+
if (!file) {
|
|
127
|
+
if (next) {
|
|
128
|
+
next();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
res.statusCode = 404;
|
|
132
|
+
res.end();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
setStaticHeaders(res, file);
|
|
136
|
+
if (method === "HEAD") {
|
|
137
|
+
res.end();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
createReadStream(file.abs).pipe(res);
|
|
141
|
+
})().catch((err) => {
|
|
142
|
+
if (next) {
|
|
143
|
+
next(err instanceof Error ? err : new Error(String(err)));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
res.statusCode = 500;
|
|
147
|
+
res.end();
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
export { serveStaticHandler, serveStaticNodeMiddleware };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { B as
|
|
1
|
+
import { B as ImmutableObject, G as SharedStatePatch, H as SharedState, K as createSharedState, L as Immutable, R as ImmutableArray, U as SharedStateEvents, V as ImmutableSet, W as SharedStateOptions, z as ImmutableMap } from "../devframe-DulcIxwu.mjs";
|
|
2
2
|
export { Immutable, ImmutableArray, ImmutableMap, ImmutableObject, ImmutableSet, SharedState, SharedStateEvents, SharedStateOptions, SharedStatePatch, createSharedState };
|
|
@@ -1,36 +1,2 @@
|
|
|
1
|
-
import {
|
|
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 { A as
|
|
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 };
|
package/dist/utils/when.d.mts
CHANGED
|
@@ -1,2 +1,384 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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$1<T, S extends string> = S & ValidateExpression<S, T>; //#endregion
|
|
329
|
+
//#region src/index.d.ts
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region src/utils/when.d.ts
|
|
332
|
+
/**
|
|
333
|
+
* Context object for evaluating `when` expressions.
|
|
334
|
+
*
|
|
335
|
+
* Built-in variables:
|
|
336
|
+
* - `clientType` — `'embedded' | 'standalone'`
|
|
337
|
+
* - `dockOpen` — whether the dock panel is open
|
|
338
|
+
* - `paletteOpen` — whether the command palette is open
|
|
339
|
+
* - `dockSelectedId` — ID of the selected dock entry (empty string if none)
|
|
340
|
+
*
|
|
341
|
+
* Plugins can add namespaced variables using dot or colon separators:
|
|
342
|
+
* - `vite.mode`, `vite:mode` — stored as `{ 'vite.mode': 'development' }` or nested `{ vite: { mode: 'development' } }`
|
|
343
|
+
*
|
|
344
|
+
* Supported operators: `!`, `==`, `!=`, `===`, `!==`, `<`, `<=`, `>`, `>=`,
|
|
345
|
+
* `&&`, `||`, `+`, `-`, `*`, `/`, `%`. Parentheses, number and string literals
|
|
346
|
+
* are also supported.
|
|
347
|
+
*/
|
|
348
|
+
interface WhenContext {
|
|
349
|
+
clientType: 'embedded' | 'standalone';
|
|
350
|
+
dockOpen: boolean;
|
|
351
|
+
paletteOpen: boolean;
|
|
352
|
+
dockSelectedId: string;
|
|
353
|
+
/** Allow custom context variables from plugins */
|
|
354
|
+
[key: string]: unknown;
|
|
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;
|
|
383
|
+
//#endregion
|
|
384
|
+
export { EvaluateWhenOptions, WhenContext, type WhenExpression, evaluateWhen, resolveContextValue };
|
package/dist/utils/when.mjs
CHANGED
|
@@ -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-
|
|
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";
|