devframe 0.0.0 → 0.1.16
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/LICENSE.md +21 -0
- package/README.md +62 -0
- package/dist/_shared-CU6dE-VX.mjs +20 -0
- package/dist/adapters/build.d.mts +37 -0
- package/dist/adapters/build.mjs +61 -0
- package/dist/adapters/cli.d.mts +35 -0
- package/dist/adapters/cli.mjs +357 -0
- package/dist/adapters/embedded.d.mts +19 -0
- package/dist/adapters/embedded.mjs +15 -0
- package/dist/adapters/kit.d.mts +23 -0
- package/dist/adapters/kit.mjs +16 -0
- package/dist/adapters/mcp.d.mts +39 -0
- package/dist/adapters/mcp.mjs +278 -0
- package/dist/adapters/vite.d.mts +32 -0
- package/dist/adapters/vite.mjs +31 -0
- package/dist/client/index.d.mts +227 -0
- package/dist/client/index.mjs +2 -0
- package/dist/client-4WrEozlH.mjs +1561 -0
- package/dist/constants.d.mts +20 -0
- package/dist/constants.mjs +34 -0
- package/dist/context-BrePWeyd.mjs +6776 -0
- package/dist/define-Bb4zh-Dc.mjs +11 -0
- package/dist/devtool-OJ3QW0ns.d.mts +1125 -0
- package/dist/helpers/nuxt/index.d.mts +46 -0
- package/dist/helpers/nuxt/index.mjs +58 -0
- package/dist/helpers/nuxt/runtime/plugin.client.d.mts +8 -0
- package/dist/helpers/nuxt/runtime/plugin.client.mjs +12 -0
- package/dist/human-id-CHS0s28X.mjs +844 -0
- package/dist/immer-HjMAm3b6.mjs +894 -0
- package/dist/index-DvKDO5H8.d.mts +333 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.mjs +2 -0
- package/dist/main-DpINGndA.mjs +601 -0
- package/dist/node/index.d.mts +365 -0
- package/dist/node/index.mjs +69 -0
- package/dist/open-BtOOEldu.mjs +533 -0
- package/dist/recipes/open-helpers.d.mts +104 -0
- package/dist/recipes/open-helpers.mjs +69 -0
- package/dist/rpc/client.d.mts +9 -0
- package/dist/rpc/client.mjs +13 -0
- package/dist/rpc/index.d.mts +2 -0
- package/dist/rpc/index.mjs +3 -0
- package/dist/rpc/server.d.mts +8 -0
- package/dist/rpc/server.mjs +10 -0
- package/dist/rpc/transports/ws-client.d.mts +2 -0
- package/dist/rpc/transports/ws-client.mjs +43 -0
- package/dist/rpc/transports/ws-server.d.mts +2 -0
- package/dist/rpc/transports/ws-server.mjs +58 -0
- package/dist/rpc-9FNa3Inb.mjs +474 -0
- package/dist/server-DrBxa6ZV.mjs +49 -0
- package/dist/src-BoIqXRc9.mjs +85 -0
- package/dist/static-dump-CQUC1aIW.mjs +82 -0
- package/dist/transports-BPUzHhI2.mjs +15 -0
- package/dist/types/index.d.mts +4 -0
- package/dist/types/index.mjs +6 -0
- package/dist/utils/events.d.mts +9 -0
- package/dist/utils/events.mjs +40 -0
- package/dist/utils/human-id.d.mts +10 -0
- package/dist/utils/human-id.mjs +3 -0
- package/dist/utils/nanoid.d.mts +4 -0
- package/dist/utils/nanoid.mjs +10 -0
- package/dist/utils/promise.d.mts +8 -0
- package/dist/utils/promise.mjs +15 -0
- package/dist/utils/shared-state.d.mts +2 -0
- package/dist/utils/shared-state.mjs +36 -0
- package/dist/utils/state.d.mts +49 -0
- package/dist/utils/state.mjs +26 -0
- package/dist/utils/when.d.mts +2 -0
- package/dist/utils/when.mjs +424 -0
- package/dist/when-CGLewRtm.d.mts +401 -0
- package/dist/ws-client-CklfxUHE.d.mts +17 -0
- package/dist/ws-server--IuUAaGi.d.mts +37 -0
- package/package.json +116 -8
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { hash } from "ohash";
|
|
2
|
+
import { consoleReporter, createLogger, defineDiagnostics, plainFormatter } from "logs-sdk";
|
|
3
|
+
//#region src/rpc/cache.ts
|
|
4
|
+
/**
|
|
5
|
+
* @experimental API is expected to change.
|
|
6
|
+
*/
|
|
7
|
+
var RpcCacheManager = class {
|
|
8
|
+
cacheMap = /* @__PURE__ */ new Map();
|
|
9
|
+
options;
|
|
10
|
+
keySerializer;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.options = options;
|
|
13
|
+
this.keySerializer = options.keySerializer || ((args) => hash(args));
|
|
14
|
+
}
|
|
15
|
+
updateOptions(options) {
|
|
16
|
+
this.options = {
|
|
17
|
+
...this.options,
|
|
18
|
+
...options
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
cached(m, a) {
|
|
22
|
+
const methodCache = this.cacheMap.get(m);
|
|
23
|
+
if (methodCache) return methodCache.get(this.keySerializer(a));
|
|
24
|
+
}
|
|
25
|
+
apply(req, res) {
|
|
26
|
+
const methodCache = this.cacheMap.get(req.m) || /* @__PURE__ */ new Map();
|
|
27
|
+
methodCache.set(this.keySerializer(req.a), res);
|
|
28
|
+
this.cacheMap.set(req.m, methodCache);
|
|
29
|
+
}
|
|
30
|
+
validate(m) {
|
|
31
|
+
return this.options.functions.includes(m);
|
|
32
|
+
}
|
|
33
|
+
clear(fn) {
|
|
34
|
+
if (fn) this.cacheMap.delete(fn);
|
|
35
|
+
else this.cacheMap.clear();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const logger = createLogger({
|
|
39
|
+
diagnostics: [defineDiagnostics({
|
|
40
|
+
docsBase: "https://devtools.vite.dev/errors",
|
|
41
|
+
codes: {
|
|
42
|
+
DTK0001: {
|
|
43
|
+
message: (p) => `RPC function "${p.name}" is already registered`,
|
|
44
|
+
hint: "Use the `force` parameter to overwrite an existing registration."
|
|
45
|
+
},
|
|
46
|
+
DTK0002: { message: (p) => `RPC function "${p.name}" is not registered. Use register() to add new functions.` },
|
|
47
|
+
DTK0003: { message: (p) => `RPC function "${p.name}" is not registered` },
|
|
48
|
+
DTK0004: { message: (p) => `Either handler or setup function must be provided for RPC function "${p.name}"` },
|
|
49
|
+
DTK0005: { message: (p) => `Function "${p.name}" not found in dump store` },
|
|
50
|
+
DTK0006: { message: (p) => `No dump match for "${p.name}" with args: ${p.args}` },
|
|
51
|
+
DTK0007: { message: (p) => `Function "${p.name}" with type "${p.type}" cannot have dump configuration. Only "static" and "query" types support dumps.` },
|
|
52
|
+
DTK0008: {
|
|
53
|
+
message: (p) => `Function "${p.name}" with type "${p.type}" cannot use \`snapshot: true\`. Only "query" functions support this sugar; "static" functions have equivalent default behavior already.`,
|
|
54
|
+
hint: "Remove `snapshot: true`, or change the function type to `query`."
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
})],
|
|
58
|
+
formatter: plainFormatter,
|
|
59
|
+
reporters: consoleReporter
|
|
60
|
+
});
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/rpc/handler.ts
|
|
63
|
+
async function getRpcResolvedSetupResult(definition, context) {
|
|
64
|
+
if (definition.__resolved) return definition.__resolved;
|
|
65
|
+
if (!definition.setup) return {};
|
|
66
|
+
definition.__promise ??= Promise.resolve(definition.setup(context)).then((r) => {
|
|
67
|
+
definition.__resolved = r;
|
|
68
|
+
definition.__promise = void 0;
|
|
69
|
+
return r;
|
|
70
|
+
});
|
|
71
|
+
return definition.__resolved ??= await definition.__promise;
|
|
72
|
+
}
|
|
73
|
+
async function getRpcHandler(definition, context) {
|
|
74
|
+
if (definition.handler) return definition.handler;
|
|
75
|
+
const result = await getRpcResolvedSetupResult(definition, context);
|
|
76
|
+
if (!result.handler) throw logger.DTK0004({ name: definition.name }).throw();
|
|
77
|
+
return result.handler;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/rpc/collector.ts
|
|
81
|
+
var RpcFunctionsCollectorBase = class {
|
|
82
|
+
definitions = /* @__PURE__ */ new Map();
|
|
83
|
+
functions;
|
|
84
|
+
_onChanged = [];
|
|
85
|
+
constructor(context) {
|
|
86
|
+
this.context = context;
|
|
87
|
+
const definitions = this.definitions;
|
|
88
|
+
const self = this;
|
|
89
|
+
this.functions = new Proxy({}, {
|
|
90
|
+
get(_, prop) {
|
|
91
|
+
const definition = definitions.get(prop);
|
|
92
|
+
if (!definition) return void 0;
|
|
93
|
+
return getRpcHandler(definition, self.context);
|
|
94
|
+
},
|
|
95
|
+
has(_, prop) {
|
|
96
|
+
return definitions.has(prop);
|
|
97
|
+
},
|
|
98
|
+
getOwnPropertyDescriptor(_, prop) {
|
|
99
|
+
return {
|
|
100
|
+
value: definitions.get(prop)?.handler,
|
|
101
|
+
configurable: true,
|
|
102
|
+
enumerable: true
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
ownKeys() {
|
|
106
|
+
return Array.from(definitions.keys());
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
register(fn, force = false) {
|
|
111
|
+
if (this.definitions.has(fn.name) && !force) throw logger.DTK0001({ name: fn.name }).throw();
|
|
112
|
+
this.definitions.set(fn.name, fn);
|
|
113
|
+
this._onChanged.forEach((cb) => cb(fn.name));
|
|
114
|
+
}
|
|
115
|
+
update(fn, force = false) {
|
|
116
|
+
if (!this.definitions.has(fn.name) && !force) throw logger.DTK0002({ name: fn.name }).throw();
|
|
117
|
+
this.definitions.set(fn.name, fn);
|
|
118
|
+
this._onChanged.forEach((cb) => cb(fn.name));
|
|
119
|
+
}
|
|
120
|
+
onChanged(fn) {
|
|
121
|
+
this._onChanged.push(fn);
|
|
122
|
+
return () => {
|
|
123
|
+
const index = this._onChanged.indexOf(fn);
|
|
124
|
+
if (index !== -1) this._onChanged.splice(index, 1);
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async getHandler(name) {
|
|
128
|
+
return await getRpcHandler(this.definitions.get(name), this.context);
|
|
129
|
+
}
|
|
130
|
+
getSchema(name) {
|
|
131
|
+
const definition = this.definitions.get(name);
|
|
132
|
+
if (!definition) throw logger.DTK0003({ name: String(name) }).throw();
|
|
133
|
+
return {
|
|
134
|
+
args: definition.args,
|
|
135
|
+
returns: definition.returns
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
has(name) {
|
|
139
|
+
return this.definitions.has(name);
|
|
140
|
+
}
|
|
141
|
+
get(name) {
|
|
142
|
+
return this.definitions.get(name);
|
|
143
|
+
}
|
|
144
|
+
list() {
|
|
145
|
+
return Array.from(this.definitions.keys());
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region ../../../node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
|
|
150
|
+
var Node = class {
|
|
151
|
+
value;
|
|
152
|
+
next;
|
|
153
|
+
constructor(value) {
|
|
154
|
+
this.value = value;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var Queue = class {
|
|
158
|
+
#head;
|
|
159
|
+
#tail;
|
|
160
|
+
#size;
|
|
161
|
+
constructor() {
|
|
162
|
+
this.clear();
|
|
163
|
+
}
|
|
164
|
+
enqueue(value) {
|
|
165
|
+
const node = new Node(value);
|
|
166
|
+
if (this.#head) {
|
|
167
|
+
this.#tail.next = node;
|
|
168
|
+
this.#tail = node;
|
|
169
|
+
} else {
|
|
170
|
+
this.#head = node;
|
|
171
|
+
this.#tail = node;
|
|
172
|
+
}
|
|
173
|
+
this.#size++;
|
|
174
|
+
}
|
|
175
|
+
dequeue() {
|
|
176
|
+
const current = this.#head;
|
|
177
|
+
if (!current) return;
|
|
178
|
+
this.#head = this.#head.next;
|
|
179
|
+
this.#size--;
|
|
180
|
+
if (!this.#head) this.#tail = void 0;
|
|
181
|
+
return current.value;
|
|
182
|
+
}
|
|
183
|
+
peek() {
|
|
184
|
+
if (!this.#head) return;
|
|
185
|
+
return this.#head.value;
|
|
186
|
+
}
|
|
187
|
+
clear() {
|
|
188
|
+
this.#head = void 0;
|
|
189
|
+
this.#tail = void 0;
|
|
190
|
+
this.#size = 0;
|
|
191
|
+
}
|
|
192
|
+
get size() {
|
|
193
|
+
return this.#size;
|
|
194
|
+
}
|
|
195
|
+
*[Symbol.iterator]() {
|
|
196
|
+
let current = this.#head;
|
|
197
|
+
while (current) {
|
|
198
|
+
yield current.value;
|
|
199
|
+
current = current.next;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
*drain() {
|
|
203
|
+
while (this.#head) yield this.dequeue();
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region ../../../node_modules/.pnpm/p-limit@7.3.0/node_modules/p-limit/index.js
|
|
208
|
+
function pLimit(concurrency) {
|
|
209
|
+
let rejectOnClear = false;
|
|
210
|
+
if (typeof concurrency === "object") ({concurrency, rejectOnClear = false} = concurrency);
|
|
211
|
+
validateConcurrency(concurrency);
|
|
212
|
+
if (typeof rejectOnClear !== "boolean") throw new TypeError("Expected `rejectOnClear` to be a boolean");
|
|
213
|
+
const queue = new Queue();
|
|
214
|
+
let activeCount = 0;
|
|
215
|
+
const resumeNext = () => {
|
|
216
|
+
if (activeCount < concurrency && queue.size > 0) {
|
|
217
|
+
activeCount++;
|
|
218
|
+
queue.dequeue().run();
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
const next = () => {
|
|
222
|
+
activeCount--;
|
|
223
|
+
resumeNext();
|
|
224
|
+
};
|
|
225
|
+
const run = async (function_, resolve, arguments_) => {
|
|
226
|
+
const result = (async () => function_(...arguments_))();
|
|
227
|
+
resolve(result);
|
|
228
|
+
try {
|
|
229
|
+
await result;
|
|
230
|
+
} catch {}
|
|
231
|
+
next();
|
|
232
|
+
};
|
|
233
|
+
const enqueue = (function_, resolve, reject, arguments_) => {
|
|
234
|
+
const queueItem = { reject };
|
|
235
|
+
new Promise((internalResolve) => {
|
|
236
|
+
queueItem.run = internalResolve;
|
|
237
|
+
queue.enqueue(queueItem);
|
|
238
|
+
}).then(run.bind(void 0, function_, resolve, arguments_));
|
|
239
|
+
if (activeCount < concurrency) resumeNext();
|
|
240
|
+
};
|
|
241
|
+
const generator = (function_, ...arguments_) => new Promise((resolve, reject) => {
|
|
242
|
+
enqueue(function_, resolve, reject, arguments_);
|
|
243
|
+
});
|
|
244
|
+
Object.defineProperties(generator, {
|
|
245
|
+
activeCount: { get: () => activeCount },
|
|
246
|
+
pendingCount: { get: () => queue.size },
|
|
247
|
+
clearQueue: { value() {
|
|
248
|
+
if (!rejectOnClear) {
|
|
249
|
+
queue.clear();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const abortError = AbortSignal.abort().reason;
|
|
253
|
+
while (queue.size > 0) queue.dequeue().reject(abortError);
|
|
254
|
+
} },
|
|
255
|
+
concurrency: {
|
|
256
|
+
get: () => concurrency,
|
|
257
|
+
set(newConcurrency) {
|
|
258
|
+
validateConcurrency(newConcurrency);
|
|
259
|
+
concurrency = newConcurrency;
|
|
260
|
+
queueMicrotask(() => {
|
|
261
|
+
while (activeCount < concurrency && queue.size > 0) resumeNext();
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
map: { async value(iterable, function_) {
|
|
266
|
+
const promises = Array.from(iterable, (value, index) => this(function_, value, index));
|
|
267
|
+
return Promise.all(promises);
|
|
268
|
+
} }
|
|
269
|
+
});
|
|
270
|
+
return generator;
|
|
271
|
+
}
|
|
272
|
+
function validateConcurrency(concurrency) {
|
|
273
|
+
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/rpc/validation.ts
|
|
277
|
+
/**
|
|
278
|
+
* Validates RPC function definitions.
|
|
279
|
+
* Action and event functions cannot have dumps (side effects should not be cached).
|
|
280
|
+
*
|
|
281
|
+
* @throws {Error} If an action or event function has a dump configuration
|
|
282
|
+
*/
|
|
283
|
+
function validateDefinitions(definitions) {
|
|
284
|
+
for (const definition of definitions) {
|
|
285
|
+
const type = definition.type || "query";
|
|
286
|
+
if ((type === "action" || type === "event") && definition.dump) throw logger.DTK0007({
|
|
287
|
+
name: definition.name,
|
|
288
|
+
type
|
|
289
|
+
}).throw();
|
|
290
|
+
if (definition.snapshot && type !== "query") throw logger.DTK0008({
|
|
291
|
+
name: definition.name,
|
|
292
|
+
type
|
|
293
|
+
}).throw();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Validates a single RPC function definition.
|
|
298
|
+
*
|
|
299
|
+
* @throws {Error} If an action or event function has a dump configuration
|
|
300
|
+
*/
|
|
301
|
+
function validateDefinition(definition) {
|
|
302
|
+
validateDefinitions([definition]);
|
|
303
|
+
}
|
|
304
|
+
//#endregion
|
|
305
|
+
//#region src/rpc/dumps.ts
|
|
306
|
+
function getDumpRecordKey(functionName, args) {
|
|
307
|
+
return `${functionName}---${hash(args)}`;
|
|
308
|
+
}
|
|
309
|
+
function getDumpFallbackKey(functionName) {
|
|
310
|
+
return `${functionName}---fallback`;
|
|
311
|
+
}
|
|
312
|
+
async function resolveGetter(valueOrGetter) {
|
|
313
|
+
return typeof valueOrGetter === "function" ? await valueOrGetter() : valueOrGetter;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Collects pre-computed dumps by executing functions with their defined input combinations.
|
|
317
|
+
* Static functions without dump config automatically get `{ inputs: [[]] }`.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```ts
|
|
321
|
+
* const store = await dumpFunctions([greet], context, { concurrency: 10 })
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
async function dumpFunctions(definitions, context, options = {}) {
|
|
325
|
+
validateDefinitions(definitions);
|
|
326
|
+
const concurrency = options.concurrency === true ? 5 : options.concurrency === false || options.concurrency == null ? 1 : options.concurrency;
|
|
327
|
+
const store = {
|
|
328
|
+
definitions: {},
|
|
329
|
+
records: {}
|
|
330
|
+
};
|
|
331
|
+
const tasksResolutions = definitions.map((definition) => async () => {
|
|
332
|
+
if (definition.type === "event" || definition.type === "action") return;
|
|
333
|
+
const setupResult = definition.setup ? await Promise.resolve(definition.setup(context)) : {};
|
|
334
|
+
const handler = setupResult.handler || definition.handler;
|
|
335
|
+
if (!handler) throw logger.DTK0004({ name: definition.name }).throw();
|
|
336
|
+
let dump = setupResult.dump ?? definition.dump;
|
|
337
|
+
if (!dump && definition.type === "static") dump = { inputs: [[]] };
|
|
338
|
+
if (!dump && definition.snapshot) dump = async (_ctx, h) => {
|
|
339
|
+
const output = await Promise.resolve(h(...[]));
|
|
340
|
+
return {
|
|
341
|
+
records: [{
|
|
342
|
+
inputs: [],
|
|
343
|
+
output
|
|
344
|
+
}],
|
|
345
|
+
fallback: output
|
|
346
|
+
};
|
|
347
|
+
};
|
|
348
|
+
if (!dump) return;
|
|
349
|
+
if (typeof dump === "function") dump = await Promise.resolve(dump(context, handler));
|
|
350
|
+
store.definitions[definition.name] = {
|
|
351
|
+
name: definition.name,
|
|
352
|
+
type: definition.type
|
|
353
|
+
};
|
|
354
|
+
return {
|
|
355
|
+
handler,
|
|
356
|
+
dump,
|
|
357
|
+
definition
|
|
358
|
+
};
|
|
359
|
+
});
|
|
360
|
+
let functionsToDump = [];
|
|
361
|
+
if (concurrency <= 1) for (const task of tasksResolutions) {
|
|
362
|
+
const resolution = await task();
|
|
363
|
+
if (resolution) functionsToDump.push(resolution);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
const limit = pLimit(concurrency);
|
|
367
|
+
functionsToDump = (await Promise.all(tasksResolutions.map((task) => limit(task)))).filter((x) => !!x);
|
|
368
|
+
}
|
|
369
|
+
const dumpTasks = [];
|
|
370
|
+
for (const { definition, handler, dump } of functionsToDump) {
|
|
371
|
+
const { inputs, records, fallback } = dump;
|
|
372
|
+
if (records) for (const record of records) {
|
|
373
|
+
const recordKey = getDumpRecordKey(definition.name, record.inputs);
|
|
374
|
+
store.records[recordKey] = record;
|
|
375
|
+
}
|
|
376
|
+
if ("fallback" in dump) {
|
|
377
|
+
const fallbackKey = getDumpFallbackKey(definition.name);
|
|
378
|
+
store.records[fallbackKey] = {
|
|
379
|
+
inputs: [],
|
|
380
|
+
output: fallback
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
if (inputs) for (const input of inputs) dumpTasks.push(async () => {
|
|
384
|
+
const recordKey = getDumpRecordKey(definition.name, input);
|
|
385
|
+
try {
|
|
386
|
+
const output = await Promise.resolve(handler(...input));
|
|
387
|
+
store.records[recordKey] = {
|
|
388
|
+
inputs: input,
|
|
389
|
+
output
|
|
390
|
+
};
|
|
391
|
+
} catch (error) {
|
|
392
|
+
store.records[recordKey] = {
|
|
393
|
+
inputs: input,
|
|
394
|
+
error: {
|
|
395
|
+
message: error.message,
|
|
396
|
+
name: error.name
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
if (concurrency <= 1) for (const task of dumpTasks) await task();
|
|
403
|
+
else {
|
|
404
|
+
const limit = pLimit(concurrency);
|
|
405
|
+
await Promise.all(dumpTasks.map((task) => limit(task)));
|
|
406
|
+
}
|
|
407
|
+
return store;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Creates a client that serves pre-computed results from a dump store.
|
|
411
|
+
* Uses argument hashing to match calls to stored records.
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* ```ts
|
|
415
|
+
* const client = createClientFromDump(store)
|
|
416
|
+
* await client.greet('Alice')
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
function createClientFromDump(store, options = {}) {
|
|
420
|
+
const { onMiss } = options;
|
|
421
|
+
return new Proxy({}, {
|
|
422
|
+
get(_, functionName) {
|
|
423
|
+
if (!(functionName in store.definitions)) throw logger.DTK0005({ name: functionName }).throw();
|
|
424
|
+
return async (...args) => {
|
|
425
|
+
const recordKey = getDumpRecordKey(functionName, args);
|
|
426
|
+
const recordOrGetter = store.records[recordKey];
|
|
427
|
+
if (recordOrGetter) {
|
|
428
|
+
const record = await resolveGetter(recordOrGetter);
|
|
429
|
+
if (record.error) {
|
|
430
|
+
const error = new Error(record.error.message);
|
|
431
|
+
error.name = record.error.name;
|
|
432
|
+
throw error;
|
|
433
|
+
}
|
|
434
|
+
if (typeof record.output === "function") return await record.output();
|
|
435
|
+
return record.output;
|
|
436
|
+
}
|
|
437
|
+
onMiss?.(functionName, args);
|
|
438
|
+
const fallbackKey = getDumpFallbackKey(functionName);
|
|
439
|
+
if (fallbackKey in store.records) {
|
|
440
|
+
const fallbackOrGetter = store.records[fallbackKey];
|
|
441
|
+
const fallbackRecord = await resolveGetter(fallbackOrGetter);
|
|
442
|
+
if (fallbackRecord && typeof fallbackRecord.output === "function") return await fallbackRecord.output();
|
|
443
|
+
if (fallbackRecord) return fallbackRecord.output;
|
|
444
|
+
}
|
|
445
|
+
throw logger.DTK0006({
|
|
446
|
+
name: functionName,
|
|
447
|
+
args: JSON.stringify(args)
|
|
448
|
+
}).throw();
|
|
449
|
+
};
|
|
450
|
+
},
|
|
451
|
+
has(_, functionName) {
|
|
452
|
+
return functionName in store.definitions;
|
|
453
|
+
},
|
|
454
|
+
ownKeys() {
|
|
455
|
+
return Object.keys(store.definitions);
|
|
456
|
+
},
|
|
457
|
+
getOwnPropertyDescriptor(_, functionName) {
|
|
458
|
+
return functionName in store.definitions ? {
|
|
459
|
+
configurable: true,
|
|
460
|
+
enumerable: true,
|
|
461
|
+
value: void 0
|
|
462
|
+
} : void 0;
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Filters function definitions to only those with dump definitions.
|
|
468
|
+
* Note: Only checks the definition itself, not setup results.
|
|
469
|
+
*/
|
|
470
|
+
function getDefinitionsWithDumps(definitions) {
|
|
471
|
+
return definitions.filter((def) => def.dump !== void 0);
|
|
472
|
+
}
|
|
473
|
+
//#endregion
|
|
474
|
+
export { validateDefinitions as a, getRpcResolvedSetupResult as c, validateDefinition as i, RpcCacheManager as l, dumpFunctions as n, RpcFunctionsCollectorBase as o, getDefinitionsWithDumps as r, getRpcHandler as s, createClientFromDump as t };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createRpcServer } from "./rpc/server.mjs";
|
|
2
|
+
import { attachWsRpcTransport } from "./rpc/transports/ws-server.mjs";
|
|
3
|
+
import { WebSocketServer } from "ws";
|
|
4
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
+
import { createServer } from "node:http";
|
|
6
|
+
import { createApp, toNodeListener } from "h3";
|
|
7
|
+
//#region src/node/server.ts
|
|
8
|
+
/**
|
|
9
|
+
* Compose an h3 + WebSocket server for a devtool context. The RPC
|
|
10
|
+
* group is bound to `context.rpc.functions`; the WS endpoint lives on
|
|
11
|
+
* the same port as the HTTP server.
|
|
12
|
+
*/
|
|
13
|
+
async function startHttpAndWs(options) {
|
|
14
|
+
const { context, port } = options;
|
|
15
|
+
const bindHost = options.host ?? "localhost";
|
|
16
|
+
const app = options.app ?? createApp();
|
|
17
|
+
const httpServer = createServer(toNodeListener(app));
|
|
18
|
+
const wss = new WebSocketServer({ server: httpServer });
|
|
19
|
+
const rpcHost = context.rpc;
|
|
20
|
+
const asyncStorage = new AsyncLocalStorage();
|
|
21
|
+
const rpcGroup = createRpcServer(rpcHost.functions);
|
|
22
|
+
attachWsRpcTransport(rpcGroup, { wss });
|
|
23
|
+
rpcHost._rpcGroup = rpcGroup;
|
|
24
|
+
rpcHost._asyncStorage = asyncStorage;
|
|
25
|
+
rpcHost._authDisabled = options.auth === false;
|
|
26
|
+
await new Promise((resolveListen) => {
|
|
27
|
+
httpServer.listen(port, bindHost, () => resolveListen());
|
|
28
|
+
});
|
|
29
|
+
const origin = `http://${bindHost}:${port}`;
|
|
30
|
+
if (options.onReady) await options.onReady({
|
|
31
|
+
origin,
|
|
32
|
+
port,
|
|
33
|
+
app
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
origin,
|
|
37
|
+
port,
|
|
38
|
+
app,
|
|
39
|
+
wss,
|
|
40
|
+
rpcGroup,
|
|
41
|
+
async close() {
|
|
42
|
+
for (const ws of wss.clients) ws.terminate();
|
|
43
|
+
await new Promise((r) => wss.close(() => r()));
|
|
44
|
+
await new Promise((r) => httpServer.close(() => r()));
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { startHttpAndWs as t };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { t as createDefineWrapperWithContext } from "./define-Bb4zh-Dc.mjs";
|
|
2
|
+
import { safeParse } from "valibot";
|
|
3
|
+
//#region src/adapters/flags.ts
|
|
4
|
+
/**
|
|
5
|
+
* Identity helper that preserves the literal schema-map type — use this
|
|
6
|
+
* so `InferCliFlags<typeof myFlags>` resolves to the right object shape.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const appFlags = defineCliFlags({
|
|
10
|
+
* depth: v.pipe(v.number(), v.integer()),
|
|
11
|
+
* config: v.optional(v.string()),
|
|
12
|
+
* })
|
|
13
|
+
*
|
|
14
|
+
* defineDevtool({
|
|
15
|
+
* cli: { flags: appFlags },
|
|
16
|
+
* setup(ctx, info) {
|
|
17
|
+
* const flags = info.flags as InferCliFlags<typeof appFlags>
|
|
18
|
+
* flags.depth // number
|
|
19
|
+
* flags.config // string | undefined
|
|
20
|
+
* },
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
function defineCliFlags(flags) {
|
|
25
|
+
return flags;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Best-effort probe of a valibot schema to decide whether the
|
|
29
|
+
* corresponding CAC option takes a value. Unwraps `optional` / `nullable`
|
|
30
|
+
* / `nullish` / `default` / `pipe` wrappers then matches on the inner
|
|
31
|
+
* type's kind.
|
|
32
|
+
*/
|
|
33
|
+
function getSchemaKind(schema) {
|
|
34
|
+
let current = schema;
|
|
35
|
+
while (current) {
|
|
36
|
+
const kind = current.type;
|
|
37
|
+
if (kind === "optional" || kind === "nullable" || kind === "nullish" || kind === "undefined") {
|
|
38
|
+
current = current.wrapped ?? current.inner;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (kind === "pipe" && Array.isArray(current.pipe) && current.pipe.length > 0) {
|
|
42
|
+
current = current.pipe[0];
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
return kind;
|
|
46
|
+
}
|
|
47
|
+
return "unknown";
|
|
48
|
+
}
|
|
49
|
+
/** Whether the CAC option for this schema should be a boolean flag. */
|
|
50
|
+
function isBooleanFlag(schema) {
|
|
51
|
+
return getSchemaKind(schema) === "boolean";
|
|
52
|
+
}
|
|
53
|
+
/** Validate and coerce the raw cac-parsed bag against a {@link CliFlagsSchema}. */
|
|
54
|
+
function parseCliFlags(schema, raw) {
|
|
55
|
+
const flags = {};
|
|
56
|
+
const issues = [];
|
|
57
|
+
for (const [key, fieldSchema] of Object.entries(schema)) {
|
|
58
|
+
const result = safeParse(fieldSchema, raw[key]);
|
|
59
|
+
if (result.success) flags[key] = result.output;
|
|
60
|
+
else issues.push(`--${toKebab(key)}: ${result.issues.map((i) => i.message).join(", ")}`);
|
|
61
|
+
}
|
|
62
|
+
for (const [key, value] of Object.entries(raw)) if (!(key in schema) && !(key in flags)) flags[key] = value;
|
|
63
|
+
return issues.length ? {
|
|
64
|
+
flags,
|
|
65
|
+
issues
|
|
66
|
+
} : { flags };
|
|
67
|
+
}
|
|
68
|
+
function toKebab(camel) {
|
|
69
|
+
return camel.replaceAll(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
70
|
+
}
|
|
71
|
+
/** Kebab-case a schema key for CAC option registration. */
|
|
72
|
+
function flagKeyToOption(camel) {
|
|
73
|
+
return toKebab(camel);
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/define.ts
|
|
77
|
+
const defineRpcFunction = createDefineWrapperWithContext();
|
|
78
|
+
function defineCommand(command) {
|
|
79
|
+
return command;
|
|
80
|
+
}
|
|
81
|
+
function defineJsonRenderSpec(spec) {
|
|
82
|
+
return spec;
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
export { flagKeyToOption as a, defineCliFlags as i, defineJsonRenderSpec as n, isBooleanFlag as o, defineRpcFunction as r, parseCliFlags as s, defineCommand as t };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { n as dumpFunctions, s as getRpcHandler } from "./rpc-9FNa3Inb.mjs";
|
|
2
|
+
import { DEVTOOLS_RPC_DUMP_DIRNAME } from "./constants.mjs";
|
|
3
|
+
//#region src/node/host-h3.ts
|
|
4
|
+
/**
|
|
5
|
+
* h3-backed {@link DevToolsHost} — used by the standalone CLI adapter.
|
|
6
|
+
* This commit adds the shell; the CLI adapter in commit 5 wires it up
|
|
7
|
+
* with a real h3 app and sirv handler.
|
|
8
|
+
*/
|
|
9
|
+
function createH3DevToolsHost(options) {
|
|
10
|
+
return {
|
|
11
|
+
mountStatic(base, distDir) {
|
|
12
|
+
return options.mount?.(base, distDir);
|
|
13
|
+
},
|
|
14
|
+
resolveOrigin() {
|
|
15
|
+
return options.origin;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/node/static-dump.ts
|
|
21
|
+
function makeDumpKey(name) {
|
|
22
|
+
return encodeURIComponent(name.replaceAll(":", "~"));
|
|
23
|
+
}
|
|
24
|
+
function makeStaticPath(name) {
|
|
25
|
+
return `${DEVTOOLS_RPC_DUMP_DIRNAME}/${makeDumpKey(name)}.static.json`;
|
|
26
|
+
}
|
|
27
|
+
function makeQueryRecordPath(name, hash) {
|
|
28
|
+
return `${DEVTOOLS_RPC_DUMP_DIRNAME}/${makeDumpKey(name)}.record.${hash}.json`;
|
|
29
|
+
}
|
|
30
|
+
function makeQueryFallbackPath(name) {
|
|
31
|
+
return `${DEVTOOLS_RPC_DUMP_DIRNAME}/${makeDumpKey(name)}.fallback.json`;
|
|
32
|
+
}
|
|
33
|
+
async function resolveRecord(record) {
|
|
34
|
+
return typeof record === "function" ? await record() : record;
|
|
35
|
+
}
|
|
36
|
+
async function collectStaticRpcDump(definitions, context) {
|
|
37
|
+
const manifest = {};
|
|
38
|
+
const files = {};
|
|
39
|
+
for (const definition of definitions) {
|
|
40
|
+
const type = definition.type ?? "query";
|
|
41
|
+
if (type === "static") {
|
|
42
|
+
const handler = await getRpcHandler(definition, context);
|
|
43
|
+
const path = makeStaticPath(definition.name);
|
|
44
|
+
files[path] = { output: await Promise.resolve(handler()) };
|
|
45
|
+
manifest[definition.name] = {
|
|
46
|
+
type: "static",
|
|
47
|
+
path
|
|
48
|
+
};
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (type !== "query") continue;
|
|
52
|
+
const store = await dumpFunctions([definition], context);
|
|
53
|
+
if (!(definition.name in store.definitions)) continue;
|
|
54
|
+
const queryEntry = {
|
|
55
|
+
type: "query",
|
|
56
|
+
records: {}
|
|
57
|
+
};
|
|
58
|
+
const prefix = `${definition.name}---`;
|
|
59
|
+
for (const [recordKey, recordOrGetter] of Object.entries(store.records)) {
|
|
60
|
+
if (!recordKey.startsWith(prefix)) continue;
|
|
61
|
+
const key = recordKey.slice(prefix.length);
|
|
62
|
+
const record = await resolveRecord(recordOrGetter);
|
|
63
|
+
if (key === "fallback") {
|
|
64
|
+
const path = makeQueryFallbackPath(definition.name);
|
|
65
|
+
files[path] = record;
|
|
66
|
+
queryEntry.fallback = path;
|
|
67
|
+
} else {
|
|
68
|
+
const path = makeQueryRecordPath(definition.name, key);
|
|
69
|
+
files[path] = record;
|
|
70
|
+
queryEntry.records[key] = path;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!Object.keys(queryEntry.records).length && !queryEntry.fallback) continue;
|
|
74
|
+
manifest[definition.name] = queryEntry;
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
manifest,
|
|
78
|
+
files
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { createH3DevToolsHost as n, collectStaticRpcDump as t };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
//#region src/node/mcp/transports.ts
|
|
3
|
+
/**
|
|
4
|
+
* Start the MCP server on stdio. Returns a stop function.
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
async function startStdioTransport(server) {
|
|
8
|
+
const transport = new StdioServerTransport();
|
|
9
|
+
await server.connect(transport);
|
|
10
|
+
return async () => {
|
|
11
|
+
await server.close();
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { startStdioTransport };
|