@weapp-sqlite/devtools 0.0.0-stage → 0.1.0
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 +21 -0
- package/README.md +42 -2
- package/dist/client/assets/browser-agent-rpc-BXhoSh1z-B8VY0LVK.js +1 -0
- package/dist/client/assets/index-C4MpIW3V.css +1 -0
- package/dist/client/assets/index-qEaVRY93.js +6 -0
- package/dist/client/index.html +13 -0
- package/dist/client.cjs +76 -0
- package/dist/client.d.cts +14 -0
- package/dist/client.d.mts +14 -0
- package/dist/client.mjs +75 -0
- package/dist/index.cjs +451 -0
- package/dist/index.d.cts +64 -0
- package/dist/index.d.mts +64 -0
- package/dist/index.mjs +423 -0
- package/dist/protocol-BhfjLrSe.d.cts +76 -0
- package/dist/protocol-BhfjLrSe.d.mts +76 -0
- package/dist/protocol-Cl3y675u.cjs +299 -0
- package/dist/protocol-nkWp0_XO.mjs +216 -0
- package/dist/protocol.cjs +16 -0
- package/dist/protocol.d.cts +2 -0
- package/dist/protocol.d.mts +2 -0
- package/dist/protocol.mjs +2 -0
- package/dist/runtime.cjs +397 -0
- package/dist/runtime.d.cts +68 -0
- package/dist/runtime.d.mts +68 -0
- package/dist/runtime.mjs +394 -0
- package/package.json +90 -3
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import { c as isRecord, d as serializeDevtoolsError, f as validDatabaseNames, i as SQLITE_DEVTOOLS_SCOPE, l as isSqliteDevtoolsMethod, m as encodeSqliteDevtoolsValue, o as SqliteDevtoolsError, p as decodeSqliteDevtoolsValue, s as isIdentifier, t as SQLITE_DEVTOOLS_MAX_MESSAGE_BYTES, u as isSqliteDevtoolsWriteMethod } from "./protocol-nkWp0_XO.mjs";
|
|
2
|
+
import { connectDevtoolsClient } from "./client.mjs";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { DevTools } from "@vitejs/devtools";
|
|
8
|
+
import { createPluginFromDevframe } from "@vitejs/devtools-kit/node";
|
|
9
|
+
import { defineDevframe, defineRpcFunction } from "devframe";
|
|
10
|
+
import { buildOtpAuthUrl, refreshTempAuthCode } from "devframe/node/auth";
|
|
11
|
+
import { createServer } from "vite";
|
|
12
|
+
import { Buffer } from "node:buffer";
|
|
13
|
+
import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
14
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
15
|
+
//#region src/broker.ts
|
|
16
|
+
const RUNTIME_PATH = "/__weapp-sqlite/runtime";
|
|
17
|
+
function errorResult(code, message) {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
error: {
|
|
21
|
+
code,
|
|
22
|
+
message
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function validOrigin(origin) {
|
|
27
|
+
if (!origin) return true;
|
|
28
|
+
try {
|
|
29
|
+
const url = new URL(origin);
|
|
30
|
+
return [
|
|
31
|
+
"localhost",
|
|
32
|
+
"127.0.0.1",
|
|
33
|
+
"[::1]"
|
|
34
|
+
].includes(url.hostname) || url.protocol === "https:" && url.hostname === "servicewechat.com";
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function validOwnerId(value) {
|
|
40
|
+
return value === void 0 || isIdentifier(value);
|
|
41
|
+
}
|
|
42
|
+
/** broker 只路由到拥有数据库的 runtime,不会在 Node 端打开数据库副本。 */
|
|
43
|
+
function createSqliteDevtoolsBroker(options = {}) {
|
|
44
|
+
const token = randomBytes(32).toString("hex");
|
|
45
|
+
const peers = /* @__PURE__ */ new Map();
|
|
46
|
+
const owners = /* @__PURE__ */ new Map();
|
|
47
|
+
const sockets = /* @__PURE__ */ new Set();
|
|
48
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
49
|
+
const server = new WebSocketServer({
|
|
50
|
+
noServer: true,
|
|
51
|
+
maxPayload: SQLITE_DEVTOOLS_MAX_MESSAGE_BYTES,
|
|
52
|
+
perMessageDeflate: false
|
|
53
|
+
});
|
|
54
|
+
let detach;
|
|
55
|
+
let closed = false;
|
|
56
|
+
function changed() {
|
|
57
|
+
for (const listener of listeners) listener();
|
|
58
|
+
}
|
|
59
|
+
function remove(peer) {
|
|
60
|
+
for (const pending of peer.pending.values()) {
|
|
61
|
+
clearTimeout(pending.timer);
|
|
62
|
+
pending.resolve(errorResult("SQLITE_DEVTOOLS_DISCONNECTED", "Runtime disconnected. The operation was not retried; its outcome may be unknown."));
|
|
63
|
+
}
|
|
64
|
+
peer.pending.clear();
|
|
65
|
+
if (peers.get(peer.descriptor.id) === peer) {
|
|
66
|
+
peers.delete(peer.descriptor.id);
|
|
67
|
+
owners.delete(peer.descriptor.id);
|
|
68
|
+
changed();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
server.on("connection", (socket) => {
|
|
72
|
+
if (closed || sockets.size >= 32) {
|
|
73
|
+
socket.close(1008, "Runtime capacity reached");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
sockets.add(socket);
|
|
77
|
+
let peer;
|
|
78
|
+
const authTimer = setTimeout(() => socket.close(1008, "Authentication timed out"), 5e3);
|
|
79
|
+
socket.on("error", () => {
|
|
80
|
+
socket.terminate();
|
|
81
|
+
});
|
|
82
|
+
socket.on("close", () => {
|
|
83
|
+
clearTimeout(authTimer);
|
|
84
|
+
sockets.delete(socket);
|
|
85
|
+
if (peer) remove(peer);
|
|
86
|
+
});
|
|
87
|
+
socket.on("message", (bytes, binary) => {
|
|
88
|
+
try {
|
|
89
|
+
const frame = Array.isArray(bytes) ? Buffer.concat(bytes) : bytes instanceof ArrayBuffer ? Buffer.from(bytes) : bytes;
|
|
90
|
+
if (binary || !peer && frame.byteLength > 32768) throw new Error("Invalid frame");
|
|
91
|
+
const message = JSON.parse(frame.toString());
|
|
92
|
+
if (!isRecord(message)) throw new Error("Invalid frame");
|
|
93
|
+
if (!peer) {
|
|
94
|
+
if (message.type !== "hello" || message.protocol !== 1 || typeof message.token !== "string" || message.token.length !== token.length || !timingSafeEqual(Buffer.from(message.token), Buffer.from(token)) || !isRecord(message.runtime) || !isIdentifier(message.runtime.id) || !isIdentifier(message.runtime.label) || !isIdentifier(message.runtime.platform) || !validDatabaseNames(message.databases)) throw new Error("Invalid authentication");
|
|
95
|
+
const old = peers.get(message.runtime.id);
|
|
96
|
+
if (old) {
|
|
97
|
+
remove(old);
|
|
98
|
+
old.socket.close(1e3, "Runtime replaced");
|
|
99
|
+
}
|
|
100
|
+
peer = {
|
|
101
|
+
socket,
|
|
102
|
+
descriptor: {
|
|
103
|
+
id: message.runtime.id,
|
|
104
|
+
label: message.runtime.label,
|
|
105
|
+
platform: message.runtime.platform,
|
|
106
|
+
databases: [...message.databases],
|
|
107
|
+
sessionId: randomUUID(),
|
|
108
|
+
connectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
109
|
+
readOnly: options.allowWrite !== true
|
|
110
|
+
},
|
|
111
|
+
pending: /* @__PURE__ */ new Map()
|
|
112
|
+
};
|
|
113
|
+
clearTimeout(authTimer);
|
|
114
|
+
peers.set(peer.descriptor.id, peer);
|
|
115
|
+
socket.send(JSON.stringify({
|
|
116
|
+
type: "ready",
|
|
117
|
+
sessionId: peer.descriptor.sessionId,
|
|
118
|
+
allowWrite: options.allowWrite === true
|
|
119
|
+
}));
|
|
120
|
+
changed();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (peers.get(peer.descriptor.id) !== peer || message.sessionId !== peer.descriptor.sessionId) throw new Error("Stale runtime session");
|
|
124
|
+
if (message.type === "databases" && validDatabaseNames(message.databases)) {
|
|
125
|
+
peer = {
|
|
126
|
+
...peer,
|
|
127
|
+
descriptor: {
|
|
128
|
+
...peer.descriptor,
|
|
129
|
+
databases: [...message.databases]
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
peers.set(peer.descriptor.id, peer);
|
|
133
|
+
changed();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (message.type !== "result" && message.type !== "release-result" || !isIdentifier(message.requestId) || !isRecord(message.result)) throw new Error("Invalid runtime response");
|
|
137
|
+
const pending = peer.pending.get(message.requestId);
|
|
138
|
+
if (!pending) return;
|
|
139
|
+
const result = message.result;
|
|
140
|
+
if (result.ok === true) decodeSqliteDevtoolsValue(result.value);
|
|
141
|
+
else if (result.ok !== false || !isRecord(result.error) || !isIdentifier(result.error.code) || typeof result.error.message !== "string" || result.error.message.length > 4096) throw new Error("Invalid runtime response");
|
|
142
|
+
clearTimeout(pending.timer);
|
|
143
|
+
peer.pending.delete(message.requestId);
|
|
144
|
+
pending.resolve(result);
|
|
145
|
+
} catch {
|
|
146
|
+
socket.close(1008, "Invalid SQLite runtime protocol");
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
token,
|
|
152
|
+
path: RUNTIME_PATH,
|
|
153
|
+
listRuntimes(ownerId) {
|
|
154
|
+
if (!validOwnerId(ownerId)) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_INVALID_REQUEST", "Invalid SQLite DevTools panel owner.");
|
|
155
|
+
if (ownerId) for (const runtime of peers.values()) {
|
|
156
|
+
const runtimeOwners = owners.get(runtime.descriptor.id) ?? /* @__PURE__ */ new Set();
|
|
157
|
+
runtimeOwners.add(ownerId);
|
|
158
|
+
owners.set(runtime.descriptor.id, runtimeOwners);
|
|
159
|
+
}
|
|
160
|
+
return [...peers.values()].map((peer) => ({
|
|
161
|
+
...peer.descriptor,
|
|
162
|
+
databases: [...peer.descriptor.databases]
|
|
163
|
+
}));
|
|
164
|
+
},
|
|
165
|
+
subscribe(listener) {
|
|
166
|
+
listeners.add(listener);
|
|
167
|
+
return () => {
|
|
168
|
+
listeners.delete(listener);
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
async invoke(input) {
|
|
172
|
+
try {
|
|
173
|
+
if (!isRecord(input) || !isIdentifier(input.runtimeId) || !isIdentifier(input.sessionId) || !isIdentifier(input.databaseName) || !isSqliteDevtoolsMethod(input.method) || input.method === "close") throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_INVALID_REQUEST", "Unknown SQLite runtime, database, or method.");
|
|
174
|
+
const args = decodeSqliteDevtoolsValue(input.args);
|
|
175
|
+
if (!Array.isArray(args) || args.length > 8) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_INVALID_REQUEST", "SQLite arguments must be an array.");
|
|
176
|
+
const peer = peers.get(input.runtimeId);
|
|
177
|
+
if (!peer || peer.descriptor.sessionId !== input.sessionId || peer.socket.readyState !== WebSocket.OPEN) return errorResult("SQLITE_DEVTOOLS_DISCONNECTED", "The selected runtime session is no longer connected.");
|
|
178
|
+
if (!peer.descriptor.databases.includes(input.databaseName)) return errorResult("SQLITE_DEVTOOLS_DATABASE_CLOSED", "The selected database is no longer registered.");
|
|
179
|
+
if (options.allowWrite !== true && isSqliteDevtoolsWriteMethod(input.method)) return errorResult("SQLITE_DEVTOOLS_READ_ONLY", "This SQLite DevTools host is read-only.");
|
|
180
|
+
if (peer.pending.size >= 64) return errorResult("SQLITE_DEVTOOLS_BUSY", "Too many pending SQLite operations.");
|
|
181
|
+
const requestId = randomUUID();
|
|
182
|
+
const frame = JSON.stringify({
|
|
183
|
+
...input,
|
|
184
|
+
type: "request",
|
|
185
|
+
requestId
|
|
186
|
+
});
|
|
187
|
+
if (Buffer.byteLength(frame) > 16777216) return errorResult("SQLITE_DEVTOOLS_PAYLOAD_LIMIT", "SQLite operation exceeds the message limit.");
|
|
188
|
+
return await new Promise((resolve) => {
|
|
189
|
+
const timer = setTimeout(() => {
|
|
190
|
+
peer.pending.delete(requestId);
|
|
191
|
+
resolve(errorResult("SQLITE_DEVTOOLS_TIMEOUT", "SQLite operation timed out. It was not retried; its outcome may be unknown."));
|
|
192
|
+
}, options.requestTimeoutMs ?? 3e4);
|
|
193
|
+
peer.pending.set(requestId, {
|
|
194
|
+
resolve,
|
|
195
|
+
timer
|
|
196
|
+
});
|
|
197
|
+
peer.socket.send(frame, (error) => {
|
|
198
|
+
if (error) {
|
|
199
|
+
remove(peer);
|
|
200
|
+
peer.socket.terminate();
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return {
|
|
206
|
+
ok: false,
|
|
207
|
+
error: serializeDevtoolsError(error)
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
async releaseSession(input) {
|
|
212
|
+
try {
|
|
213
|
+
if (!isRecord(input) || !isIdentifier(input.runtimeId) || !isIdentifier(input.sessionId) || !validOwnerId(input.ownerId)) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_INVALID_REQUEST", "Unknown SQLite runtime session.");
|
|
214
|
+
const ownerId = input.ownerId;
|
|
215
|
+
if (ownerId) {
|
|
216
|
+
const runtimeOwners = owners.get(input.runtimeId);
|
|
217
|
+
if (!runtimeOwners?.has(ownerId)) return {
|
|
218
|
+
ok: true,
|
|
219
|
+
value: encodeSqliteDevtoolsValue(void 0)
|
|
220
|
+
};
|
|
221
|
+
runtimeOwners.delete(ownerId);
|
|
222
|
+
if (runtimeOwners.size > 0) return {
|
|
223
|
+
ok: true,
|
|
224
|
+
value: encodeSqliteDevtoolsValue(void 0)
|
|
225
|
+
};
|
|
226
|
+
owners.delete(input.runtimeId);
|
|
227
|
+
}
|
|
228
|
+
const peer = peers.get(input.runtimeId);
|
|
229
|
+
if (!peer || peer.descriptor.sessionId !== input.sessionId || peer.socket.readyState !== WebSocket.OPEN) return errorResult("SQLITE_DEVTOOLS_DISCONNECTED", "The selected runtime session is no longer connected.");
|
|
230
|
+
if (peer.pending.size >= 64) return errorResult("SQLITE_DEVTOOLS_BUSY", "Too many pending SQLite operations.");
|
|
231
|
+
const requestId = randomUUID();
|
|
232
|
+
const frame = JSON.stringify({
|
|
233
|
+
type: "release",
|
|
234
|
+
sessionId: peer.descriptor.sessionId,
|
|
235
|
+
requestId
|
|
236
|
+
});
|
|
237
|
+
if (Buffer.byteLength(frame) > 16777216) return errorResult("SQLITE_DEVTOOLS_PAYLOAD_LIMIT", "SQLite session release exceeds the message limit.");
|
|
238
|
+
return await new Promise((resolve) => {
|
|
239
|
+
const timer = setTimeout(() => {
|
|
240
|
+
peer.pending.delete(requestId);
|
|
241
|
+
resolve(errorResult("SQLITE_DEVTOOLS_TIMEOUT", "SQLite debug session release timed out."));
|
|
242
|
+
}, options.requestTimeoutMs ?? 3e4);
|
|
243
|
+
peer.pending.set(requestId, {
|
|
244
|
+
resolve,
|
|
245
|
+
timer
|
|
246
|
+
});
|
|
247
|
+
peer.socket.send(frame, (error) => {
|
|
248
|
+
if (error) {
|
|
249
|
+
remove(peer);
|
|
250
|
+
peer.socket.terminate();
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
} catch (error) {
|
|
255
|
+
return {
|
|
256
|
+
ok: false,
|
|
257
|
+
error: serializeDevtoolsError(error)
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
attach(httpServer) {
|
|
262
|
+
if (closed || detach) throw new Error("SQLite runtime broker is already attached or closed.");
|
|
263
|
+
const upgrade = (request, socket, head) => {
|
|
264
|
+
if (request.url?.split("?")[0] !== RUNTIME_PATH) return;
|
|
265
|
+
if (closed || !validOrigin(request.headers.origin)) {
|
|
266
|
+
socket.destroy();
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
server.handleUpgrade(request, socket, head, (client) => {
|
|
270
|
+
server.emit("connection", client, request);
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
httpServer.on("upgrade", upgrade);
|
|
274
|
+
detach = () => {
|
|
275
|
+
httpServer.off("upgrade", upgrade);
|
|
276
|
+
};
|
|
277
|
+
},
|
|
278
|
+
async close() {
|
|
279
|
+
if (closed) return;
|
|
280
|
+
closed = true;
|
|
281
|
+
detach?.();
|
|
282
|
+
for (const peer of peers.values()) remove(peer);
|
|
283
|
+
for (const socket of sockets) socket.terminate();
|
|
284
|
+
await new Promise((resolve) => {
|
|
285
|
+
server.close(() => resolve());
|
|
286
|
+
});
|
|
287
|
+
listeners.clear();
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region src/index.ts
|
|
293
|
+
function createSqliteDevtoolsDevframe(options = {}) {
|
|
294
|
+
const broker = createSqliteDevtoolsBroker(options);
|
|
295
|
+
let unsubscribe;
|
|
296
|
+
let disposing;
|
|
297
|
+
return {
|
|
298
|
+
definition: defineDevframe({
|
|
299
|
+
id: SQLITE_DEVTOOLS_SCOPE,
|
|
300
|
+
name: "SQLite",
|
|
301
|
+
version: "0.1.0",
|
|
302
|
+
packageName: "@weapp-sqlite/devtools",
|
|
303
|
+
importMetaUrl: import.meta.url,
|
|
304
|
+
homepage: "https://github.com/weapp-sqlite/weapp-sqlite",
|
|
305
|
+
description: "查看与调试 Web 和小程序运行时中的 SQLite 数据库。",
|
|
306
|
+
icon: "ph:database-duotone",
|
|
307
|
+
capabilities: {
|
|
308
|
+
dev: true,
|
|
309
|
+
build: false
|
|
310
|
+
},
|
|
311
|
+
clientAssets: options.clientAssets ?? fileURLToPath(new URL("./client/", import.meta.url)),
|
|
312
|
+
async setup(ctx) {
|
|
313
|
+
const scope = ctx.scope(SQLITE_DEVTOOLS_SCOPE);
|
|
314
|
+
scope.rpc.register(defineRpcFunction({
|
|
315
|
+
name: "list-runtimes",
|
|
316
|
+
type: "query",
|
|
317
|
+
jsonSerializable: true,
|
|
318
|
+
handler: broker.listRuntimes
|
|
319
|
+
}));
|
|
320
|
+
scope.rpc.register(defineRpcFunction({
|
|
321
|
+
name: "invoke",
|
|
322
|
+
type: "action",
|
|
323
|
+
jsonSerializable: true,
|
|
324
|
+
handler: broker.invoke
|
|
325
|
+
}));
|
|
326
|
+
scope.rpc.register(defineRpcFunction({
|
|
327
|
+
name: "release-session",
|
|
328
|
+
type: "action",
|
|
329
|
+
jsonSerializable: true,
|
|
330
|
+
handler: broker.releaseSession
|
|
331
|
+
}));
|
|
332
|
+
unsubscribe?.();
|
|
333
|
+
unsubscribe = broker.subscribe(() => {
|
|
334
|
+
scope.rpc.broadcast({
|
|
335
|
+
method: "state-updated",
|
|
336
|
+
args: [broker.listRuntimes()],
|
|
337
|
+
event: true
|
|
338
|
+
}).catch(() => {});
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}),
|
|
342
|
+
broker,
|
|
343
|
+
dispose() {
|
|
344
|
+
return disposing ??= (async () => {
|
|
345
|
+
unsubscribe?.();
|
|
346
|
+
unsubscribe = void 0;
|
|
347
|
+
await broker.close();
|
|
348
|
+
})();
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function createSqliteDevtoolsPlugin(controller) {
|
|
353
|
+
return {
|
|
354
|
+
...createPluginFromDevframe(controller.definition),
|
|
355
|
+
apply: "serve",
|
|
356
|
+
configureServer(server) {
|
|
357
|
+
if (!server.httpServer) throw new Error("SQLite DevTools requires an HTTP server for its runtime bridge.");
|
|
358
|
+
controller.broker.attach(server.httpServer);
|
|
359
|
+
server.httpServer.once("close", () => {
|
|
360
|
+
controller.dispose();
|
|
361
|
+
});
|
|
362
|
+
},
|
|
363
|
+
async closeServer() {
|
|
364
|
+
await controller.dispose();
|
|
365
|
+
},
|
|
366
|
+
async closeBundle() {
|
|
367
|
+
await controller.dispose();
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
/** 创建独立开发宿主;调用方负责其 watcher 与退出生命周期。 */
|
|
372
|
+
async function startSqliteDevtoolsServer(options = {}) {
|
|
373
|
+
const controller = createSqliteDevtoolsDevframe(options);
|
|
374
|
+
const assets = controller.definition.clientAssets;
|
|
375
|
+
if (typeof assets !== "string" || !existsSync(path.join(assets, "index.html"))) {
|
|
376
|
+
await controller.dispose();
|
|
377
|
+
throw new Error("SQLite DevTools client is missing. Build @weapp-sqlite/devtools first.");
|
|
378
|
+
}
|
|
379
|
+
let server;
|
|
380
|
+
try {
|
|
381
|
+
const devtoolsPlugins = await DevTools({
|
|
382
|
+
builtinDevTools: false,
|
|
383
|
+
clientAuth: true,
|
|
384
|
+
allowedOrigins: [],
|
|
385
|
+
mcp: false
|
|
386
|
+
});
|
|
387
|
+
server = await createServer({
|
|
388
|
+
configFile: false,
|
|
389
|
+
root: options.cwd ?? process.cwd(),
|
|
390
|
+
publicDir: false,
|
|
391
|
+
appType: "custom",
|
|
392
|
+
clearScreen: false,
|
|
393
|
+
logLevel: "silent",
|
|
394
|
+
plugins: [...devtoolsPlugins, createSqliteDevtoolsPlugin(controller)],
|
|
395
|
+
server: {
|
|
396
|
+
host: "127.0.0.1",
|
|
397
|
+
port: options.port ?? 0,
|
|
398
|
+
strictPort: options.port !== void 0 && options.port !== 0,
|
|
399
|
+
watch: { ignored: ["**/*"] }
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
await server.listen();
|
|
403
|
+
const address = server.httpServer?.address();
|
|
404
|
+
if (!address || typeof address === "string") throw new Error("SQLite DevTools could not resolve its listening port.");
|
|
405
|
+
const origin = `http://127.0.0.1:${address.port}`;
|
|
406
|
+
let closing;
|
|
407
|
+
const activeServer = server;
|
|
408
|
+
return {
|
|
409
|
+
url: buildOtpAuthUrl(`${origin}/__weapp-sqlite/`, refreshTempAuthCode()),
|
|
410
|
+
runtimeEndpoint: `ws://127.0.0.1:${address.port}${controller.broker.path}`,
|
|
411
|
+
runtimeToken: controller.broker.token,
|
|
412
|
+
close() {
|
|
413
|
+
return closing ??= controller.dispose().finally(() => activeServer.close());
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
} catch (error) {
|
|
417
|
+
await controller.dispose();
|
|
418
|
+
await server?.close();
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
//#endregion
|
|
423
|
+
export { connectDevtoolsClient, createSqliteDevtoolsDevframe, createSqliteDevtoolsPlugin, startSqliteDevtoolsServer };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/codec.d.ts
|
|
2
|
+
/** 使用显式标签保留整数和二进制值,普通对象不能伪装成协议标签。 */
|
|
3
|
+
declare function encodeSqliteDevtoolsValue(value: unknown, maxBytes?: number): SqliteDevtoolsWireValue;
|
|
4
|
+
declare function decodeSqliteDevtoolsValue(value: unknown, maxBytes?: number): unknown;
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/protocol.d.ts
|
|
7
|
+
declare const SQLITE_DEVTOOLS_SCOPE = "weapp-sqlite";
|
|
8
|
+
declare const SQLITE_DEVTOOLS_PROTOCOL = 1;
|
|
9
|
+
declare const SQLITE_DEVTOOLS_MAX_MESSAGE_BYTES: number;
|
|
10
|
+
declare const SQLITE_DEVTOOLS_READ_METHODS: readonly ["listTables", "describeTable", "getTableCapabilities", "listIndexes", "readTable", "query", "analyzeQuery", "getMigrationStatus", "getMigrationDiagnostics", "getForeignKeyDiagnostics", "exportDatabase", "exportTable", "previewTableImport", "getUndoState"];
|
|
11
|
+
declare const SQLITE_DEVTOOLS_WRITE_METHODS: readonly ["execute", "insertRow", "updateRow", "deleteRows", "createTable", "renameTable", "dropTable", "truncateTable", "addColumn", "renameColumn", "dropColumn", "createIndex", "dropIndex", "importDatabase", "importTable", "undoLastDestructiveChange", "resetDatabase"];
|
|
12
|
+
type SqliteDevtoolsMethod = typeof SQLITE_DEVTOOLS_READ_METHODS[number] | typeof SQLITE_DEVTOOLS_WRITE_METHODS[number] | 'close';
|
|
13
|
+
interface SqliteDevtoolsRuntime {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly label: string;
|
|
16
|
+
readonly platform: string;
|
|
17
|
+
}
|
|
18
|
+
interface SqliteDevtoolsRuntimeDescriptor extends SqliteDevtoolsRuntime {
|
|
19
|
+
readonly databases: readonly string[];
|
|
20
|
+
readonly sessionId: string;
|
|
21
|
+
readonly connectedAt: string;
|
|
22
|
+
readonly readOnly: boolean;
|
|
23
|
+
}
|
|
24
|
+
interface SqliteDevtoolsErrorData {
|
|
25
|
+
readonly code: string;
|
|
26
|
+
readonly message: string;
|
|
27
|
+
}
|
|
28
|
+
declare class SqliteDevtoolsError extends Error {
|
|
29
|
+
readonly code: string;
|
|
30
|
+
constructor(code: string, message: string);
|
|
31
|
+
}
|
|
32
|
+
type SqliteDevtoolsWireValue = null | boolean | number | string | {
|
|
33
|
+
readonly type: 'undefined';
|
|
34
|
+
} | {
|
|
35
|
+
readonly type: 'bigint';
|
|
36
|
+
readonly value: string;
|
|
37
|
+
} | {
|
|
38
|
+
readonly type: 'bytes';
|
|
39
|
+
readonly value: string;
|
|
40
|
+
readonly kind: 'uint8array' | 'arraybuffer';
|
|
41
|
+
} | {
|
|
42
|
+
readonly type: 'array';
|
|
43
|
+
readonly value: readonly SqliteDevtoolsWireValue[];
|
|
44
|
+
} | {
|
|
45
|
+
readonly type: 'object';
|
|
46
|
+
readonly value: readonly (readonly [string, SqliteDevtoolsWireValue])[];
|
|
47
|
+
};
|
|
48
|
+
type SqliteDevtoolsResult = {
|
|
49
|
+
readonly ok: true;
|
|
50
|
+
readonly value: SqliteDevtoolsWireValue;
|
|
51
|
+
} | {
|
|
52
|
+
readonly ok: false;
|
|
53
|
+
readonly error: SqliteDevtoolsErrorData;
|
|
54
|
+
};
|
|
55
|
+
interface SqliteDevtoolsInvocation {
|
|
56
|
+
readonly runtimeId: string;
|
|
57
|
+
readonly sessionId: string;
|
|
58
|
+
readonly databaseName: string;
|
|
59
|
+
readonly method: SqliteDevtoolsMethod;
|
|
60
|
+
readonly args: SqliteDevtoolsWireValue;
|
|
61
|
+
}
|
|
62
|
+
/** Releases only the debug controllers owned by a panel session. */
|
|
63
|
+
interface SqliteDevtoolsReleaseSession {
|
|
64
|
+
readonly runtimeId: string;
|
|
65
|
+
readonly sessionId: string;
|
|
66
|
+
/** Stable id for one Devframe panel connection. */
|
|
67
|
+
readonly ownerId?: string;
|
|
68
|
+
}
|
|
69
|
+
declare function isSqliteDevtoolsMethod(value: unknown): value is SqliteDevtoolsMethod;
|
|
70
|
+
declare function isSqliteDevtoolsWriteMethod(value: SqliteDevtoolsMethod): boolean;
|
|
71
|
+
declare function isIdentifier(value: unknown): value is string;
|
|
72
|
+
declare function serializeDevtoolsError(error: unknown): SqliteDevtoolsErrorData;
|
|
73
|
+
declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
74
|
+
declare function validDatabaseNames(value: unknown): value is string[];
|
|
75
|
+
//#endregion
|
|
76
|
+
export { encodeSqliteDevtoolsValue as S, isSqliteDevtoolsMethod as _, SQLITE_DEVTOOLS_WRITE_METHODS as a, validDatabaseNames as b, SqliteDevtoolsInvocation as c, SqliteDevtoolsResult as d, SqliteDevtoolsRuntime as f, isRecord as g, isIdentifier as h, SQLITE_DEVTOOLS_SCOPE as i, SqliteDevtoolsMethod as l, SqliteDevtoolsWireValue as m, SQLITE_DEVTOOLS_PROTOCOL as n, SqliteDevtoolsError as o, SqliteDevtoolsRuntimeDescriptor as p, SQLITE_DEVTOOLS_READ_METHODS as r, SqliteDevtoolsErrorData as s, SQLITE_DEVTOOLS_MAX_MESSAGE_BYTES as t, SqliteDevtoolsReleaseSession as u, isSqliteDevtoolsWriteMethod as v, decodeSqliteDevtoolsValue as x, serializeDevtoolsError as y };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/codec.d.ts
|
|
2
|
+
/** 使用显式标签保留整数和二进制值,普通对象不能伪装成协议标签。 */
|
|
3
|
+
declare function encodeSqliteDevtoolsValue(value: unknown, maxBytes?: number): SqliteDevtoolsWireValue;
|
|
4
|
+
declare function decodeSqliteDevtoolsValue(value: unknown, maxBytes?: number): unknown;
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/protocol.d.ts
|
|
7
|
+
declare const SQLITE_DEVTOOLS_SCOPE = "weapp-sqlite";
|
|
8
|
+
declare const SQLITE_DEVTOOLS_PROTOCOL = 1;
|
|
9
|
+
declare const SQLITE_DEVTOOLS_MAX_MESSAGE_BYTES: number;
|
|
10
|
+
declare const SQLITE_DEVTOOLS_READ_METHODS: readonly ["listTables", "describeTable", "getTableCapabilities", "listIndexes", "readTable", "query", "analyzeQuery", "getMigrationStatus", "getMigrationDiagnostics", "getForeignKeyDiagnostics", "exportDatabase", "exportTable", "previewTableImport", "getUndoState"];
|
|
11
|
+
declare const SQLITE_DEVTOOLS_WRITE_METHODS: readonly ["execute", "insertRow", "updateRow", "deleteRows", "createTable", "renameTable", "dropTable", "truncateTable", "addColumn", "renameColumn", "dropColumn", "createIndex", "dropIndex", "importDatabase", "importTable", "undoLastDestructiveChange", "resetDatabase"];
|
|
12
|
+
type SqliteDevtoolsMethod = typeof SQLITE_DEVTOOLS_READ_METHODS[number] | typeof SQLITE_DEVTOOLS_WRITE_METHODS[number] | 'close';
|
|
13
|
+
interface SqliteDevtoolsRuntime {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly label: string;
|
|
16
|
+
readonly platform: string;
|
|
17
|
+
}
|
|
18
|
+
interface SqliteDevtoolsRuntimeDescriptor extends SqliteDevtoolsRuntime {
|
|
19
|
+
readonly databases: readonly string[];
|
|
20
|
+
readonly sessionId: string;
|
|
21
|
+
readonly connectedAt: string;
|
|
22
|
+
readonly readOnly: boolean;
|
|
23
|
+
}
|
|
24
|
+
interface SqliteDevtoolsErrorData {
|
|
25
|
+
readonly code: string;
|
|
26
|
+
readonly message: string;
|
|
27
|
+
}
|
|
28
|
+
declare class SqliteDevtoolsError extends Error {
|
|
29
|
+
readonly code: string;
|
|
30
|
+
constructor(code: string, message: string);
|
|
31
|
+
}
|
|
32
|
+
type SqliteDevtoolsWireValue = null | boolean | number | string | {
|
|
33
|
+
readonly type: 'undefined';
|
|
34
|
+
} | {
|
|
35
|
+
readonly type: 'bigint';
|
|
36
|
+
readonly value: string;
|
|
37
|
+
} | {
|
|
38
|
+
readonly type: 'bytes';
|
|
39
|
+
readonly value: string;
|
|
40
|
+
readonly kind: 'uint8array' | 'arraybuffer';
|
|
41
|
+
} | {
|
|
42
|
+
readonly type: 'array';
|
|
43
|
+
readonly value: readonly SqliteDevtoolsWireValue[];
|
|
44
|
+
} | {
|
|
45
|
+
readonly type: 'object';
|
|
46
|
+
readonly value: readonly (readonly [string, SqliteDevtoolsWireValue])[];
|
|
47
|
+
};
|
|
48
|
+
type SqliteDevtoolsResult = {
|
|
49
|
+
readonly ok: true;
|
|
50
|
+
readonly value: SqliteDevtoolsWireValue;
|
|
51
|
+
} | {
|
|
52
|
+
readonly ok: false;
|
|
53
|
+
readonly error: SqliteDevtoolsErrorData;
|
|
54
|
+
};
|
|
55
|
+
interface SqliteDevtoolsInvocation {
|
|
56
|
+
readonly runtimeId: string;
|
|
57
|
+
readonly sessionId: string;
|
|
58
|
+
readonly databaseName: string;
|
|
59
|
+
readonly method: SqliteDevtoolsMethod;
|
|
60
|
+
readonly args: SqliteDevtoolsWireValue;
|
|
61
|
+
}
|
|
62
|
+
/** Releases only the debug controllers owned by a panel session. */
|
|
63
|
+
interface SqliteDevtoolsReleaseSession {
|
|
64
|
+
readonly runtimeId: string;
|
|
65
|
+
readonly sessionId: string;
|
|
66
|
+
/** Stable id for one Devframe panel connection. */
|
|
67
|
+
readonly ownerId?: string;
|
|
68
|
+
}
|
|
69
|
+
declare function isSqliteDevtoolsMethod(value: unknown): value is SqliteDevtoolsMethod;
|
|
70
|
+
declare function isSqliteDevtoolsWriteMethod(value: SqliteDevtoolsMethod): boolean;
|
|
71
|
+
declare function isIdentifier(value: unknown): value is string;
|
|
72
|
+
declare function serializeDevtoolsError(error: unknown): SqliteDevtoolsErrorData;
|
|
73
|
+
declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
74
|
+
declare function validDatabaseNames(value: unknown): value is string[];
|
|
75
|
+
//#endregion
|
|
76
|
+
export { encodeSqliteDevtoolsValue as S, isSqliteDevtoolsMethod as _, SQLITE_DEVTOOLS_WRITE_METHODS as a, validDatabaseNames as b, SqliteDevtoolsInvocation as c, SqliteDevtoolsResult as d, SqliteDevtoolsRuntime as f, isRecord as g, isIdentifier as h, SQLITE_DEVTOOLS_SCOPE as i, SqliteDevtoolsMethod as l, SqliteDevtoolsWireValue as m, SQLITE_DEVTOOLS_PROTOCOL as n, SqliteDevtoolsError as o, SqliteDevtoolsRuntimeDescriptor as p, SQLITE_DEVTOOLS_READ_METHODS as r, SqliteDevtoolsErrorData as s, SQLITE_DEVTOOLS_MAX_MESSAGE_BYTES as t, SqliteDevtoolsReleaseSession as u, isSqliteDevtoolsWriteMethod as v, decodeSqliteDevtoolsValue as x, serializeDevtoolsError as y };
|