@sfmc-bds/db-server 0.2.0-beta.0 → 0.2.0-beta.10
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 +663 -0
- package/dist/domain/bridge.d.ts +57 -23
- package/dist/domain/bridge.d.ts.map +1 -1
- package/dist/domain/bridge.js +77 -6
- package/dist/domain/bridge.js.map +2 -2
- package/dist/domain/bridge.test.d.ts +2 -0
- package/dist/domain/bridge.test.d.ts.map +1 -0
- package/dist/domain/bridge.test.js +87 -0
- package/dist/domain/bridge.test.js.map +7 -0
- package/dist/domain/economy.d.ts +38 -7
- package/dist/domain/economy.d.ts.map +1 -1
- package/dist/domain/economy.js.map +2 -2
- package/dist/domain/messages-bridge-filter.test.d.ts +5 -0
- package/dist/domain/messages-bridge-filter.test.d.ts.map +1 -0
- package/dist/domain/messages-bridge-filter.test.js +20 -0
- package/dist/domain/messages-bridge-filter.test.js.map +7 -0
- package/dist/domain/qq-bind-status.test.d.ts +2 -0
- package/dist/domain/qq-bind-status.test.d.ts.map +1 -0
- package/dist/domain/qq-bind-status.test.js +211 -0
- package/dist/domain/qq-bind-status.test.js.map +7 -0
- package/dist/domain/qq-events.d.ts +84 -0
- package/dist/domain/qq-events.d.ts.map +1 -0
- package/dist/domain/qq-events.js +193 -0
- package/dist/domain/qq-events.js.map +7 -0
- package/dist/domain/qq-events.test.d.ts +5 -0
- package/dist/domain/qq-events.test.d.ts.map +1 -0
- package/dist/domain/qq-events.test.js +170 -0
- package/dist/domain/qq-events.test.js.map +7 -0
- package/dist/domain/qq-join.test.d.ts +2 -0
- package/dist/domain/qq-join.test.d.ts.map +1 -0
- package/dist/domain/qq-join.test.js +353 -0
- package/dist/domain/qq-join.test.js.map +7 -0
- package/dist/domain/schema.d.ts +15 -18
- package/dist/domain/schema.d.ts.map +1 -1
- package/dist/domain/schema.js +67 -451
- package/dist/domain/schema.js.map +2 -2
- package/dist/domain/system-status.d.ts +75 -0
- package/dist/domain/system-status.d.ts.map +1 -0
- package/dist/domain/system-status.js +181 -0
- package/dist/domain/system-status.js.map +7 -0
- package/dist/domain/system-status.test.d.ts +5 -0
- package/dist/domain/system-status.test.d.ts.map +1 -0
- package/dist/domain/system-status.test.js +19 -0
- package/dist/domain/system-status.test.js.map +7 -0
- package/dist/domain/transaction.d.ts +5 -4
- package/dist/domain/transaction.d.ts.map +1 -1
- package/dist/domain/transaction.js.map +2 -2
- package/dist/env.d.ts +7 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +57 -9
- package/dist/env.js.map +2 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -19
- package/dist/index.js.map +2 -2
- package/dist/lib/idempotency-store.d.ts +17 -11
- package/dist/lib/idempotency-store.d.ts.map +1 -1
- package/dist/lib/idempotency-store.js.map +2 -2
- package/dist/lib/idempotency.d.ts +3 -4
- package/dist/lib/idempotency.d.ts.map +1 -1
- package/dist/lib/idempotency.js.map +2 -2
- package/dist/lib/log.d.ts.map +1 -1
- package/dist/lib/log.js +3 -6
- package/dist/lib/log.js.map +2 -2
- package/dist/lib/module-state.d.ts +38 -8
- package/dist/lib/module-state.d.ts.map +1 -1
- package/dist/lib/module-state.js.map +2 -2
- package/dist/lib/order-by.d.ts +8 -6
- package/dist/lib/order-by.d.ts.map +1 -1
- package/dist/lib/order-by.js.map +2 -2
- package/dist/lib/sql-helpers.d.ts +25 -13
- package/dist/lib/sql-helpers.d.ts.map +1 -1
- package/dist/lib/sql-helpers.js.map +2 -2
- package/dist/lib/sqlite.d.ts +14 -5
- package/dist/lib/sqlite.d.ts.map +1 -1
- package/dist/lib/sqlite.js.map +2 -2
- package/dist/manifest-loader.d.ts +5 -2
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +16 -6
- package/dist/manifest-loader.js.map +2 -2
- package/dist/manifest.d.ts +1 -2
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +13 -8
- package/dist/manifest.js.map +2 -2
- package/dist/module-auth.d.ts.map +1 -1
- package/dist/module-auth.js +3 -7
- package/dist/module-auth.js.map +2 -2
- package/dist/permission-gate.d.ts.map +1 -1
- package/dist/permission-gate.js +8 -2
- package/dist/permission-gate.js.map +2 -2
- package/dist/project-root.d.ts +6 -0
- package/dist/project-root.d.ts.map +1 -0
- package/dist/project-root.js +9 -0
- package/dist/project-root.js.map +7 -0
- package/dist/routes/_shared.d.ts +14 -5
- package/dist/routes/_shared.d.ts.map +1 -1
- package/dist/routes/_shared.js.map +2 -2
- package/dist/routes/config.d.ts +8 -11
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +17 -23
- package/dist/routes/config.js.map +2 -2
- package/dist/routes/db-routes.d.ts +8 -2
- package/dist/routes/db-routes.d.ts.map +1 -1
- package/dist/routes/db-routes.js +5 -12
- package/dist/routes/db-routes.js.map +2 -2
- package/dist/routes/messages.d.ts +6 -2
- package/dist/routes/messages.d.ts.map +1 -1
- package/dist/routes/messages.js +17 -3
- package/dist/routes/messages.js.map +2 -2
- package/dist/routes/module-config-routes.d.ts +13 -9
- package/dist/routes/module-config-routes.d.ts.map +1 -1
- package/dist/routes/module-config-routes.js.map +2 -2
- package/dist/routes/modules.d.ts +6 -0
- package/dist/routes/modules.d.ts.map +1 -1
- package/dist/routes/modules.js.map +2 -2
- package/dist/routes/qq-bind.d.ts +24 -0
- package/dist/routes/qq-bind.d.ts.map +1 -0
- package/dist/routes/qq-bind.js +179 -0
- package/dist/routes/qq-bind.js.map +7 -0
- package/dist/routes/qq-events.d.ts +20 -0
- package/dist/routes/qq-events.d.ts.map +1 -0
- package/dist/routes/qq-events.js +39 -0
- package/dist/routes/qq-events.js.map +7 -0
- package/dist/routes/qq-join.d.ts +46 -0
- package/dist/routes/qq-join.d.ts.map +1 -0
- package/dist/routes/qq-join.js +277 -0
- package/dist/routes/qq-join.js.map +7 -0
- package/dist/routes/service-routes.d.ts +6 -0
- package/dist/routes/service-routes.d.ts.map +1 -1
- package/dist/routes/service-routes.js.map +2 -2
- package/dist/routes/status.d.ts +19 -0
- package/dist/routes/status.d.ts.map +1 -0
- package/dist/routes/status.js +74 -0
- package/dist/routes/status.js.map +7 -0
- package/dist/runtime.test.js +216 -4
- package/dist/runtime.test.js.map +2 -2
- package/dist/schema-registry.d.ts +17 -16
- package/dist/schema-registry.d.ts.map +1 -1
- package/dist/schema-registry.js +76 -6
- package/dist/schema-registry.js.map +2 -2
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js.map +2 -2
- package/dist/service-registry.d.ts +39 -9
- package/dist/service-registry.d.ts.map +1 -1
- package/dist/service-registry.js +30 -2
- package/dist/service-registry.js.map +2 -2
- package/dist/services/builtin-handlers.d.ts +27 -7
- package/dist/services/builtin-handlers.d.ts.map +1 -1
- package/dist/services/builtin-handlers.js.map +2 -2
- package/dist/tx-runner.d.ts +48 -9
- package/dist/tx-runner.d.ts.map +1 -1
- package/dist/tx-runner.js +99 -27
- package/dist/tx-runner.js.map +2 -2
- package/package.json +26 -24
package/dist/runtime.test.js
CHANGED
|
@@ -107,9 +107,7 @@ test("jsonV2Ok: \u4E0E Fail \u5BF9\u79F0 ok \u65B9\u8A00(LSP/DRY)", async () =>
|
|
|
107
107
|
test("normalizeOrderBy: SDK field \u4E0E\u9057\u7559 col / \u6570\u7EC4\u4E92\u901A(LSP)", async () => {
|
|
108
108
|
const { normalizeOrderBy } = await import("./lib/order-by.js");
|
|
109
109
|
deepEqual(normalizeOrderBy(void 0), []);
|
|
110
|
-
deepEqual(normalizeOrderBy({ field: "created_at", dir: "desc" }), [
|
|
111
|
-
{ col: "created_at", dir: "desc" }
|
|
112
|
-
]);
|
|
110
|
+
deepEqual(normalizeOrderBy({ field: "created_at", dir: "desc" }), [{ col: "created_at", dir: "desc" }]);
|
|
113
111
|
deepEqual(normalizeOrderBy({ col: "id" }), [{ col: "id", dir: "asc" }]);
|
|
114
112
|
deepEqual(normalizeOrderBy([{ field: "a" }, { col: "b", dir: "desc" }]), [
|
|
115
113
|
{ col: "a", dir: "asc" },
|
|
@@ -236,7 +234,7 @@ test("TxRunner \u4EA4\u4E92\u4F1A\u8BDD: step \u4E2D\u9014\u8BFB\u56DE insert/ge
|
|
|
236
234
|
serviceRegistry: new ServiceRegistry(),
|
|
237
235
|
enabled
|
|
238
236
|
});
|
|
239
|
-
const begin = runner.beginSession("feature-demo");
|
|
237
|
+
const begin = await runner.beginSession("feature-demo");
|
|
240
238
|
equal(begin.ok, true);
|
|
241
239
|
if (!begin.ok) return;
|
|
242
240
|
const { txId } = begin;
|
|
@@ -262,4 +260,218 @@ test("TxRunner \u4EA4\u4E92\u4F1A\u8BDD: step \u4E2D\u9014\u8BFB\u56DE insert/ge
|
|
|
262
260
|
equal(rows[0]?.name, "alpha");
|
|
263
261
|
db.close();
|
|
264
262
|
});
|
|
263
|
+
test("TxRunner \u4EA4\u4E92\u4F1A\u8BDD: \u5E76\u53D1 beginSession \u81EA\u52A8\u6392\u961F\u4E92\u65A5\u6267\u884C", async () => {
|
|
264
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
265
|
+
const { createQuery } = await import("./lib/sqlite.js");
|
|
266
|
+
const { SchemaRegistry } = await import("./schema-registry.js");
|
|
267
|
+
const { TxRunner } = await import("./tx-runner.js");
|
|
268
|
+
const db = new DatabaseSync(":memory:");
|
|
269
|
+
const query = createQuery(db);
|
|
270
|
+
const schema = new SchemaRegistry(db);
|
|
271
|
+
schema.define("feature-demo", {
|
|
272
|
+
name: "queue_items",
|
|
273
|
+
columns: {
|
|
274
|
+
id: { type: "text", primary: true },
|
|
275
|
+
val: { type: "text", notNull: true }
|
|
276
|
+
},
|
|
277
|
+
softDelete: false
|
|
278
|
+
});
|
|
279
|
+
const enabled = /* @__PURE__ */ new Map([
|
|
280
|
+
[
|
|
281
|
+
"feature-demo",
|
|
282
|
+
{
|
|
283
|
+
id: "feature-demo",
|
|
284
|
+
version: "1.0.0",
|
|
285
|
+
permissions: ["db:read:queue_items", "db:write:queue_items"],
|
|
286
|
+
services: { provides: [], requires: [] },
|
|
287
|
+
db: { tables: [] },
|
|
288
|
+
config: { key: "demo" }
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
]);
|
|
292
|
+
const runner = new TxRunner({
|
|
293
|
+
db,
|
|
294
|
+
query,
|
|
295
|
+
schema,
|
|
296
|
+
serviceRegistry: new ServiceRegistry(),
|
|
297
|
+
enabled
|
|
298
|
+
});
|
|
299
|
+
const s1 = await runner.beginSession("feature-demo");
|
|
300
|
+
equal(s1.ok, true);
|
|
301
|
+
if (!s1.ok) return;
|
|
302
|
+
let s2Started = false;
|
|
303
|
+
const s2Promise = runner.beginSession("feature-demo").then((res) => {
|
|
304
|
+
s2Started = true;
|
|
305
|
+
return res;
|
|
306
|
+
});
|
|
307
|
+
equal(s2Started, false);
|
|
308
|
+
const s1Step = await runner.stepSession(s1.txId, "feature-demo", {
|
|
309
|
+
op: "insert",
|
|
310
|
+
table: "queue_items",
|
|
311
|
+
row: { id: "1", val: "one" }
|
|
312
|
+
});
|
|
313
|
+
equal(s1Step.ok, true);
|
|
314
|
+
const s1Commit = runner.commitSession(s1.txId, "feature-demo");
|
|
315
|
+
equal(s1Commit.ok, true);
|
|
316
|
+
const s2 = await s2Promise;
|
|
317
|
+
equal(s2.ok, true);
|
|
318
|
+
equal(s2Started, true);
|
|
319
|
+
if (!s2.ok) return;
|
|
320
|
+
const s2Step = await runner.stepSession(s2.txId, "feature-demo", {
|
|
321
|
+
op: "insert",
|
|
322
|
+
table: "queue_items",
|
|
323
|
+
row: { id: "2", val: "two" }
|
|
324
|
+
});
|
|
325
|
+
equal(s2Step.ok, true);
|
|
326
|
+
const s2Commit = runner.commitSession(s2.txId, "feature-demo");
|
|
327
|
+
equal(s2Commit.ok, true);
|
|
328
|
+
const rows = db.prepare("SELECT val FROM queue_items ORDER BY id ASC").all();
|
|
329
|
+
equal(rows.length, 2);
|
|
330
|
+
equal(rows[0]?.val, "one");
|
|
331
|
+
equal(rows[1]?.val, "two");
|
|
332
|
+
db.close();
|
|
333
|
+
});
|
|
334
|
+
test("TxRunner \u5355\u8FDE\u63A5: run \u4E0E\u4EA4\u4E92\u4F1A\u8BDD\u5171\u7528\u4E8B\u52A1\u961F\u5217", async () => {
|
|
335
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
336
|
+
const { createQuery } = await import("./lib/sqlite.js");
|
|
337
|
+
const { SchemaRegistry } = await import("./schema-registry.js");
|
|
338
|
+
const { TxRunner } = await import("./tx-runner.js");
|
|
339
|
+
const db = new DatabaseSync(":memory:");
|
|
340
|
+
const query = createQuery(db);
|
|
341
|
+
const schema = new SchemaRegistry(db);
|
|
342
|
+
schema.define("feature-demo", {
|
|
343
|
+
name: "queue_items",
|
|
344
|
+
columns: {
|
|
345
|
+
id: { type: "text", primary: true },
|
|
346
|
+
val: { type: "text", notNull: true }
|
|
347
|
+
},
|
|
348
|
+
softDelete: false
|
|
349
|
+
});
|
|
350
|
+
const enabled = /* @__PURE__ */ new Map([
|
|
351
|
+
[
|
|
352
|
+
"feature-demo",
|
|
353
|
+
{
|
|
354
|
+
id: "feature-demo",
|
|
355
|
+
version: "1.0.0",
|
|
356
|
+
permissions: ["db:read:queue_items", "db:write:queue_items"],
|
|
357
|
+
services: { provides: [], requires: [] },
|
|
358
|
+
db: { tables: [] },
|
|
359
|
+
config: { key: "demo" }
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
]);
|
|
363
|
+
const runner = new TxRunner({
|
|
364
|
+
db,
|
|
365
|
+
query,
|
|
366
|
+
schema,
|
|
367
|
+
serviceRegistry: new ServiceRegistry(),
|
|
368
|
+
enabled
|
|
369
|
+
});
|
|
370
|
+
const session = await runner.beginSession("feature-demo");
|
|
371
|
+
equal(session.ok, true);
|
|
372
|
+
if (!session.ok) return;
|
|
373
|
+
let runFinished = false;
|
|
374
|
+
const queuedRun = runner.run({
|
|
375
|
+
moduleId: "feature-demo",
|
|
376
|
+
steps: [{ op: "query", table: "queue_items" }]
|
|
377
|
+
}).then((result) => {
|
|
378
|
+
runFinished = true;
|
|
379
|
+
return result;
|
|
380
|
+
});
|
|
381
|
+
equal(runFinished, false);
|
|
382
|
+
equal(runner.commitSession(session.txId, "feature-demo").ok, true);
|
|
383
|
+
equal((await queuedRun).ok, true);
|
|
384
|
+
equal(runFinished, true);
|
|
385
|
+
const running = runner.run({
|
|
386
|
+
moduleId: "feature-demo",
|
|
387
|
+
steps: [{ op: "query", table: "queue_items" }]
|
|
388
|
+
});
|
|
389
|
+
let sessionStarted = false;
|
|
390
|
+
const queuedSession = runner.beginSession("feature-demo").then((result) => {
|
|
391
|
+
sessionStarted = true;
|
|
392
|
+
return result;
|
|
393
|
+
});
|
|
394
|
+
equal(sessionStarted, false);
|
|
395
|
+
equal((await running).ok, true);
|
|
396
|
+
const nextSession = await queuedSession;
|
|
397
|
+
equal(nextSession.ok, true);
|
|
398
|
+
equal(sessionStarted, true);
|
|
399
|
+
if (nextSession.ok) equal(runner.rollbackSession(nextSession.txId, "feature-demo").ok, true);
|
|
400
|
+
db.close();
|
|
401
|
+
});
|
|
402
|
+
test("TxRunner \u4F1A\u56DE\u6536\u7A7A\u95F2\u4EA4\u4E92\u4E8B\u52A1\u5E76\u91CA\u653E\u7B49\u5F85\u8005", async () => {
|
|
403
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
404
|
+
const { createQuery } = await import("./lib/sqlite.js");
|
|
405
|
+
const { SchemaRegistry } = await import("./schema-registry.js");
|
|
406
|
+
const { TxRunner } = await import("./tx-runner.js");
|
|
407
|
+
const db = new DatabaseSync(":memory:");
|
|
408
|
+
const schema = new SchemaRegistry(db);
|
|
409
|
+
const enabled = /* @__PURE__ */ new Map([
|
|
410
|
+
[
|
|
411
|
+
"feature-demo",
|
|
412
|
+
{
|
|
413
|
+
id: "feature-demo",
|
|
414
|
+
version: "1.0.0",
|
|
415
|
+
permissions: [],
|
|
416
|
+
services: { provides: [], requires: [] },
|
|
417
|
+
db: { tables: [] },
|
|
418
|
+
config: { key: "demo" }
|
|
419
|
+
}
|
|
420
|
+
]
|
|
421
|
+
]);
|
|
422
|
+
const runner = new TxRunner(
|
|
423
|
+
{
|
|
424
|
+
db,
|
|
425
|
+
query: createQuery(db),
|
|
426
|
+
schema,
|
|
427
|
+
serviceRegistry: new ServiceRegistry(),
|
|
428
|
+
enabled
|
|
429
|
+
},
|
|
430
|
+
{ sessionIdleTimeoutMs: 20, slotWaitTimeoutMs: 200 }
|
|
431
|
+
);
|
|
432
|
+
const orphan = await runner.beginSession("feature-demo");
|
|
433
|
+
equal(orphan.ok, true);
|
|
434
|
+
const recovered = await runner.beginSession("feature-demo");
|
|
435
|
+
equal(recovered.ok, true);
|
|
436
|
+
if (recovered.ok) equal(runner.rollbackSession(recovered.txId, "feature-demo").ok, true);
|
|
437
|
+
db.close();
|
|
438
|
+
});
|
|
439
|
+
test("TxRunner \u4E8B\u52A1\u69FD\u7B49\u5F85\u8D85\u65F6\u8FD4\u56DE transaction_busy", async () => {
|
|
440
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
441
|
+
const { createQuery } = await import("./lib/sqlite.js");
|
|
442
|
+
const { SchemaRegistry } = await import("./schema-registry.js");
|
|
443
|
+
const { TxRunner } = await import("./tx-runner.js");
|
|
444
|
+
const db = new DatabaseSync(":memory:");
|
|
445
|
+
const schema = new SchemaRegistry(db);
|
|
446
|
+
const enabled = /* @__PURE__ */ new Map([
|
|
447
|
+
[
|
|
448
|
+
"feature-demo",
|
|
449
|
+
{
|
|
450
|
+
id: "feature-demo",
|
|
451
|
+
version: "1.0.0",
|
|
452
|
+
permissions: [],
|
|
453
|
+
services: { provides: [], requires: [] },
|
|
454
|
+
db: { tables: [] },
|
|
455
|
+
config: { key: "demo" }
|
|
456
|
+
}
|
|
457
|
+
]
|
|
458
|
+
]);
|
|
459
|
+
const runner = new TxRunner(
|
|
460
|
+
{
|
|
461
|
+
db,
|
|
462
|
+
query: createQuery(db),
|
|
463
|
+
schema,
|
|
464
|
+
serviceRegistry: new ServiceRegistry(),
|
|
465
|
+
enabled
|
|
466
|
+
},
|
|
467
|
+
{ sessionIdleTimeoutMs: 200, slotWaitTimeoutMs: 20 }
|
|
468
|
+
);
|
|
469
|
+
const owner = await runner.beginSession("feature-demo");
|
|
470
|
+
equal(owner.ok, true);
|
|
471
|
+
const blocked = await runner.beginSession("feature-demo");
|
|
472
|
+
equal(blocked.ok, false);
|
|
473
|
+
if (!blocked.ok) equal(blocked.code, "transaction_busy");
|
|
474
|
+
if (owner.ok) equal(runner.rollbackSession(owner.txId, "feature-demo").ok, true);
|
|
475
|
+
db.close();
|
|
476
|
+
});
|
|
265
477
|
//# sourceMappingURL=runtime.test.js.map
|
package/dist/runtime.test.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/runtime.test.ts"],
|
|
4
|
-
"sourcesContent": ["import { deepEqual, equal, rejects, throws } from \"node:assert/strict\";\nimport test from \"node:test\";\nimport { SQL } from \"sql-template-strings\";\nimport { assertIdentifier, quoteIdentifier } from \"./lib/identifiers.js\";\nimport { parseNodeVersion } from \"./lib/runtime.js\";\nimport { raw, sql } from \"./lib/sql-helpers.js\";\nimport { DispatchError, ServiceRegistry } from \"./service-registry.js\";\n\ntest(\"parses Node versions\", () => {\n deepEqual(parseNodeVersion(\"22.5.1\"), { major: 22, minor: 5, patch: 1 });\n deepEqual(parseNodeVersion(\"v22.5.1\"), { major: 22, minor: 5, patch: 1 });\n});\n\ntest(\"accepts only safe SQL identifiers\", () => {\n equal(quoteIdentifier(\"sfmc_players\", \"table\"), '\"sfmc_players\"');\n throws(() => assertIdentifier(\"users; DROP TABLE x\"), /Invalid SQL/);\n});\n\ntest(\"sql() embeds trusted identifiers and binds values\", () => {\n const TABLE = \"sfmc_economy_accounts\";\n const q = sql(`SELECT * FROM ${TABLE} WHERE player_id = ?`, [\"p1\"]);\n equal(q.sql, \"SELECT * FROM sfmc_economy_accounts WHERE player_id = ?\");\n deepEqual(q.values, [\"p1\"]);\n});\n\ntest(\"raw() only works via append \u2014 template interpolation still becomes ?\", () => {\n const TABLE = \"sfmc_economy_accounts\";\n const broken = SQL`SELECT * FROM ${raw(TABLE)} WHERE id = ${\"p1\"}`;\n equal(broken.sql, \"SELECT * FROM ? WHERE id = ?\");\n\n const ok = SQL`SELECT * FROM `.append(raw(TABLE)).append(SQL` WHERE id = ${\"p1\"}`);\n equal(ok.sql, \"SELECT * FROM sfmc_economy_accounts WHERE id = ?\");\n deepEqual(ok.values, [\"p1\"]);\n});\n\ntest(\"ServiceRegistry: provider self-call skips requires (LSP with tx.call path)\", async () => {\n const reg = new ServiceRegistry();\n reg.registerHandler(\"feature-economy\", \"economy.stats.monthly\", async () => ({ ok: true }));\n\n const enabled = new Map([\n [\n \"feature-economy\",\n {\n id: \"feature-economy\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [{ name: \"economy.stats.monthly\" }], requires: [] as Array<{ name: string }> },\n db: { tables: [] as unknown[] },\n config: { key: \"economy\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const out = await reg.dispatch(enabled, \"feature-economy\", \"economy.stats.monthly\", {});\n deepEqual(out, { ok: true, result: { ok: true } });\n});\n\ntest(\"ServiceRegistry: missing service \u2192 no_such_service (not not_in_requires)\", async () => {\n const reg = new ServiceRegistry();\n const enabled = new Map([\n [\n \"feature-chat\",\n {\n id: \"feature-chat\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"chat\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n await rejects(\n () => reg.dispatch(enabled, \"feature-chat\", \"economy.account.get\", {}),\n (e: unknown) => e instanceof DispatchError && e.code === \"no_such_service\"\n );\n});\n\ntest(\"jsonV2Fail: ok \u65B9\u8A00 + extra(step) \u5408\u5E76(LSP/DRY)\", async () => {\n const { jsonV2Fail } = await import(\"./routes/_shared.js\");\n const chunks: Buffer[] = [];\n let statusCode = 0;\n const res = {\n writeHead(code: number) {\n statusCode = code;\n },\n end(body?: string) {\n if (body) chunks.push(Buffer.from(body));\n },\n setHeader() {},\n } as unknown as import(\"node:http\").ServerResponse;\n\n jsonV2Fail(res, \"boom\", 400, \"bad_step\", { step: 2 });\n const payload = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n equal(statusCode, 400);\n deepEqual(payload, { ok: false, error: \"boom\", code: \"bad_step\", step: 2 });\n});\n\ntest(\"jsonV2Ok: \u4E0E Fail \u5BF9\u79F0 ok \u65B9\u8A00(LSP/DRY)\", async () => {\n const { jsonV2Ok } = await import(\"./routes/_shared.js\");\n const chunks: Buffer[] = [];\n let statusCode = 0;\n const res = {\n writeHead(code: number) {\n statusCode = code;\n },\n end(body?: string) {\n if (body) chunks.push(Buffer.from(body));\n },\n setHeader() {},\n } as unknown as import(\"node:http\").ServerResponse;\n\n jsonV2Ok(res, { rows: [1] });\n const payload = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n equal(statusCode, 200);\n deepEqual(payload, { ok: true, rows: [1] });\n});\n\ntest(\"normalizeOrderBy: SDK field \u4E0E\u9057\u7559 col / \u6570\u7EC4\u4E92\u901A(LSP)\", async () => {\n const { normalizeOrderBy } = await import(\"./lib/order-by.js\");\n deepEqual(normalizeOrderBy(undefined), []);\n deepEqual(normalizeOrderBy({ field: \"created_at\", dir: \"desc\" }), [\n { col: \"created_at\", dir: \"desc\" },\n ]);\n deepEqual(normalizeOrderBy({ col: \"id\" }), [{ col: \"id\", dir: \"asc\" }]);\n deepEqual(normalizeOrderBy([{ field: \"a\" }, { col: \"b\", dir: \"desc\" }]), [\n { col: \"a\", dir: \"asc\" },\n { col: \"b\", dir: \"desc\" },\n ]);\n throws(() => normalizeOrderBy({ dir: \"asc\" }), /field\\/col/);\n});\n\ntest(\"syncModuleRuntimeState: enable/disable \u70ED\u66F4\u65B0 token+enabledSet(DIP)\", async () => {\n const { mkdtempSync, rmSync, readFileSync } = await import(\"node:fs\");\n const { join } = await import(\"node:path\");\n const { tmpdir } = await import(\"node:os\");\n const { syncModuleRuntimeState } = await import(\"./module-runtime-sync.js\");\n const { deriveToken } = await import(\"./module-auth.js\");\n const { unregisterBuiltinPluginForModule } = await import(\"./services/builtin-handlers.js\");\n\n const root = mkdtempSync(join(tmpdir(), \"sfmc-runtime-sync-\"));\n const dbPath = join(root, \"data\", \"sfmc_data.db\");\n try {\n const enabledSet = new Set<string>([\"feature-a\"]);\n const enabledManifests = new Map();\n const moduleAuth = { tokens: { \"feature-a\": \"old\" } as Record<string, string>, secret: \"test-secret\" };\n const registry = new ServiceRegistry();\n const fakeManifest = {\n id: \"feature-b\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"b\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2;\n\n syncModuleRuntimeState({\n moduleId: \"feature-b\",\n enabled: true,\n dbPath,\n envAuthToken: \"fixed-auth\",\n enabledSet,\n enabledManifests,\n loadedManifest: { modules: { \"feature-b\": fakeManifest } },\n moduleAuth,\n serviceRegistry: registry,\n builtinDeps: { query: (() => []) as never, db: {} as never },\n });\n\n equal(enabledSet.has(\"feature-b\"), true);\n equal(enabledManifests.has(\"feature-b\"), true);\n equal(moduleAuth.tokens[\"feature-b\"], deriveToken(\"feature-b\", \"test-secret\"));\n const store = JSON.parse(readFileSync(join(root, \"data\", \"module-tokens.json\"), \"utf8\"));\n equal(store.tokens[\"feature-b\"], moduleAuth.tokens[\"feature-b\"]);\n\n syncModuleRuntimeState({\n moduleId: \"feature-b\",\n enabled: false,\n dbPath,\n envAuthToken: \"fixed-auth\",\n enabledSet,\n enabledManifests,\n loadedManifest: { modules: { \"feature-b\": fakeManifest } },\n moduleAuth,\n serviceRegistry: registry,\n builtinDeps: { query: (() => []) as never, db: {} as never },\n });\n\n equal(enabledSet.has(\"feature-b\"), false);\n equal(enabledManifests.has(\"feature-b\"), false);\n equal(moduleAuth.tokens[\"feature-b\"], undefined);\n equal(unregisterBuiltinPluginForModule(registry, \"feature-b\"), 0);\n } finally {\n rmSync(root, { recursive: true, force: true });\n }\n});\n\ntest(\"module-auth: ensureModuleToken / revoke \u590D\u7528 secret(DRY)\", async () => {\n const { deriveToken, ensureModuleToken, revokeModuleToken } = await import(\"./module-auth.js\");\n const secret = \"test-secret\";\n const auth = { secret, tokens: {} as Record<string, string> };\n equal(ensureModuleToken(auth, \"feature-afk\"), true);\n equal(auth.tokens[\"feature-afk\"], deriveToken(\"feature-afk\", secret));\n equal(ensureModuleToken(auth, \"feature-afk\"), false);\n equal(revokeModuleToken(auth, \"feature-afk\"), true);\n equal(auth.tokens[\"feature-afk\"], undefined);\n equal(revokeModuleToken(auth, \"feature-afk\"), false);\n});\n\ntest(\"builtin-handlers: \u70ED\u7981\u7528\u6309 moduleId \u5378\u8F7D(DRY/OCP)\", async () => {\n const { unregisterBuiltinPluginForModule, registerBuiltinPluginForModule } = await import(\n \"./services/builtin-handlers.js\"\n );\n\n const reg = new ServiceRegistry();\n reg.registerHandler(\"feature-economy\", \"economy.account.get\", async () => ({}));\n reg.registerHandler(\"feature-economy\", \"economy.account.debit\", async () => ({}));\n reg.registerHandler(\"other-mod\", \"other.ping\", async () => ({}));\n equal(unregisterBuiltinPluginForModule(reg, \"feature-economy\"), 2);\n equal(reg.list().length, 1);\n equal(reg.list()[0]?.moduleId, \"other-mod\");\n equal(unregisterBuiltinPluginForModule(reg, \"feature-unknown\"), 0);\n\n // \u5DF2\u6709\u540C moduleId handler \u65F6\u70ED\u542F\u7528\u8DF3\u8FC7\n equal(registerBuiltinPluginForModule(reg, { query: (() => []) as never, db: {} as never }, \"feature-economy\"), true);\n equal(registerBuiltinPluginForModule(reg, { query: (() => []) as never, db: {} as never }, \"feature-economy\"), false);\n});\n\ntest(\"TxRunner \u4EA4\u4E92\u4F1A\u8BDD: step \u4E2D\u9014\u8BFB\u56DE insert/get(PR #31 leftover)\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const query = createQuery(db);\n const schema = new SchemaRegistry(db);\n schema.define(\"feature-demo\", {\n name: \"demo_items\",\n columns: {\n id: { type: \"text\", primary: true },\n name: { type: \"text\", notNull: true },\n },\n softDelete: false,\n });\n\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [\"db:read:demo_items\", \"db:write:demo_items\"],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const runner = new TxRunner({\n db,\n query,\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n });\n\n const begin = runner.beginSession(\"feature-demo\");\n equal(begin.ok, true);\n if (!begin.ok) return;\n const { txId } = begin;\n\n const ins = await runner.stepSession(txId, \"feature-demo\", {\n op: \"insert\",\n table: \"demo_items\",\n row: { id: \"a1\", name: \"alpha\" },\n });\n equal(ins.ok, true);\n if (!ins.ok) return;\n equal((ins.result as { op: string; row: { id: string } }).row.id, \"a1\");\n\n const got = await runner.stepSession(txId, \"feature-demo\", {\n op: \"get\",\n table: \"demo_items\",\n id: \"a1\",\n });\n equal(got.ok, true);\n if (!got.ok) return;\n equal((got.result as { row: { name: string } | null }).row?.name, \"alpha\");\n\n const committed = runner.commitSession(txId, \"feature-demo\");\n equal(committed.ok, true);\n\n const rows = db.prepare(\"SELECT name FROM demo_items WHERE id = ?\").all(\"a1\") as Array<{ name: string }>;\n equal(rows[0]?.name, \"alpha\");\n db.close();\n});\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { deepEqual, equal, rejects, throws } from \"node:assert/strict\";\nimport test from \"node:test\";\nimport { SQL } from \"sql-template-strings\";\nimport { assertIdentifier, quoteIdentifier } from \"./lib/identifiers.js\";\nimport { parseNodeVersion } from \"./lib/runtime.js\";\nimport { raw, sql } from \"./lib/sql-helpers.js\";\nimport { DispatchError, ServiceRegistry } from \"./service-registry.js\";\n\ntest(\"parses Node versions\", () => {\n deepEqual(parseNodeVersion(\"22.5.1\"), { major: 22, minor: 5, patch: 1 });\n deepEqual(parseNodeVersion(\"v22.5.1\"), { major: 22, minor: 5, patch: 1 });\n});\n\ntest(\"accepts only safe SQL identifiers\", () => {\n equal(quoteIdentifier(\"sfmc_players\", \"table\"), '\"sfmc_players\"');\n throws(() => assertIdentifier(\"users; DROP TABLE x\"), /Invalid SQL/);\n});\n\ntest(\"sql() embeds trusted identifiers and binds values\", () => {\n const TABLE = \"sfmc_economy_accounts\";\n const q = sql(`SELECT * FROM ${TABLE} WHERE player_id = ?`, [\"p1\"]);\n equal(q.sql, \"SELECT * FROM sfmc_economy_accounts WHERE player_id = ?\");\n deepEqual(q.values, [\"p1\"]);\n});\n\ntest(\"raw() only works via append \u2014 template interpolation still becomes ?\", () => {\n const TABLE = \"sfmc_economy_accounts\";\n const broken = SQL`SELECT * FROM ${raw(TABLE)} WHERE id = ${\"p1\"}`;\n equal(broken.sql, \"SELECT * FROM ? WHERE id = ?\");\n\n const ok = SQL`SELECT * FROM `.append(raw(TABLE)).append(SQL` WHERE id = ${\"p1\"}`);\n equal(ok.sql, \"SELECT * FROM sfmc_economy_accounts WHERE id = ?\");\n deepEqual(ok.values, [\"p1\"]);\n});\n\ntest(\"ServiceRegistry: provider self-call skips requires (LSP with tx.call path)\", async () => {\n const reg = new ServiceRegistry();\n reg.registerHandler(\"feature-economy\", \"economy.stats.monthly\", async () => ({ ok: true }));\n\n const enabled = new Map([\n [\n \"feature-economy\",\n {\n id: \"feature-economy\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [{ name: \"economy.stats.monthly\" }], requires: [] as Array<{ name: string }> },\n db: { tables: [] as unknown[] },\n config: { key: \"economy\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const out = await reg.dispatch(enabled, \"feature-economy\", \"economy.stats.monthly\", {});\n deepEqual(out, { ok: true, result: { ok: true } });\n});\n\ntest(\"ServiceRegistry: missing service \u2192 no_such_service (not not_in_requires)\", async () => {\n const reg = new ServiceRegistry();\n const enabled = new Map([\n [\n \"feature-chat\",\n {\n id: \"feature-chat\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"chat\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n await rejects(\n () => reg.dispatch(enabled, \"feature-chat\", \"economy.account.get\", {}),\n (e: unknown) => e instanceof DispatchError && e.code === \"no_such_service\"\n );\n});\n\ntest(\"jsonV2Fail: ok \u65B9\u8A00 + extra(step) \u5408\u5E76(LSP/DRY)\", async () => {\n const { jsonV2Fail } = await import(\"./routes/_shared.js\");\n const chunks: Buffer[] = [];\n let statusCode = 0;\n const res = {\n writeHead(code: number) {\n statusCode = code;\n },\n end(body?: string) {\n if (body) chunks.push(Buffer.from(body));\n },\n setHeader() {},\n } as unknown as import(\"node:http\").ServerResponse;\n\n jsonV2Fail(res, \"boom\", 400, \"bad_step\", { step: 2 });\n const payload = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n equal(statusCode, 400);\n deepEqual(payload, { ok: false, error: \"boom\", code: \"bad_step\", step: 2 });\n});\n\ntest(\"jsonV2Ok: \u4E0E Fail \u5BF9\u79F0 ok \u65B9\u8A00(LSP/DRY)\", async () => {\n const { jsonV2Ok } = await import(\"./routes/_shared.js\");\n const chunks: Buffer[] = [];\n let statusCode = 0;\n const res = {\n writeHead(code: number) {\n statusCode = code;\n },\n end(body?: string) {\n if (body) chunks.push(Buffer.from(body));\n },\n setHeader() {},\n } as unknown as import(\"node:http\").ServerResponse;\n\n jsonV2Ok(res, { rows: [1] });\n const payload = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n equal(statusCode, 200);\n deepEqual(payload, { ok: true, rows: [1] });\n});\n\ntest(\"normalizeOrderBy: SDK field \u4E0E\u9057\u7559 col / \u6570\u7EC4\u4E92\u901A(LSP)\", async () => {\n const { normalizeOrderBy } = await import(\"./lib/order-by.js\");\n deepEqual(normalizeOrderBy(undefined), []);\n deepEqual(normalizeOrderBy({ field: \"created_at\", dir: \"desc\" }), [{ col: \"created_at\", dir: \"desc\" }]);\n deepEqual(normalizeOrderBy({ col: \"id\" }), [{ col: \"id\", dir: \"asc\" }]);\n deepEqual(normalizeOrderBy([{ field: \"a\" }, { col: \"b\", dir: \"desc\" }]), [\n { col: \"a\", dir: \"asc\" },\n { col: \"b\", dir: \"desc\" },\n ]);\n throws(() => normalizeOrderBy({ dir: \"asc\" }), /field\\/col/);\n});\n\ntest(\"syncModuleRuntimeState: enable/disable \u70ED\u66F4\u65B0 token+enabledSet(DIP)\", async () => {\n const { mkdtempSync, rmSync, readFileSync } = await import(\"node:fs\");\n const { join } = await import(\"node:path\");\n const { tmpdir } = await import(\"node:os\");\n const { syncModuleRuntimeState } = await import(\"./module-runtime-sync.js\");\n const { deriveToken } = await import(\"./module-auth.js\");\n const { unregisterBuiltinPluginForModule } = await import(\"./services/builtin-handlers.js\");\n\n const root = mkdtempSync(join(tmpdir(), \"sfmc-runtime-sync-\"));\n const dbPath = join(root, \"data\", \"sfmc_data.db\");\n try {\n const enabledSet = new Set<string>([\"feature-a\"]);\n const enabledManifests = new Map();\n const moduleAuth = { tokens: { \"feature-a\": \"old\" } as Record<string, string>, secret: \"test-secret\" };\n const registry = new ServiceRegistry();\n const fakeManifest = {\n id: \"feature-b\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"b\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2;\n\n syncModuleRuntimeState({\n moduleId: \"feature-b\",\n enabled: true,\n dbPath,\n envAuthToken: \"fixed-auth\",\n enabledSet,\n enabledManifests,\n loadedManifest: { modules: { \"feature-b\": fakeManifest } },\n moduleAuth,\n serviceRegistry: registry,\n builtinDeps: { query: (() => []) as never, db: {} as never },\n });\n\n equal(enabledSet.has(\"feature-b\"), true);\n equal(enabledManifests.has(\"feature-b\"), true);\n equal(moduleAuth.tokens[\"feature-b\"], deriveToken(\"feature-b\", \"test-secret\"));\n const store = JSON.parse(readFileSync(join(root, \"data\", \"module-tokens.json\"), \"utf8\"));\n equal(store.tokens[\"feature-b\"], moduleAuth.tokens[\"feature-b\"]);\n\n syncModuleRuntimeState({\n moduleId: \"feature-b\",\n enabled: false,\n dbPath,\n envAuthToken: \"fixed-auth\",\n enabledSet,\n enabledManifests,\n loadedManifest: { modules: { \"feature-b\": fakeManifest } },\n moduleAuth,\n serviceRegistry: registry,\n builtinDeps: { query: (() => []) as never, db: {} as never },\n });\n\n equal(enabledSet.has(\"feature-b\"), false);\n equal(enabledManifests.has(\"feature-b\"), false);\n equal(moduleAuth.tokens[\"feature-b\"], undefined);\n equal(unregisterBuiltinPluginForModule(registry, \"feature-b\"), 0);\n } finally {\n rmSync(root, { recursive: true, force: true });\n }\n});\n\ntest(\"module-auth: ensureModuleToken / revoke \u590D\u7528 secret(DRY)\", async () => {\n const { deriveToken, ensureModuleToken, revokeModuleToken } = await import(\"./module-auth.js\");\n const secret = \"test-secret\";\n const auth = { secret, tokens: {} as Record<string, string> };\n equal(ensureModuleToken(auth, \"feature-afk\"), true);\n equal(auth.tokens[\"feature-afk\"], deriveToken(\"feature-afk\", secret));\n equal(ensureModuleToken(auth, \"feature-afk\"), false);\n equal(revokeModuleToken(auth, \"feature-afk\"), true);\n equal(auth.tokens[\"feature-afk\"], undefined);\n equal(revokeModuleToken(auth, \"feature-afk\"), false);\n});\n\ntest(\"builtin-handlers: \u70ED\u7981\u7528\u6309 moduleId \u5378\u8F7D(DRY/OCP)\", async () => {\n const { unregisterBuiltinPluginForModule, registerBuiltinPluginForModule } =\n await import(\"./services/builtin-handlers.js\");\n\n const reg = new ServiceRegistry();\n reg.registerHandler(\"feature-economy\", \"economy.account.get\", async () => ({}));\n reg.registerHandler(\"feature-economy\", \"economy.account.debit\", async () => ({}));\n reg.registerHandler(\"other-mod\", \"other.ping\", async () => ({}));\n equal(unregisterBuiltinPluginForModule(reg, \"feature-economy\"), 2);\n equal(reg.list().length, 1);\n equal(reg.list()[0]?.moduleId, \"other-mod\");\n equal(unregisterBuiltinPluginForModule(reg, \"feature-unknown\"), 0);\n\n // \u5DF2\u6709\u540C moduleId handler \u65F6\u70ED\u542F\u7528\u8DF3\u8FC7\n equal(registerBuiltinPluginForModule(reg, { query: (() => []) as never, db: {} as never }, \"feature-economy\"), true);\n equal(registerBuiltinPluginForModule(reg, { query: (() => []) as never, db: {} as never }, \"feature-economy\"), false);\n});\n\ntest(\"TxRunner \u4EA4\u4E92\u4F1A\u8BDD: step \u4E2D\u9014\u8BFB\u56DE insert/get(PR #31 leftover)\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const query = createQuery(db);\n const schema = new SchemaRegistry(db);\n schema.define(\"feature-demo\", {\n name: \"demo_items\",\n columns: {\n id: { type: \"text\", primary: true },\n name: { type: \"text\", notNull: true },\n },\n softDelete: false,\n });\n\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [\"db:read:demo_items\", \"db:write:demo_items\"],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const runner = new TxRunner({\n db,\n query,\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n });\n\n const begin = await runner.beginSession(\"feature-demo\");\n equal(begin.ok, true);\n if (!begin.ok) return;\n const { txId } = begin;\n\n const ins = await runner.stepSession(txId, \"feature-demo\", {\n op: \"insert\",\n table: \"demo_items\",\n row: { id: \"a1\", name: \"alpha\" },\n });\n equal(ins.ok, true);\n if (!ins.ok) return;\n equal((ins.result as { op: string; row: { id: string } }).row.id, \"a1\");\n\n const got = await runner.stepSession(txId, \"feature-demo\", {\n op: \"get\",\n table: \"demo_items\",\n id: \"a1\",\n });\n equal(got.ok, true);\n if (!got.ok) return;\n equal((got.result as { row: { name: string } | null }).row?.name, \"alpha\");\n\n const committed = runner.commitSession(txId, \"feature-demo\");\n equal(committed.ok, true);\n\n const rows = db.prepare(\"SELECT name FROM demo_items WHERE id = ?\").all(\"a1\") as Array<{ name: string }>;\n equal(rows[0]?.name, \"alpha\");\n db.close();\n});\n\ntest(\"TxRunner \u4EA4\u4E92\u4F1A\u8BDD: \u5E76\u53D1 beginSession \u81EA\u52A8\u6392\u961F\u4E92\u65A5\u6267\u884C\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const query = createQuery(db);\n const schema = new SchemaRegistry(db);\n schema.define(\"feature-demo\", {\n name: \"queue_items\",\n columns: {\n id: { type: \"text\", primary: true },\n val: { type: \"text\", notNull: true },\n },\n softDelete: false,\n });\n\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [\"db:read:queue_items\", \"db:write:queue_items\"],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const runner = new TxRunner({\n db,\n query,\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n });\n\n // Session 1 \u542F\u52A8\n const s1 = await runner.beginSession(\"feature-demo\");\n equal(s1.ok, true);\n if (!s1.ok) return;\n\n // Session 2 \u5E76\u53D1\u542F\u52A8\uFF08\u4F1A\u5F02\u6B65\u6392\u961F\u7B49\u5F85 Session 1 \u5B8C\u6210\uFF09\n let s2Started = false;\n const s2Promise = runner.beginSession(\"feature-demo\").then((res) => {\n s2Started = true;\n return res;\n });\n\n // \u6B64\u65F6 Session 2 \u5E94\u5904\u4E8E\u7B49\u5F85\u4E2D\uFF0C\u672A\u8FDB\u5165\n equal(s2Started, false);\n\n // Session 1 \u63D2\u5165\u6B65\u9AA4\n const s1Step = await runner.stepSession(s1.txId, \"feature-demo\", {\n op: \"insert\",\n table: \"queue_items\",\n row: { id: \"1\", val: \"one\" },\n });\n equal(s1Step.ok, true);\n\n // Session 1 \u63D0\u4EA4\n const s1Commit = runner.commitSession(s1.txId, \"feature-demo\");\n equal(s1Commit.ok, true);\n\n // Session 1 \u63D0\u4EA4\u540E\uFF0CSession 2 \u5E94\u8BE5\u88AB\u5524\u9192\u5E76\u6210\u529F\u83B7\u5F97\u4E8B\u52A1\n const s2 = await s2Promise;\n equal(s2.ok, true);\n equal(s2Started, true);\n if (!s2.ok) return;\n\n // Session 2 \u63D2\u5165\u6B65\u9AA4\u5E76\u63D0\u4EA4\n const s2Step = await runner.stepSession(s2.txId, \"feature-demo\", {\n op: \"insert\",\n table: \"queue_items\",\n row: { id: \"2\", val: \"two\" },\n });\n equal(s2Step.ok, true);\n\n const s2Commit = runner.commitSession(s2.txId, \"feature-demo\");\n equal(s2Commit.ok, true);\n\n const rows = db.prepare(\"SELECT val FROM queue_items ORDER BY id ASC\").all() as Array<{ val: string }>;\n equal(rows.length, 2);\n equal(rows[0]?.val, \"one\");\n equal(rows[1]?.val, \"two\");\n db.close();\n});\n\ntest(\"TxRunner \u5355\u8FDE\u63A5: run \u4E0E\u4EA4\u4E92\u4F1A\u8BDD\u5171\u7528\u4E8B\u52A1\u961F\u5217\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const query = createQuery(db);\n const schema = new SchemaRegistry(db);\n schema.define(\"feature-demo\", {\n name: \"queue_items\",\n columns: {\n id: { type: \"text\", primary: true },\n val: { type: \"text\", notNull: true },\n },\n softDelete: false,\n });\n\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [\"db:read:queue_items\", \"db:write:queue_items\"],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const runner = new TxRunner({\n db,\n query,\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n });\n\n const session = await runner.beginSession(\"feature-demo\");\n equal(session.ok, true);\n if (!session.ok) return;\n\n let runFinished = false;\n const queuedRun = runner\n .run({\n moduleId: \"feature-demo\",\n steps: [{ op: \"query\", table: \"queue_items\" }],\n })\n .then((result) => {\n runFinished = true;\n return result;\n });\n equal(runFinished, false);\n\n equal(runner.commitSession(session.txId, \"feature-demo\").ok, true);\n equal((await queuedRun).ok, true);\n equal(runFinished, true);\n\n const running = runner.run({\n moduleId: \"feature-demo\",\n steps: [{ op: \"query\", table: \"queue_items\" }],\n });\n let sessionStarted = false;\n const queuedSession = runner.beginSession(\"feature-demo\").then((result) => {\n sessionStarted = true;\n return result;\n });\n equal(sessionStarted, false);\n equal((await running).ok, true);\n\n const nextSession = await queuedSession;\n equal(nextSession.ok, true);\n equal(sessionStarted, true);\n if (nextSession.ok) equal(runner.rollbackSession(nextSession.txId, \"feature-demo\").ok, true);\n\n db.close();\n});\n\ntest(\"TxRunner \u4F1A\u56DE\u6536\u7A7A\u95F2\u4EA4\u4E92\u4E8B\u52A1\u5E76\u91CA\u653E\u7B49\u5F85\u8005\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const schema = new SchemaRegistry(db);\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n const runner = new TxRunner(\n {\n db,\n query: createQuery(db),\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n },\n { sessionIdleTimeoutMs: 20, slotWaitTimeoutMs: 200 }\n );\n\n const orphan = await runner.beginSession(\"feature-demo\");\n equal(orphan.ok, true);\n const recovered = await runner.beginSession(\"feature-demo\");\n equal(recovered.ok, true);\n if (recovered.ok) equal(runner.rollbackSession(recovered.txId, \"feature-demo\").ok, true);\n db.close();\n});\n\ntest(\"TxRunner \u4E8B\u52A1\u69FD\u7B49\u5F85\u8D85\u65F6\u8FD4\u56DE transaction_busy\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const schema = new SchemaRegistry(db);\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n const runner = new TxRunner(\n {\n db,\n query: createQuery(db),\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n },\n { sessionIdleTimeoutMs: 200, slotWaitTimeoutMs: 20 }\n );\n\n const owner = await runner.beginSession(\"feature-demo\");\n equal(owner.ok, true);\n const blocked = await runner.beginSession(\"feature-demo\");\n equal(blocked.ok, false);\n if (!blocked.ok) equal(blocked.code, \"transaction_busy\");\n if (owner.ok) equal(runner.rollbackSession(owner.txId, \"feature-demo\").ok, true);\n db.close();\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* schema-registry.ts —
|
|
2
|
+
* schema-registry.ts — 动态数据表结构注册与创建管理器
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - 等所有"已注册模块"都调过(超时 fallback 或 explicit `finalize()`),
|
|
9
|
-
* 一次性 CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS
|
|
10
|
-
* - 同一 module 重复 define 同名表 = 幂等(同名 columns 允许)
|
|
11
|
-
* - 不同 module 抢同名表 = 启动失败
|
|
12
|
-
* - 平台表(moduleId = "__platform__")由 db-tables.ts 在 initSchema 直接 bootstrap
|
|
4
|
+
* 协议与流程:
|
|
5
|
+
* 1. 模块启动时调用 `db.defineTable(name, columns, { softDelete })` 向服务端注册表结构定义。
|
|
6
|
+
* 2. `SchemaRegistry` 校验列定义合法性、外键引用与主键唯一性。
|
|
7
|
+
* 3. 校验通过后自动执行 `CREATE TABLE IF NOT EXISTS` 及索引创建语句。
|
|
13
8
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
9
|
+
* 冲突与隔离控制:
|
|
10
|
+
* - 相同模块重复定义同名表:具备幂等性(要求列结构完全一致)
|
|
11
|
+
* - 不同模块定义相同表名:抛出冲突异常拒绝注册
|
|
12
|
+
* - 软删除支持:开启 `softDelete`(默认开启)时自动在末尾统一追加 `_deleted_at` 与乐观锁 `_version`
|
|
18
13
|
*/
|
|
19
14
|
import type { DatabaseSync } from "node:sqlite";
|
|
20
|
-
export type ColumnType = "text" | "integer" | "real" | "blob";
|
|
15
|
+
export type ColumnType = "text" | "integer" | "real" | "blob" | "TEXT" | "INTEGER" | "REAL" | "BLOB";
|
|
21
16
|
export interface ColumnDef {
|
|
22
17
|
type: ColumnType;
|
|
23
18
|
primary?: boolean;
|
|
@@ -44,7 +39,13 @@ export declare class SchemaRegistry {
|
|
|
44
39
|
private readonly tables;
|
|
45
40
|
private finalized;
|
|
46
41
|
constructor(db: DatabaseSync);
|
|
47
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* 注册并创建模块数据表结构。
|
|
44
|
+
*
|
|
45
|
+
* @param moduleId 注册模块唯一标识符。
|
|
46
|
+
* @param req 表结构定义请求对象。
|
|
47
|
+
* @returns 建表结果与创建的索引列表。
|
|
48
|
+
*/
|
|
48
49
|
define(moduleId: string, req: DefineTableRequest): DefineTableResult;
|
|
49
50
|
/** 幂等地物理建表 + 建索引(供 define / finalize 共用)。 */
|
|
50
51
|
private createPhysical;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-registry.d.ts","sourceRoot":"","sources":["../src/schema-registry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"schema-registry.d.ts","sourceRoot":"","sources":["../src/schema-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,SAAS,GACT,MAAM,GACN,MAAM,GACN,MAAM,GACN,SAAS,GACT,MAAM,GACN,MAAM,CAAC;AAEX,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AASD,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AA6EjD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAC1D,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,EAAE,EAAE,YAAY,EAE3B;IAED;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,iBAAiB,CAuCnE;IAED,6CAA6C;IAC7C,OAAO,CAAC,cAAc;IAyEtB;;;OAGG;IACH,QAAQ,IAAI;QAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CA0BtD;IAED,UAAU,IAAI,MAAM,EAAE,CAErB;IAED,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElC;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEzC;CACF"}
|
package/dist/schema-registry.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { log } from "./lib/log.js";
|
|
2
2
|
const PLATFORM_MODULE_ID = "__platform__";
|
|
3
3
|
const IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
4
|
-
const VALID_TYPES = /* @__PURE__ */ new Set([
|
|
4
|
+
const VALID_TYPES = /* @__PURE__ */ new Set([
|
|
5
|
+
"text",
|
|
6
|
+
"integer",
|
|
7
|
+
"real",
|
|
8
|
+
"blob",
|
|
9
|
+
"TEXT",
|
|
10
|
+
"INTEGER",
|
|
11
|
+
"REAL",
|
|
12
|
+
"BLOB"
|
|
13
|
+
]);
|
|
5
14
|
function assertValid(req, moduleId) {
|
|
6
15
|
if (!IDENT.test(req.name)) throw new Error(`[schema] ${moduleId}: invalid table name "${req.name}"`);
|
|
7
16
|
if (!req.columns || Object.keys(req.columns).length === 0) {
|
|
@@ -10,9 +19,11 @@ function assertValid(req, moduleId) {
|
|
|
10
19
|
let pks = 0;
|
|
11
20
|
for (const [colName, def] of Object.entries(req.columns)) {
|
|
12
21
|
if (!IDENT.test(colName)) throw new Error(`[schema] ${moduleId}: invalid column name "${colName}" in ${req.name}`);
|
|
13
|
-
|
|
22
|
+
const rawType = String(def.type || "");
|
|
23
|
+
if (!VALID_TYPES.has(rawType)) {
|
|
14
24
|
throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} bad type "${def.type}"`);
|
|
15
25
|
}
|
|
26
|
+
def.type = rawType.toLowerCase();
|
|
16
27
|
if (def.primary) pks++;
|
|
17
28
|
if (def.ref) {
|
|
18
29
|
const [t, c] = def.ref.split(".");
|
|
@@ -52,7 +63,13 @@ class SchemaRegistry {
|
|
|
52
63
|
constructor(db) {
|
|
53
64
|
this.db = db;
|
|
54
65
|
}
|
|
55
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* 注册并创建模块数据表结构。
|
|
68
|
+
*
|
|
69
|
+
* @param moduleId 注册模块唯一标识符。
|
|
70
|
+
* @param req 表结构定义请求对象。
|
|
71
|
+
* @returns 建表结果与创建的索引列表。
|
|
72
|
+
*/
|
|
56
73
|
define(moduleId, req) {
|
|
57
74
|
if (this.finalized) {
|
|
58
75
|
throw new Error(`[schema] registry \u5DF2 finalize,\u6A21\u5757 ${moduleId} \u4E0D\u80FD\u518D defineTable`);
|
|
@@ -87,12 +104,65 @@ class SchemaRegistry {
|
|
|
87
104
|
}
|
|
88
105
|
/** 幂等地物理建表 + 建索引(供 define / finalize 共用)。 */
|
|
89
106
|
createPhysical(t) {
|
|
90
|
-
const
|
|
91
|
-
|
|
107
|
+
const existingTableInfo = this.db.prepare(`PRAGMA table_info("${t.name}")`).all();
|
|
108
|
+
if (existingTableInfo.length > 0) {
|
|
109
|
+
let rowCount = 0;
|
|
110
|
+
try {
|
|
111
|
+
const countRes = this.db.prepare(`SELECT COUNT(*) AS c FROM "${t.name}"`).get();
|
|
112
|
+
rowCount = countRes?.c ?? 0;
|
|
113
|
+
} catch {
|
|
114
|
+
rowCount = 0;
|
|
115
|
+
}
|
|
116
|
+
const existingColNames = new Set(existingTableInfo.map((c) => c.name));
|
|
117
|
+
const requiredCols = Object.keys(t.columns);
|
|
118
|
+
const hasMissingRequiredCol = requiredCols.some((c) => !existingColNames.has(c));
|
|
119
|
+
const pkDef = Object.entries(t.columns).find(([_, c]) => c.primary);
|
|
120
|
+
const pkMatches = !pkDef || existingTableInfo.some((c) => c.pk === 1 && c.name === pkDef[0]);
|
|
121
|
+
if (rowCount === 0 && (hasMissingRequiredCol || !pkMatches)) {
|
|
122
|
+
try {
|
|
123
|
+
this.db.exec(`DROP TABLE "${t.name}"`);
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
const cols = buildColumnList(t.columns, t.softDelete);
|
|
127
|
+
this.db.exec(`CREATE TABLE "${t.name}" (${cols.join(", ")})`);
|
|
128
|
+
} else {
|
|
129
|
+
for (const [colName, colDef] of Object.entries(t.columns)) {
|
|
130
|
+
if (!existingColNames.has(colName)) {
|
|
131
|
+
const clause = buildColumnClause(colName, colDef);
|
|
132
|
+
try {
|
|
133
|
+
this.db.exec(`ALTER TABLE "${t.name}" ADD COLUMN ${clause};`);
|
|
134
|
+
} catch (e) {
|
|
135
|
+
log.warn(`[schema] ${t.moduleId}: ALTER TABLE "${t.name}" ADD COLUMN "${colName}" \u5F02\u5E38: ${e.message}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (t.softDelete) {
|
|
140
|
+
if (!existingColNames.has("_deleted_at")) {
|
|
141
|
+
try {
|
|
142
|
+
this.db.exec(`ALTER TABLE "${t.name}" ADD COLUMN "_deleted_at" INTEGER;`);
|
|
143
|
+
} catch {
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!existingColNames.has("_version")) {
|
|
147
|
+
try {
|
|
148
|
+
this.db.exec(`ALTER TABLE "${t.name}" ADD COLUMN "_version" INTEGER DEFAULT 0;`);
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
const cols = buildColumnList(t.columns, t.softDelete);
|
|
156
|
+
this.db.exec(`CREATE TABLE IF NOT EXISTS "${t.name}" (${cols.join(", ")})`);
|
|
157
|
+
}
|
|
92
158
|
for (const [n, def] of Object.entries(t.columns)) {
|
|
93
159
|
if (def.index) {
|
|
94
160
|
const idxName = `idx_${t.name}_${n}`.slice(0, 60);
|
|
95
|
-
|
|
161
|
+
try {
|
|
162
|
+
this.db.exec(`CREATE INDEX IF NOT EXISTS "${idxName}" ON "${t.name}"("${n}")`);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
log.warn(`[schema] ${t.moduleId}: \u521B\u5EFA\u7D22\u5F15 "${idxName}" \u5931\u8D25: ${e.message}`);
|
|
165
|
+
}
|
|
96
166
|
}
|
|
97
167
|
}
|
|
98
168
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/schema-registry.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * schema-registry.ts \u2014 \u6536\u96C6\u6A21\u5757 defineTable,\u7EDF\u4E00\u5EFA\u8868\n *\n * \u534F\u8BAE:\n * - \u6A21\u5757 SAPI \u542F\u52A8\u65F6\u8C03 db.defineTable(name, columns, {softDelete})\n * \u2192 POST /api/sfmc/db/define-table {moduleId, name, columns, softDelete}\n * - schema-registry \u6536\u96C6\u6240\u6709\u8C03\u7528,CAS \u9501\n * - \u7B49\u6240\u6709\"\u5DF2\u6CE8\u518C\u6A21\u5757\"\u90FD\u8C03\u8FC7(\u8D85\u65F6 fallback \u6216 explicit `finalize()`),\n * \u4E00\u6B21\u6027 CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS\n * - \u540C\u4E00 module \u91CD\u590D define \u540C\u540D\u8868 = \u5E42\u7B49(\u540C\u540D columns \u5141\u8BB8)\n * - \u4E0D\u540C module \u62A2\u540C\u540D\u8868 = \u542F\u52A8\u5931\u8D25\n * - \u5E73\u53F0\u8868(moduleId = \"__platform__\")\u7531 db-tables.ts \u5728 initSchema \u76F4\u63A5 bootstrap\n *\n * \u5217\u7C7B\u578B\u767D\u540D\u5355:\n * text / integer / real / blob\n * \u4FEE\u9970: primary / notNull / unique / default / index\n * softDelete = \u81EA\u52A8\u52A0 _deleted_at INTEGER(\u5217\u540D\u5E26\u4E0B\u5212\u7EBF) \u548C _version INTEGER\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport { log } from \"./lib/log.js\";\n\nexport type ColumnType = \"text\" | \"integer\" | \"real\" | \"blob\";\n\nexport interface ColumnDef {\n type: ColumnType;\n primary?: boolean;\n notNull?: boolean;\n unique?: boolean;\n default?: Primitive;\n index?: boolean;\n ref?: string;\n}\n\nexport type Primitive = string | number | boolean | null;\n\nexport interface DefineTableRequest {\n name: string;\n columns: Record<string, ColumnDef>;\n softDelete?: boolean;\n}\n\nexport interface DefineTableResult {\n table: string;\n created: boolean;\n indices: string[];\n}\n\ninterface DefinedTable {\n moduleId: string;\n name: string;\n columns: Record<string, ColumnDef>;\n softDelete: boolean;\n}\n\nexport const PLATFORM_MODULE_ID = \"__platform__\";\n\nconst IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst VALID_TYPES: Set<ColumnType> = new Set([\"text\", \"integer\", \"real\", \"blob\"]);\n\nfunction assertValid(req: DefineTableRequest, moduleId: string): void {\n if (!IDENT.test(req.name)) throw new Error(`[schema] ${moduleId}: invalid table name \"${req.name}\"`);\n if (!req.columns || Object.keys(req.columns).length === 0) {\n throw new Error(`[schema] ${moduleId}: ${req.name} has no columns`);\n }\n let pks = 0;\n for (const [colName, def] of Object.entries(req.columns)) {\n if (!IDENT.test(colName)) throw new Error(`[schema] ${moduleId}: invalid column name \"${colName}\" in ${req.name}`);\n if (!VALID_TYPES.has(def.type)) {\n throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} bad type \"${def.type}\"`);\n }\n if (def.primary) pks++;\n if (def.ref) {\n const [t, c] = def.ref.split(\".\");\n if (!t || !c || !IDENT.test(t) || !IDENT.test(c)) {\n throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} invalid ref \"${def.ref}\"`);\n }\n }\n }\n if (pks > 1) throw new Error(`[schema] ${moduleId}: ${req.name} has ${pks} primary keys (max 1)`);\n}\n\n/**\n * \u4E00\u6B21\u6027\u628A columns \u7FFB\u8BD1\u6210 CREATE TABLE\u3002\n * \u4F8B\u5982 {id:{type:\"text\",primary:true},name:{type:\"text\",notNull:true}}\n * \u2192 \"id\" TEXT PRIMARY KEY, \"name\" TEXT NOT NULL\n */\nfunction buildColumnClause(name: string, def: ColumnDef): string {\n const parts: string[] = [`\"${name}\"`, def.type.toUpperCase()];\n if (def.primary) parts.push(\"PRIMARY KEY\");\n if (def.notNull && !def.primary) parts.push(\"NOT NULL\");\n if (def.unique) parts.push(\"UNIQUE\");\n if (def.default !== undefined && def.default !== null) {\n if (typeof def.default === \"string\") parts.push(`DEFAULT '${def.default.replace(/'/g, \"''\")}'`);\n else parts.push(`DEFAULT ${def.default}`);\n }\n if (def.ref) parts.push(`REFERENCES ${def.ref}`);\n return parts.join(\" \");\n}\n\n/**\n * \u628A\u4E00\u5F20\u8868\u7684\u6240\u6709\u5217(\u542B softDelete \u9690\u5F0F\u5217)\u62FC\u6210 CREATE TABLE \u7684\u5217\u5B9A\u4E49\u6570\u7EC4\u3002\n * \u9690\u5F0F _deleted_at / _version \u4F5C\u4E3A\u72EC\u7ACB\u5217\u5728\u672B\u5C3E\u8FFD\u52A0\u300C\u4E00\u6B21\u300D(\u6B64\u524D\u8BEF\u5728\u6BCF\u4E2A\u5217\n * \u5B50\u53E5\u91CC\u5404\u8FFD\u52A0\u4E00\u6B21,\u4E14\u65E0\u9017\u53F7\u5206\u9694,\u5BFC\u81F4 softDelete \u8868\u5EFA\u8868 SQL \u8BED\u6CD5\u9519\u8BEF)\u3002\n */\nfunction buildColumnList(columns: Record<string, ColumnDef>, softDelete: boolean): string[] {\n const cols: string[] = [];\n for (const [n, def] of Object.entries(columns)) {\n cols.push(buildColumnClause(n, def));\n }\n if (softDelete) {\n cols.push('\"_deleted_at\" INTEGER', '\"_version\" INTEGER DEFAULT 0');\n }\n return cols;\n}\n\nexport class SchemaRegistry {\n private readonly db: DatabaseSync;\n private readonly tables = new Map<string, DefinedTable>();\n private finalized = false;\n\n constructor(db: DatabaseSync) {\n this.db = db;\n }\n\n /** \u6A21\u5757 SAPI \u542F\u52A8\u65F6\u8C03\u3002 */\n define(moduleId: string, req: DefineTableRequest): DefineTableResult {\n if (this.finalized) {\n throw new Error(`[schema] registry \u5DF2 finalize,\u6A21\u5757 ${moduleId} \u4E0D\u80FD\u518D defineTable`);\n }\n assertValid(req, moduleId);\n\n const existing = this.tables.get(req.name);\n if (existing) {\n if (existing.moduleId === moduleId) {\n // \u540C\u6A21\u5757\u91CD\u590D\u5B9A\u4E49:\u6821\u9A8C columns \u4E00\u81F4\n if (JSON.stringify(existing.columns) !== JSON.stringify(req.columns)) {\n throw new Error(`[schema] ${moduleId} \u91CD\u590D defineTable \"${req.name}\",columns \u4E0D\u4E00\u81F4`);\n }\n if (existing.softDelete !== (req.softDelete ?? true)) {\n throw new Error(`[schema] ${moduleId} \u91CD\u590D defineTable \"${req.name}\",softDelete \u4E0D\u4E00\u81F4`);\n }\n log.info(`[schema] ${moduleId} \u91CD\u590D define \"${req.name}\" \u8DF3\u8FC7`);\n return { table: req.name, created: false, indices: [] };\n }\n throw new Error(\n `[schema] \u8868 \"${req.name}\" \u5DF2\u88AB\u6A21\u5757 ${existing.moduleId} \u5B9A\u4E49,${moduleId} \u4E0D\u80FD\u62A2\u540C\u540D\u8868`\n );\n }\n\n const softDelete = req.softDelete ?? true;\n const defined: DefinedTable = {\n moduleId,\n name: req.name,\n columns: req.columns,\n softDelete,\n };\n this.tables.set(req.name, defined);\n // \u7ACB\u5373\u7269\u7406\u5EFA\u8868(\u5E42\u7B49 CREATE TABLE IF NOT EXISTS)\u3002\n // \u539F\u8BBE\u8BA1\u662F\u300Cdefine \u6536\u96C6 \u2192 finalize \u7EDF\u4E00\u5EFA\u8868\u300D,\u4F46 finalize() \u4ECE\u672A\u88AB\u8C03\u7528,\n // \u5BFC\u81F4\u6A21\u5757\u8868\u6C38\u8FDC\u4E0D\u4F1A\u843D\u5730\u3001\u540E\u7EED insert/query \u5168\u90E8 no such table\u3002\n // \u91C7\u7528 define \u5373\u5EFA\u8868:\u78B0\u649E\u68C0\u6D4B\u5DF2\u5728\u4E0A\u65B9\u6309\u5185\u5B58\u8868\u5B8C\u6210,\u4E0D\u5F71\u54CD\u5355\u4E00 owner \u4FDD\u8BC1\u3002\n this.createPhysical(defined);\n return { table: req.name, created: true, indices: [] };\n }\n\n /** \u5E42\u7B49\u5730\u7269\u7406\u5EFA\u8868 + \u5EFA\u7D22\u5F15(\u4F9B define / finalize \u5171\u7528)\u3002 */\n private createPhysical(t: DefinedTable): void {\n const cols = buildColumnList(t.columns, t.softDelete);\n this.db.exec(`CREATE TABLE IF NOT EXISTS \"${t.name}\" (${cols.join(\", \")})`);\n for (const [n, def] of Object.entries(t.columns)) {\n if (def.index) {\n const idxName = `idx_${t.name}_${n}`.slice(0, 60);\n this.db.exec(`CREATE INDEX IF NOT EXISTS \"${idxName}\" ON \"${t.name}\"(\"${n}\")`);\n }\n }\n }\n\n /**\n * \u771F\u6B63\u5EFA\u8868\u7684\u65F6\u523B\u3002\u6240\u6709 enabled \u6A21\u5757 init \u5B8C\u6210(\u6216\u660E\u786E\u8C03)\u540E\u518D finalize\u3002\n * CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS,\u5E42\u7B49\u3002\n */\n finalize(): { tableNames: string[]; created: string[] } {\n if (this.finalized) return { tableNames: [...this.tables.keys()], created: [] };\n this.finalized = true;\n const created: string[] = [];\n\n for (const t of this.tables.values()) {\n const cols = buildColumnList(t.columns, t.softDelete);\n const ddl = `CREATE TABLE IF NOT EXISTS \"${t.name}\" (${cols.join(\", \")})`;\n this.db.exec(ddl);\n\n for (const [n, def] of Object.entries(t.columns)) {\n if (def.index) {\n const idxName = `idx_${t.name}_${n}`.slice(0, 60);\n this.db.exec(`CREATE INDEX IF NOT EXISTS \"${idxName}\" ON \"${t.name}\"(\"${n}\")`);\n }\n }\n\n const exists =\n (this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name=?\")\n .get(t.name) as { name?: string } | undefined)?.name === t.name;\n if (exists) created.push(t.name);\n log.success(`[schema] ${t.moduleId} \u2192 ${t.name} (softDelete=${t.softDelete})`);\n }\n\n return { tableNames: [...this.tables.keys()], created };\n }\n\n listTables(): string[] {\n return [...this.tables.keys()];\n }\n\n isRegistered(name: string): boolean {\n return this.tables.has(name);\n }\n\n getOwner(name: string): string | undefined {\n return this.tables.get(name)?.moduleId;\n }\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * schema-registry.ts \u2014 \u52A8\u6001\u6570\u636E\u8868\u7ED3\u6784\u6CE8\u518C\u4E0E\u521B\u5EFA\u7BA1\u7406\u5668\n *\n * \u534F\u8BAE\u4E0E\u6D41\u7A0B\uFF1A\n * 1. \u6A21\u5757\u542F\u52A8\u65F6\u8C03\u7528 `db.defineTable(name, columns, { softDelete })` \u5411\u670D\u52A1\u7AEF\u6CE8\u518C\u8868\u7ED3\u6784\u5B9A\u4E49\u3002\n * 2. `SchemaRegistry` \u6821\u9A8C\u5217\u5B9A\u4E49\u5408\u6CD5\u6027\u3001\u5916\u952E\u5F15\u7528\u4E0E\u4E3B\u952E\u552F\u4E00\u6027\u3002\n * 3. \u6821\u9A8C\u901A\u8FC7\u540E\u81EA\u52A8\u6267\u884C `CREATE TABLE IF NOT EXISTS` \u53CA\u7D22\u5F15\u521B\u5EFA\u8BED\u53E5\u3002\n *\n * \u51B2\u7A81\u4E0E\u9694\u79BB\u63A7\u5236\uFF1A\n * - \u76F8\u540C\u6A21\u5757\u91CD\u590D\u5B9A\u4E49\u540C\u540D\u8868\uFF1A\u5177\u5907\u5E42\u7B49\u6027\uFF08\u8981\u6C42\u5217\u7ED3\u6784\u5B8C\u5168\u4E00\u81F4\uFF09\n * - \u4E0D\u540C\u6A21\u5757\u5B9A\u4E49\u76F8\u540C\u8868\u540D\uFF1A\u629B\u51FA\u51B2\u7A81\u5F02\u5E38\u62D2\u7EDD\u6CE8\u518C\n * - \u8F6F\u5220\u9664\u652F\u6301\uFF1A\u5F00\u542F `softDelete`\uFF08\u9ED8\u8BA4\u5F00\u542F\uFF09\u65F6\u81EA\u52A8\u5728\u672B\u5C3E\u7EDF\u4E00\u8FFD\u52A0 `_deleted_at` \u4E0E\u4E50\u89C2\u9501 `_version`\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport { log } from \"./lib/log.js\";\n\nexport type ColumnType =\n | \"text\"\n | \"integer\"\n | \"real\"\n | \"blob\"\n | \"TEXT\"\n | \"INTEGER\"\n | \"REAL\"\n | \"BLOB\";\n\nexport interface ColumnDef {\n type: ColumnType;\n primary?: boolean;\n notNull?: boolean;\n unique?: boolean;\n default?: Primitive;\n index?: boolean;\n ref?: string;\n}\n\nexport type Primitive = string | number | boolean | null;\n\nexport interface DefineTableRequest {\n name: string;\n columns: Record<string, ColumnDef>;\n softDelete?: boolean;\n}\n\nexport interface DefineTableResult {\n table: string;\n created: boolean;\n indices: string[];\n}\n\ninterface DefinedTable {\n moduleId: string;\n name: string;\n columns: Record<string, ColumnDef>;\n softDelete: boolean;\n}\n\nexport const PLATFORM_MODULE_ID = \"__platform__\";\n\nconst IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst VALID_TYPES: Set<string> = new Set([\n \"text\",\n \"integer\",\n \"real\",\n \"blob\",\n \"TEXT\",\n \"INTEGER\",\n \"REAL\",\n \"BLOB\",\n]);\n\nfunction assertValid(req: DefineTableRequest, moduleId: string): void {\n if (!IDENT.test(req.name)) throw new Error(`[schema] ${moduleId}: invalid table name \"${req.name}\"`);\n if (!req.columns || Object.keys(req.columns).length === 0) {\n throw new Error(`[schema] ${moduleId}: ${req.name} has no columns`);\n }\n let pks = 0;\n for (const [colName, def] of Object.entries(req.columns)) {\n if (!IDENT.test(colName)) throw new Error(`[schema] ${moduleId}: invalid column name \"${colName}\" in ${req.name}`);\n const rawType = String(def.type || \"\");\n if (!VALID_TYPES.has(rawType)) {\n throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} bad type \"${def.type}\"`);\n }\n def.type = rawType.toLowerCase() as ColumnType;\n if (def.primary) pks++;\n if (def.ref) {\n const [t, c] = def.ref.split(\".\");\n if (!t || !c || !IDENT.test(t) || !IDENT.test(c)) {\n throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} invalid ref \"${def.ref}\"`);\n }\n }\n }\n if (pks > 1) throw new Error(`[schema] ${moduleId}: ${req.name} has ${pks} primary keys (max 1)`);\n}\n\n/**\n * \u5C06\u5355\u5217\u5B9A\u4E49\u7F16\u8BD1\u4E3A SQL \u5217\u5B9A\u4E49\u5B50\u53E5\uFF08\u5982 `\"id\" TEXT PRIMARY KEY`\uFF09\u3002\n *\n * @param name \u5217\u540D\u3002\n * @param def \u5217\u5C5E\u6027\u5B9A\u4E49\u3002\n * @returns \u7F16\u8BD1\u540E\u7684 SQL \u7247\u6BB5\u3002\n */\nfunction buildColumnClause(name: string, def: ColumnDef): string {\n const parts: string[] = [`\"${name}\"`, def.type.toUpperCase()];\n if (def.primary) parts.push(\"PRIMARY KEY\");\n if (def.notNull && !def.primary) parts.push(\"NOT NULL\");\n if (def.unique) parts.push(\"UNIQUE\");\n if (def.default !== undefined && def.default !== null) {\n if (typeof def.default === \"string\") parts.push(`DEFAULT '${def.default.replace(/'/g, \"''\")}'`);\n else parts.push(`DEFAULT ${def.default}`);\n }\n if (def.ref) parts.push(`REFERENCES ${def.ref}`);\n return parts.join(\" \");\n}\n\n/**\n * \u5C06\u6574\u5F20\u8868\u7684\u6240\u6709\u5217\u5B9A\u4E49\uFF08\u5305\u542B softDelete \u9690\u5F0F\u5217\uFF09\u7F16\u8BD1\u4E3A\u5217\u5B9A\u4E49\u5B57\u7B26\u4E32\u6570\u7EC4\u3002\n * \u8F6F\u5220\u9664\u5217 `_deleted_at` \u4E0E\u4E50\u89C2\u9501\u7248\u672C\u53F7 `_version` \u4F5C\u4E3A\u72EC\u7ACB\u5217\u5728\u672B\u5C3E\u7EDF\u4E00\u8FFD\u52A0\u3002\n *\n * @param columns \u5217\u7ED3\u6784\u5B9A\u4E49\u5B57\u5178\u3002\n * @param softDelete \u662F\u5426\u542F\u7528\u8F6F\u5220\u9664\u6269\u5C55\u5217\u3002\n * @returns \u7F16\u8BD1\u540E\u7684\u5217\u5B9A\u4E49\u5B50\u53E5\u6570\u7EC4\u3002\n */\nfunction buildColumnList(columns: Record<string, ColumnDef>, softDelete: boolean): string[] {\n const cols: string[] = [];\n for (const [n, def] of Object.entries(columns)) {\n cols.push(buildColumnClause(n, def));\n }\n if (softDelete) {\n cols.push('\"_deleted_at\" INTEGER', '\"_version\" INTEGER DEFAULT 0');\n }\n return cols;\n}\n\nexport class SchemaRegistry {\n private readonly db: DatabaseSync;\n private readonly tables = new Map<string, DefinedTable>();\n private finalized = false;\n\n constructor(db: DatabaseSync) {\n this.db = db;\n }\n\n /**\n * \u6CE8\u518C\u5E76\u521B\u5EFA\u6A21\u5757\u6570\u636E\u8868\u7ED3\u6784\u3002\n *\n * @param moduleId \u6CE8\u518C\u6A21\u5757\u552F\u4E00\u6807\u8BC6\u7B26\u3002\n * @param req \u8868\u7ED3\u6784\u5B9A\u4E49\u8BF7\u6C42\u5BF9\u8C61\u3002\n * @returns \u5EFA\u8868\u7ED3\u679C\u4E0E\u521B\u5EFA\u7684\u7D22\u5F15\u5217\u8868\u3002\n */\n define(moduleId: string, req: DefineTableRequest): DefineTableResult {\n\n if (this.finalized) {\n throw new Error(`[schema] registry \u5DF2 finalize,\u6A21\u5757 ${moduleId} \u4E0D\u80FD\u518D defineTable`);\n }\n assertValid(req, moduleId);\n\n const existing = this.tables.get(req.name);\n if (existing) {\n if (existing.moduleId === moduleId) {\n // \u540C\u6A21\u5757\u91CD\u590D\u5B9A\u4E49:\u6821\u9A8C columns \u4E00\u81F4\n if (JSON.stringify(existing.columns) !== JSON.stringify(req.columns)) {\n throw new Error(`[schema] ${moduleId} \u91CD\u590D defineTable \"${req.name}\",columns \u4E0D\u4E00\u81F4`);\n }\n if (existing.softDelete !== (req.softDelete ?? true)) {\n throw new Error(`[schema] ${moduleId} \u91CD\u590D defineTable \"${req.name}\",softDelete \u4E0D\u4E00\u81F4`);\n }\n log.info(`[schema] ${moduleId} \u91CD\u590D define \"${req.name}\" \u8DF3\u8FC7`);\n return { table: req.name, created: false, indices: [] };\n }\n throw new Error(\n `[schema] \u8868 \"${req.name}\" \u5DF2\u88AB\u6A21\u5757 ${existing.moduleId} \u5B9A\u4E49,${moduleId} \u4E0D\u80FD\u62A2\u540C\u540D\u8868`\n );\n }\n\n const softDelete = req.softDelete ?? true;\n const defined: DefinedTable = {\n moduleId,\n name: req.name,\n columns: req.columns,\n softDelete,\n };\n this.tables.set(req.name, defined);\n // \u7ACB\u5373\u7269\u7406\u5EFA\u8868(\u5E42\u7B49 CREATE TABLE IF NOT EXISTS)\u3002\n // \u539F\u8BBE\u8BA1\u662F\u300Cdefine \u6536\u96C6 \u2192 finalize \u7EDF\u4E00\u5EFA\u8868\u300D,\u4F46 finalize() \u4ECE\u672A\u88AB\u8C03\u7528,\n // \u5BFC\u81F4\u6A21\u5757\u8868\u6C38\u8FDC\u4E0D\u4F1A\u843D\u5730\u3001\u540E\u7EED insert/query \u5168\u90E8 no such table\u3002\n // \u91C7\u7528 define \u5373\u5EFA\u8868:\u78B0\u649E\u68C0\u6D4B\u5DF2\u5728\u4E0A\u65B9\u6309\u5185\u5B58\u8868\u5B8C\u6210,\u4E0D\u5F71\u54CD\u5355\u4E00 owner \u4FDD\u8BC1\u3002\n this.createPhysical(defined);\n return { table: req.name, created: true, indices: [] };\n }\n\n /** \u5E42\u7B49\u5730\u7269\u7406\u5EFA\u8868 + \u5EFA\u7D22\u5F15(\u4F9B define / finalize \u5171\u7528)\u3002 */\n private createPhysical(t: DefinedTable): void {\n const existingTableInfo = this.db.prepare(`PRAGMA table_info(\"${t.name}\")`).all() as Array<{\n cid: number;\n name: string;\n type: string;\n notnull: number;\n dflt_value: unknown;\n pk: number;\n }>;\n\n if (existingTableInfo.length > 0) {\n let rowCount = 0;\n try {\n const countRes = this.db.prepare(`SELECT COUNT(*) AS c FROM \"${t.name}\"`).get() as { c: number } | undefined;\n rowCount = countRes?.c ?? 0;\n } catch {\n rowCount = 0;\n }\n const existingColNames = new Set(existingTableInfo.map((c) => c.name));\n const requiredCols = Object.keys(t.columns);\n const hasMissingRequiredCol = requiredCols.some((c) => !existingColNames.has(c));\n const pkDef = Object.entries(t.columns).find(([_, c]) => c.primary);\n const pkMatches = !pkDef || existingTableInfo.some((c) => c.pk === 1 && c.name === pkDef[0]);\n\n if (rowCount === 0 && (hasMissingRequiredCol || !pkMatches)) {\n // \u7A7A\u8868\u4E14\u7ED3\u6784\u4E0E\u5F53\u524D\u5B9A\u4E49\u4E0D\u517C\u5BB9\uFF08\u5982\u65E7\u7248\u672C\u9057\u7559\u7A7A\u8868\uFF09\uFF1A\u5B89\u5168\u5220\u9664\u5E76\u4EE5\u65B0\u7ED3\u6784\u91CD\u5EFA\n try {\n this.db.exec(`DROP TABLE \"${t.name}\"`);\n } catch {}\n const cols = buildColumnList(t.columns, t.softDelete);\n this.db.exec(`CREATE TABLE \"${t.name}\" (${cols.join(\", \")})`);\n } else {\n // \u8868\u4E2D\u6709\u6570\u636E\u6216\u7ED3\u6784\u517C\u5BB9\uFF1A\u901A\u8FC7 ALTER TABLE ADD COLUMN \u589E\u91CF\u8865\u9F50\u7F3A\u5931\u5217\n for (const [colName, colDef] of Object.entries(t.columns)) {\n if (!existingColNames.has(colName)) {\n const clause = buildColumnClause(colName, colDef);\n try {\n this.db.exec(`ALTER TABLE \"${t.name}\" ADD COLUMN ${clause};`);\n } catch (e) {\n log.warn(`[schema] ${t.moduleId}: ALTER TABLE \"${t.name}\" ADD COLUMN \"${colName}\" \u5F02\u5E38: ${(e as Error).message}`);\n }\n }\n }\n if (t.softDelete) {\n if (!existingColNames.has(\"_deleted_at\")) {\n try {\n this.db.exec(`ALTER TABLE \"${t.name}\" ADD COLUMN \"_deleted_at\" INTEGER;`);\n } catch {}\n }\n if (!existingColNames.has(\"_version\")) {\n try {\n this.db.exec(`ALTER TABLE \"${t.name}\" ADD COLUMN \"_version\" INTEGER DEFAULT 0;`);\n } catch {}\n }\n }\n }\n } else {\n const cols = buildColumnList(t.columns, t.softDelete);\n this.db.exec(`CREATE TABLE IF NOT EXISTS \"${t.name}\" (${cols.join(\", \")})`);\n }\n\n for (const [n, def] of Object.entries(t.columns)) {\n if (def.index) {\n const idxName = `idx_${t.name}_${n}`.slice(0, 60);\n try {\n this.db.exec(`CREATE INDEX IF NOT EXISTS \"${idxName}\" ON \"${t.name}\"(\"${n}\")`);\n } catch (e) {\n log.warn(`[schema] ${t.moduleId}: \u521B\u5EFA\u7D22\u5F15 \"${idxName}\" \u5931\u8D25: ${(e as Error).message}`);\n }\n }\n }\n }\n\n /**\n * \u771F\u6B63\u5EFA\u8868\u7684\u65F6\u523B\u3002\u6240\u6709 enabled \u6A21\u5757 init \u5B8C\u6210(\u6216\u660E\u786E\u8C03)\u540E\u518D finalize\u3002\n * CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS,\u5E42\u7B49\u3002\n */\n finalize(): { tableNames: string[]; created: string[] } {\n if (this.finalized) return { tableNames: [...this.tables.keys()], created: [] };\n this.finalized = true;\n const created: string[] = [];\n\n for (const t of this.tables.values()) {\n const cols = buildColumnList(t.columns, t.softDelete);\n const ddl = `CREATE TABLE IF NOT EXISTS \"${t.name}\" (${cols.join(\", \")})`;\n this.db.exec(ddl);\n\n for (const [n, def] of Object.entries(t.columns)) {\n if (def.index) {\n const idxName = `idx_${t.name}_${n}`.slice(0, 60);\n this.db.exec(`CREATE INDEX IF NOT EXISTS \"${idxName}\" ON \"${t.name}\"(\"${n}\")`);\n }\n }\n\n const exists =\n (this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name=?\")\n .get(t.name) as { name?: string } | undefined)?.name === t.name;\n if (exists) created.push(t.name);\n log.success(`[schema] ${t.moduleId} \u2192 ${t.name} (softDelete=${t.softDelete})`);\n }\n\n return { tableNames: [...this.tables.keys()], created };\n }\n\n listTables(): string[] {\n return [...this.tables.keys()];\n }\n\n isRegistered(name: string): boolean {\n return this.tables.has(name);\n }\n\n getOwner(name: string): string | undefined {\n return this.tables.get(name)?.moduleId;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|