@weapp-sqlite/devtools 0.0.0-init.0 → 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.
@@ -0,0 +1,394 @@
1
+ import { c as isRecord, d as serializeDevtoolsError, f as validDatabaseNames, l as isSqliteDevtoolsMethod, m as encodeSqliteDevtoolsValue, o as SqliteDevtoolsError, p as decodeSqliteDevtoolsValue, s as isIdentifier, u as isSqliteDevtoolsWriteMethod } from "./protocol-nkWp0_XO.mjs";
2
+ //#region src/socket.ts
3
+ function toBinaryBytes(value) {
4
+ if (typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer) return new Uint8Array(value);
5
+ if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value)) {
6
+ const view = value;
7
+ return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
8
+ }
9
+ }
10
+ function decodeUtf8(value) {
11
+ if (typeof value === "string") return value;
12
+ const bytes = toBinaryBytes(value);
13
+ if (!bytes) return;
14
+ if (typeof TextDecoder !== "undefined") try {
15
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
16
+ } catch {
17
+ return;
18
+ }
19
+ let output = "";
20
+ for (let index = 0; index < bytes.length;) {
21
+ const first = bytes[index++] ?? -1;
22
+ if (first <= 127) {
23
+ output += String.fromCharCode(first);
24
+ continue;
25
+ }
26
+ const continuation = (count) => {
27
+ if (index + count > bytes.length) return;
28
+ let codePoint = 0;
29
+ for (let offset = 0; offset < count; offset++) {
30
+ const next = bytes[index++] ?? -1;
31
+ if ((next & 192) !== 128) return;
32
+ codePoint = codePoint << 6 | next & 63;
33
+ }
34
+ return codePoint;
35
+ };
36
+ let codePoint;
37
+ if (first >= 194 && first <= 223) {
38
+ const next = bytes[index++] ?? -1;
39
+ if ((next & 192) === 128) codePoint = (first & 31) << 6 | next & 63;
40
+ } else if (first >= 224 && first <= 239) {
41
+ const second = bytes[index++] ?? -1;
42
+ if (first === 224 && second >= 160 && second <= 191 || first === 237 && second >= 128 && second <= 159 || first !== 224 && first !== 237 && second >= 128 && second <= 191) {
43
+ const next = continuation(1);
44
+ if (next !== void 0) codePoint = (first & 15) << 12 | (second & 63) << 6 | next;
45
+ }
46
+ } else if (first >= 240 && first <= 244) {
47
+ const second = bytes[index++] ?? -1;
48
+ if (first === 240 && second >= 144 && second <= 191 || first === 244 && second >= 128 && second <= 143 || first !== 240 && first !== 244 && second >= 128 && second <= 191) {
49
+ const next = continuation(2);
50
+ if (next !== void 0) codePoint = (first & 7) << 18 | (second & 63) << 12 | next;
51
+ }
52
+ }
53
+ if (codePoint === void 0 || codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343) return;
54
+ output += String.fromCodePoint(codePoint);
55
+ }
56
+ return output;
57
+ }
58
+ function createWebSocketFactory(Constructor) {
59
+ return (url, handlers) => {
60
+ let socket;
61
+ let ended = false;
62
+ let underlyingClosed = false;
63
+ function closeUnderlying() {
64
+ if (!socket || underlyingClosed) return;
65
+ underlyingClosed = true;
66
+ try {
67
+ socket.close();
68
+ } catch {}
69
+ }
70
+ function fail() {
71
+ if (ended) return;
72
+ ended = true;
73
+ closeUnderlying();
74
+ handlers.error();
75
+ }
76
+ try {
77
+ socket = new Constructor(url);
78
+ socket.addEventListener("open", () => {
79
+ if (!ended) handlers.open();
80
+ });
81
+ socket.addEventListener("message", (event) => {
82
+ if (ended) return;
83
+ const data = decodeUtf8(event.data);
84
+ if (data === void 0) {
85
+ fail();
86
+ return;
87
+ }
88
+ handlers.message(data);
89
+ });
90
+ socket.addEventListener("close", () => {
91
+ if (ended) return;
92
+ ended = true;
93
+ handlers.close();
94
+ });
95
+ socket.addEventListener("error", fail);
96
+ } catch {
97
+ fail();
98
+ }
99
+ return {
100
+ send(data) {
101
+ if (ended || !socket) return;
102
+ try {
103
+ socket.send(data);
104
+ } catch {
105
+ fail();
106
+ }
107
+ },
108
+ close() {
109
+ if (ended) return;
110
+ ended = true;
111
+ closeUnderlying();
112
+ }
113
+ };
114
+ };
115
+ }
116
+ /** 直接使用 SocketTask,不依赖浏览器全局变量或动态求值。 */
117
+ function createWeappSocketFactory(api) {
118
+ return (url, handlers) => {
119
+ let task;
120
+ let ended = false;
121
+ let underlyingClosed = false;
122
+ function closeUnderlying() {
123
+ if (!task || underlyingClosed) return;
124
+ underlyingClosed = true;
125
+ try {
126
+ task.close({
127
+ code: 1e3,
128
+ reason: "SQLite DevTools closed"
129
+ });
130
+ } catch {}
131
+ }
132
+ function fail() {
133
+ if (ended) return;
134
+ ended = true;
135
+ closeUnderlying();
136
+ handlers.error();
137
+ }
138
+ function handleMessage(event) {
139
+ if (ended) return;
140
+ const data = decodeUtf8(event.data);
141
+ if (data === void 0) {
142
+ fail();
143
+ return;
144
+ }
145
+ handlers.message(data);
146
+ }
147
+ try {
148
+ task = api.connectSocket({
149
+ url,
150
+ success: () => {},
151
+ fail
152
+ });
153
+ if (!task) throw new Error("wx.connectSocket did not return a SocketTask.");
154
+ if (ended) closeUnderlying();
155
+ else {
156
+ task.onOpen(() => {
157
+ if (!ended) handlers.open();
158
+ });
159
+ task.onMessage(handleMessage);
160
+ task.onClose(() => {
161
+ if (ended) return;
162
+ ended = true;
163
+ handlers.close();
164
+ });
165
+ task.onError(fail);
166
+ }
167
+ } catch {
168
+ fail();
169
+ }
170
+ return {
171
+ send(data) {
172
+ if (ended || !task) return;
173
+ try {
174
+ task.send({
175
+ data,
176
+ fail
177
+ });
178
+ } catch {
179
+ fail();
180
+ }
181
+ },
182
+ close() {
183
+ if (ended) return;
184
+ ended = true;
185
+ closeUnderlying();
186
+ }
187
+ };
188
+ };
189
+ }
190
+ //#endregion
191
+ //#region src/runtime.ts
192
+ function utf8ByteLength(value) {
193
+ if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(value).byteLength;
194
+ let bytes = 0;
195
+ for (const character of value) {
196
+ const codePoint = character.codePointAt(0) ?? 0;
197
+ bytes += codePoint <= 127 ? 1 : codePoint <= 2047 ? 2 : codePoint <= 65535 ? 3 : 4;
198
+ }
199
+ return bytes;
200
+ }
201
+ /** 只登记和转发当前运行时的数据库,断线后不重放任何数据库操作。 */
202
+ function connectSqliteDevtoolsRuntime(options) {
203
+ if (!/^ws:\/\/(?:127\.0\.0\.1|localhost|\[::1\])(?::\d+)?\//.test(options.endpoint) || !options.token || !isIdentifier(options.runtime.id) || !isIdentifier(options.runtime.label) || !isIdentifier(options.runtime.platform)) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_INVALID_CONFIG", "SQLite DevTools requires a loopback endpoint and valid runtime identity.");
204
+ let disposed = false;
205
+ let generation = 0;
206
+ let socket;
207
+ let reconnectTimer;
208
+ let authTimer;
209
+ let sessionId;
210
+ let allowWrite = false;
211
+ let requestIds = /* @__PURE__ */ new Set();
212
+ let reconnectAttempt = 0;
213
+ let releaseVersion = 0;
214
+ let releaseInProgress;
215
+ function databases() {
216
+ const names = [...options.listDatabases()];
217
+ if (!validDatabaseNames(names)) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_INVALID_CONFIG", "SQLite DevTools database names are invalid.");
218
+ return names;
219
+ }
220
+ function send(payload) {
221
+ const text = JSON.stringify(payload);
222
+ if (utf8ByteLength(text) > 16777216) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_PAYLOAD_LIMIT", "SQLite DevTools message exceeds the size limit.");
223
+ socket?.send(text);
224
+ }
225
+ function disconnect(current) {
226
+ if (current !== generation) return;
227
+ generation += 1;
228
+ sessionId = void 0;
229
+ requestIds.clear();
230
+ clearTimeout(authTimer);
231
+ const previous = socket;
232
+ socket = void 0;
233
+ try {
234
+ previous?.close();
235
+ } catch {}
236
+ Promise.resolve().then(() => options.onDisconnect?.()).catch(() => {});
237
+ if (!disposed && !reconnectTimer) {
238
+ const delay = Math.min(5e3, (options.reconnectDelayMs ?? 500) * 2 ** Math.min(reconnectAttempt++, 4));
239
+ reconnectTimer = setTimeout(() => {
240
+ reconnectTimer = void 0;
241
+ connect();
242
+ }, delay);
243
+ }
244
+ }
245
+ async function receive(text, current) {
246
+ if (disposed || current !== generation) return;
247
+ try {
248
+ if (utf8ByteLength(text) > 16777216) throw new Error("Oversized message");
249
+ const message = JSON.parse(text);
250
+ if (!isRecord(message)) throw new Error("Invalid message");
251
+ if (message.type === "ready" && isIdentifier(message.sessionId) && typeof message.allowWrite === "boolean") {
252
+ if (sessionId) throw new Error("Duplicate handshake");
253
+ clearTimeout(authTimer);
254
+ sessionId = message.sessionId;
255
+ allowWrite = message.allowWrite;
256
+ reconnectAttempt = 0;
257
+ return;
258
+ }
259
+ if (message.type === "release") {
260
+ if (!sessionId || message.sessionId !== sessionId || !isIdentifier(message.requestId)) throw new Error("Invalid runtime release");
261
+ const requestedSession = sessionId;
262
+ ++releaseVersion;
263
+ const release = (async () => {
264
+ await options.onDisconnect?.();
265
+ })();
266
+ releaseInProgress = release;
267
+ try {
268
+ await release;
269
+ if (!disposed && current === generation && requestedSession === sessionId) send({
270
+ type: "release-result",
271
+ sessionId: requestedSession,
272
+ requestId: message.requestId,
273
+ result: {
274
+ ok: true,
275
+ value: encodeSqliteDevtoolsValue(void 0)
276
+ }
277
+ });
278
+ } catch (error) {
279
+ if (!disposed && current === generation && requestedSession === sessionId) send({
280
+ type: "release-result",
281
+ sessionId: requestedSession,
282
+ requestId: message.requestId,
283
+ result: {
284
+ ok: false,
285
+ error: serializeDevtoolsError(error)
286
+ }
287
+ });
288
+ } finally {
289
+ if (releaseInProgress === release) releaseInProgress = void 0;
290
+ }
291
+ return;
292
+ }
293
+ if (message.type !== "request" || !sessionId || message.sessionId !== sessionId || !isIdentifier(message.requestId)) throw new Error("Invalid runtime request");
294
+ if (requestIds.has(message.requestId) || requestIds.size >= 1e5) throw new Error("Duplicate or excessive request ids");
295
+ requestIds.add(message.requestId);
296
+ const requestId = message.requestId;
297
+ const requestedSession = sessionId;
298
+ const requestedReleaseVersion = releaseVersion;
299
+ let result;
300
+ try {
301
+ if (releaseInProgress) await releaseInProgress;
302
+ if (disposed || current !== generation || requestedSession !== sessionId || requestedReleaseVersion !== releaseVersion) return;
303
+ if (!isSqliteDevtoolsMethod(message.method) || message.method === "close" || !isIdentifier(message.databaseName) || !databases().includes(message.databaseName)) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_INVALID_REQUEST", "Unknown SQLite database or operation.");
304
+ if (!allowWrite && isSqliteDevtoolsWriteMethod(message.method)) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_READ_ONLY", "This SQLite DevTools session is read-only.");
305
+ const args = decodeSqliteDevtoolsValue(message.args);
306
+ if (!Array.isArray(args) || args.length > 8) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_INVALID_REQUEST", "SQLite operation arguments must be an array.");
307
+ const controller = await options.getController(message.databaseName);
308
+ if (disposed || current !== generation || requestedSession !== sessionId) return;
309
+ if (!databases().includes(message.databaseName)) throw new SqliteDevtoolsError("SQLITE_DEVTOOLS_DATABASE_CLOSED", "The selected SQLite database is no longer registered.");
310
+ const value = await controller[message.method].apply(controller, args);
311
+ result = {
312
+ ok: true,
313
+ value: encodeSqliteDevtoolsValue(value)
314
+ };
315
+ } catch (error) {
316
+ result = {
317
+ ok: false,
318
+ error: serializeDevtoolsError(error)
319
+ };
320
+ }
321
+ if (!disposed && current === generation && requestedSession === sessionId) send({
322
+ type: "result",
323
+ sessionId,
324
+ requestId,
325
+ result
326
+ });
327
+ } catch {
328
+ disconnect(current);
329
+ }
330
+ }
331
+ function connect() {
332
+ if (disposed) return;
333
+ const current = ++generation;
334
+ requestIds = /* @__PURE__ */ new Set();
335
+ try {
336
+ const created = options.createSocket(options.endpoint, {
337
+ open() {
338
+ queueMicrotask(() => {
339
+ if (disposed || current !== generation) return;
340
+ try {
341
+ send({
342
+ type: "hello",
343
+ protocol: 1,
344
+ token: options.token,
345
+ runtime: options.runtime,
346
+ databases: databases()
347
+ });
348
+ authTimer = setTimeout(disconnect, 5e3, current);
349
+ } catch {
350
+ disconnect(current);
351
+ }
352
+ });
353
+ },
354
+ message: (text) => {
355
+ receive(text, current);
356
+ },
357
+ close: () => disconnect(current),
358
+ error: () => disconnect(current)
359
+ });
360
+ if (disposed || current !== generation) {
361
+ try {
362
+ created.close();
363
+ } catch {}
364
+ return;
365
+ }
366
+ socket = created;
367
+ } catch {
368
+ disconnect(current);
369
+ }
370
+ }
371
+ connect();
372
+ return {
373
+ refresh() {
374
+ if (!disposed && sessionId) try {
375
+ send({
376
+ type: "databases",
377
+ sessionId,
378
+ databases: databases()
379
+ });
380
+ } catch {
381
+ disconnect(generation);
382
+ }
383
+ },
384
+ close() {
385
+ if (disposed) return;
386
+ disposed = true;
387
+ clearTimeout(reconnectTimer);
388
+ clearTimeout(authTimer);
389
+ disconnect(generation);
390
+ }
391
+ };
392
+ }
393
+ //#endregion
394
+ export { connectSqliteDevtoolsRuntime, createWeappSocketFactory, createWebSocketFactory };
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@weapp-sqlite/devtools",
3
- "version": "0.0.0-init.0",
4
- "description": "初始化 npm 包名与 OIDC 可信发布;正式版本尚未发布。",
5
- "license": "MIT",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "description": "连接 Web 与小程序实时 SQLite 数据库的 Devframe 开发工具",
6
6
  "author": "weapp-sqlite contributors",
7
+ "license": "MIT",
7
8
  "homepage": "https://github.com/weapp-sqlite/weapp-sqlite#readme",
8
9
  "repository": {
9
10
  "type": "git",
@@ -13,13 +14,80 @@
13
14
  "bugs": {
14
15
  "url": "https://github.com/weapp-sqlite/weapp-sqlite/issues"
15
16
  },
16
- "type": "module",
17
+ "sideEffects": false,
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.mts",
21
+ "import": "./dist/index.mjs",
22
+ "require": "./dist/index.cjs"
23
+ },
24
+ "./runtime": {
25
+ "types": "./dist/runtime.d.mts",
26
+ "import": "./dist/runtime.mjs",
27
+ "require": "./dist/runtime.cjs"
28
+ },
29
+ "./client": {
30
+ "types": "./dist/client.d.mts",
31
+ "import": "./dist/client.mjs",
32
+ "require": "./dist/client.cjs"
33
+ },
34
+ "./protocol": {
35
+ "types": "./dist/protocol.d.mts",
36
+ "import": "./dist/protocol.mjs",
37
+ "require": "./dist/protocol.cjs"
38
+ }
39
+ },
40
+ "main": "./dist/index.cjs",
41
+ "module": "./dist/index.mjs",
42
+ "types": "./dist/index.d.mts",
17
43
  "files": [
18
- "README.md",
19
- "LICENSE"
44
+ "dist"
20
45
  ],
46
+ "engines": {
47
+ "node": ">=22.22.1"
48
+ },
49
+ "tsd": {
50
+ "compilerOptions": {
51
+ "lib": [
52
+ "ESNext",
53
+ "DOM",
54
+ "DOM.Iterable"
55
+ ],
56
+ "module": "NodeNext",
57
+ "moduleResolution": "Bundler",
58
+ "baseUrl": ".",
59
+ "paths": {
60
+ "@weapp-sqlite/core": [
61
+ "../core/dist/index.d.mts"
62
+ ],
63
+ "@weapp-sqlite/debug": [
64
+ "../debug/dist/index.d.mts"
65
+ ]
66
+ },
67
+ "skipLibCheck": true
68
+ }
69
+ },
70
+ "dependencies": {
71
+ "@vitejs/devtools": "0.7.6",
72
+ "@vitejs/devtools-kit": "0.7.6",
73
+ "@weapp-sqlite/debug": "0.2.0",
74
+ "devframe": "1.2.0",
75
+ "vite": "8.3.2",
76
+ "ws": "^8.22.0"
77
+ },
78
+ "devDependencies": {
79
+ "@types/ws": "^8.18.2"
80
+ },
21
81
  "publishConfig": {
22
82
  "access": "public",
23
83
  "registry": "https://registry.npmjs.org/"
84
+ },
85
+ "scripts": {
86
+ "build": "tsdown && tsx build-client.ts",
87
+ "test": "vitest run",
88
+ "lint": "eslint .",
89
+ "lint:fix": "eslint . --fix",
90
+ "typecheck": "tsc -p tsconfig.json",
91
+ "tsd": "pnpm build && tsd --typings dist/index.d.mts --files test-d/**/*.test-d.ts"
24
92
  }
25
- }
93
+ }