@sfmc-bds/db-server 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.
Files changed (146) hide show
  1. package/README.md +21 -0
  2. package/dist/db-tables.d.ts +13 -0
  3. package/dist/db-tables.d.ts.map +1 -0
  4. package/dist/db-tables.js +35 -0
  5. package/dist/db-tables.js.map +1 -0
  6. package/dist/domain/bridge.d.ts +48 -0
  7. package/dist/domain/bridge.d.ts.map +1 -0
  8. package/dist/domain/bridge.js +82 -0
  9. package/dist/domain/bridge.js.map +1 -0
  10. package/dist/domain/economy.d.ts +120 -0
  11. package/dist/domain/economy.d.ts.map +1 -0
  12. package/dist/domain/economy.js +321 -0
  13. package/dist/domain/economy.js.map +1 -0
  14. package/dist/domain/schema.d.ts +26 -0
  15. package/dist/domain/schema.d.ts.map +1 -0
  16. package/dist/domain/schema.js +462 -0
  17. package/dist/domain/schema.js.map +1 -0
  18. package/dist/domain/transaction.d.ts +67 -0
  19. package/dist/domain/transaction.d.ts.map +1 -0
  20. package/dist/domain/transaction.js +85 -0
  21. package/dist/domain/transaction.js.map +1 -0
  22. package/dist/env.d.ts +22 -0
  23. package/dist/env.d.ts.map +1 -0
  24. package/dist/env.js +94 -0
  25. package/dist/env.js.map +1 -0
  26. package/dist/index.d.ts +33 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +313 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/lib/http.d.ts +18 -0
  31. package/dist/lib/http.d.ts.map +1 -0
  32. package/dist/lib/http.js +43 -0
  33. package/dist/lib/http.js.map +1 -0
  34. package/dist/lib/idempotency-store.d.ts +28 -0
  35. package/dist/lib/idempotency-store.d.ts.map +1 -0
  36. package/dist/lib/idempotency-store.js +49 -0
  37. package/dist/lib/idempotency-store.js.map +1 -0
  38. package/dist/lib/idempotency.d.ts +9 -0
  39. package/dist/lib/idempotency.d.ts.map +1 -0
  40. package/dist/lib/idempotency.js +14 -0
  41. package/dist/lib/idempotency.js.map +1 -0
  42. package/dist/lib/identifiers.d.ts +3 -0
  43. package/dist/lib/identifiers.d.ts.map +1 -0
  44. package/dist/lib/identifiers.js +18 -0
  45. package/dist/lib/identifiers.js.map +1 -0
  46. package/dist/lib/log.d.ts +11 -0
  47. package/dist/lib/log.d.ts.map +1 -0
  48. package/dist/lib/log.js +15 -0
  49. package/dist/lib/log.js.map +1 -0
  50. package/dist/lib/module-state.d.ts +15 -0
  51. package/dist/lib/module-state.d.ts.map +1 -0
  52. package/dist/lib/module-state.js +27 -0
  53. package/dist/lib/module-state.js.map +1 -0
  54. package/dist/lib/runtime.d.ts +21 -0
  55. package/dist/lib/runtime.d.ts.map +1 -0
  56. package/dist/lib/runtime.js +34 -0
  57. package/dist/lib/runtime.js.map +1 -0
  58. package/dist/lib/sql-helpers.d.ts +31 -0
  59. package/dist/lib/sql-helpers.d.ts.map +1 -0
  60. package/dist/lib/sql-helpers.js +30 -0
  61. package/dist/lib/sql-helpers.js.map +1 -0
  62. package/dist/lib/sqlite.d.ts +20 -0
  63. package/dist/lib/sqlite.d.ts.map +1 -0
  64. package/dist/lib/sqlite.js +59 -0
  65. package/dist/lib/sqlite.js.map +1 -0
  66. package/dist/manifest-loader.d.ts +69 -0
  67. package/dist/manifest-loader.d.ts.map +1 -0
  68. package/dist/manifest-loader.js +212 -0
  69. package/dist/manifest-loader.js.map +1 -0
  70. package/dist/manifest.d.ts +76 -0
  71. package/dist/manifest.d.ts.map +1 -0
  72. package/dist/manifest.js +128 -0
  73. package/dist/manifest.js.map +1 -0
  74. package/dist/module-auth.d.ts +58 -0
  75. package/dist/module-auth.d.ts.map +1 -0
  76. package/dist/module-auth.js +118 -0
  77. package/dist/module-auth.js.map +1 -0
  78. package/dist/permission-gate.d.ts +53 -0
  79. package/dist/permission-gate.d.ts.map +1 -0
  80. package/dist/permission-gate.js +94 -0
  81. package/dist/permission-gate.js.map +1 -0
  82. package/dist/routes/_shared.d.ts +68 -0
  83. package/dist/routes/_shared.d.ts.map +1 -0
  84. package/dist/routes/_shared.js +27 -0
  85. package/dist/routes/_shared.js.map +1 -0
  86. package/dist/routes/config.d.ts +27 -0
  87. package/dist/routes/config.d.ts.map +1 -0
  88. package/dist/routes/config.js +236 -0
  89. package/dist/routes/config.js.map +1 -0
  90. package/dist/routes/db-routes.d.ts +38 -0
  91. package/dist/routes/db-routes.d.ts.map +1 -0
  92. package/dist/routes/db-routes.js +186 -0
  93. package/dist/routes/db-routes.js.map +1 -0
  94. package/dist/routes/health.d.ts +10 -0
  95. package/dist/routes/health.d.ts.map +1 -0
  96. package/dist/routes/health.js +23 -0
  97. package/dist/routes/health.js.map +1 -0
  98. package/dist/routes/messages.d.ts +23 -0
  99. package/dist/routes/messages.d.ts.map +1 -0
  100. package/dist/routes/messages.js +72 -0
  101. package/dist/routes/messages.js.map +1 -0
  102. package/dist/routes/module-config-routes.d.ts +31 -0
  103. package/dist/routes/module-config-routes.d.ts.map +1 -0
  104. package/dist/routes/module-config-routes.js +128 -0
  105. package/dist/routes/module-config-routes.js.map +1 -0
  106. package/dist/routes/modules.d.ts +34 -0
  107. package/dist/routes/modules.d.ts.map +1 -0
  108. package/dist/routes/modules.js +92 -0
  109. package/dist/routes/modules.js.map +1 -0
  110. package/dist/routes/service-routes.d.ts +26 -0
  111. package/dist/routes/service-routes.d.ts.map +1 -0
  112. package/dist/routes/service-routes.js +68 -0
  113. package/dist/routes/service-routes.js.map +1 -0
  114. package/dist/runtime.test.d.ts +2 -0
  115. package/dist/runtime.test.d.ts.map +1 -0
  116. package/dist/runtime.test.js +66 -0
  117. package/dist/runtime.test.js.map +1 -0
  118. package/dist/schema-registry.d.ts +63 -0
  119. package/dist/schema-registry.d.ts.map +1 -0
  120. package/dist/schema-registry.js +178 -0
  121. package/dist/schema-registry.js.map +1 -0
  122. package/dist/server.d.ts +27 -0
  123. package/dist/server.d.ts.map +1 -0
  124. package/dist/server.js +82 -0
  125. package/dist/server.js.map +1 -0
  126. package/dist/service-registry.d.ts +46 -0
  127. package/dist/service-registry.d.ts.map +1 -0
  128. package/dist/service-registry.js +73 -0
  129. package/dist/service-registry.js.map +1 -0
  130. package/dist/services/builtin-handlers.d.ts +22 -0
  131. package/dist/services/builtin-handlers.d.ts.map +1 -0
  132. package/dist/services/builtin-handlers.js +23 -0
  133. package/dist/services/builtin-handlers.js.map +1 -0
  134. package/dist/services/economy-handlers.d.ts +14 -0
  135. package/dist/services/economy-handlers.d.ts.map +1 -0
  136. package/dist/services/economy-handlers.js +144 -0
  137. package/dist/services/economy-handlers.js.map +1 -0
  138. package/dist/tx-runner.d.ts +144 -0
  139. package/dist/tx-runner.d.ts.map +1 -0
  140. package/dist/tx-runner.js +273 -0
  141. package/dist/tx-runner.js.map +1 -0
  142. package/dist/where.d.ts +48 -0
  143. package/dist/where.d.ts.map +1 -0
  144. package/dist/where.js +107 -0
  145. package/dist/where.js.map +1 -0
  146. package/package.json +52 -0
@@ -0,0 +1,92 @@
1
+ /**
2
+ * routes/modules.ts — 模块目录与启停(基于 modules/catalog.json + module-lock.json)
3
+ *
4
+ * 路由列表:
5
+ * GET /api/sfmc/modules — 模块列表(含 enable 状态)
6
+ * GET /api/sfmc/modules/catalog — 原始 catalog 列表
7
+ * GET /api/sfmc/modules/:key — 单个模块详情
8
+ * PATCH/PUT /api/sfmc/modules/:key — 设置 enabled
9
+ * POST /api/sfmc/modules/:key/enable — 启用模块
10
+ * POST /api/sfmc/modules/:key/disable — 禁用模块
11
+ */
12
+ function createModuleRoutes({ loadModuleCatalog, buildModuleList, resolveModuleByKey, setModuleEnabled, body, json, }) {
13
+ return async function handleModuleRoute({ path, method, params, req, res, }) {
14
+ void params; // unused
15
+ if (path === "/api/sfmc/modules/catalog") {
16
+ if (method === "GET") {
17
+ json(res, { modules: loadModuleCatalog() });
18
+ }
19
+ else {
20
+ json(res, { success: false, error: "not_found" }, 404);
21
+ }
22
+ return true;
23
+ }
24
+ if (path === "/api/sfmc/modules") {
25
+ if (method === "GET")
26
+ json(res, { modules: buildModuleList() });
27
+ else
28
+ json(res, { success: false, error: "not_found" }, 404);
29
+ return true;
30
+ }
31
+ if (!path.startsWith("/api/sfmc/modules/"))
32
+ return false;
33
+ const rest = path.slice("/api/sfmc/modules/".length);
34
+ const [rawKey, action] = rest.split("/");
35
+ const module = resolveModuleByKey(decodeURIComponent(rawKey ?? ""));
36
+ if (!module) {
37
+ json(res, { success: false, error: "module_not_found" }, 404);
38
+ return true;
39
+ }
40
+ const current = () => buildModuleList().find((entry) => entry.id === module.id);
41
+ if (!action && method === "GET") {
42
+ json(res, { module: current() ?? null });
43
+ }
44
+ else if (!action && (method === "PATCH" || method === "PUT")) {
45
+ const { enabled } = await body(req);
46
+ if (!enabled && !module.canDisable) {
47
+ json(res, { success: false, error: "module_cannot_disable" }, 400);
48
+ return true;
49
+ }
50
+ try {
51
+ setModuleEnabled(module, !!enabled);
52
+ }
53
+ catch (error) {
54
+ const e = error;
55
+ if (e.code === "dependency_unmet") {
56
+ json(res, { success: false, error: "dependency_unmet", unmet: e.unmet }, 409);
57
+ return true;
58
+ }
59
+ throw error;
60
+ }
61
+ json(res, { success: true, module: current() });
62
+ }
63
+ else if (action === "enable" && method === "POST") {
64
+ try {
65
+ setModuleEnabled(module, true);
66
+ }
67
+ catch (error) {
68
+ const e = error;
69
+ if (e.code === "dependency_unmet") {
70
+ json(res, { success: false, error: "dependency_unmet", unmet: e.unmet }, 409);
71
+ return true;
72
+ }
73
+ throw error;
74
+ }
75
+ json(res, { success: true, module: current() });
76
+ }
77
+ else if (action === "disable" && method === "POST") {
78
+ if (!module.canDisable) {
79
+ json(res, { success: false, error: "module_cannot_disable" }, 400);
80
+ return true;
81
+ }
82
+ setModuleEnabled(module, false);
83
+ json(res, { success: true, module: current() });
84
+ }
85
+ else {
86
+ json(res, { success: false, error: "not_found" }, 404);
87
+ }
88
+ return true;
89
+ };
90
+ }
91
+ export { createModuleRoutes };
92
+ //# sourceMappingURL=modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.js","sourceRoot":"","sources":["../../src/routes/modules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * routes/service-routes.ts — /api/sfmc/services/* 处理器
3
+ *
4
+ * 端点:
5
+ * GET /api/sfmc/services → { services: [{name, moduleId}] }
6
+ * GET /api/sfmc/services/:name?input=<urlencoded json>
7
+ * → { ok: true, result } | { ok: false, error, code } (+ HTTP status)
8
+ */
9
+ import type { IncomingMessage, ServerResponse } from "node:http";
10
+ import { json as defaultJson, type Method } from "../lib/http.js";
11
+ import type { ServiceRegistry } from "../service-registry.js";
12
+ import { type ModuleAuth } from "./_shared.js";
13
+ export interface ServiceRoutesDeps {
14
+ serviceRegistry: ServiceRegistry;
15
+ enabled: Map<string, import("../manifest-loader.js").ModuleManifestV2>;
16
+ json?: typeof defaultJson;
17
+ }
18
+ export declare function createServiceRoutes(depsIn: Partial<ServiceRoutesDeps>): (ctx: {
19
+ path: string;
20
+ method: Method | string;
21
+ params: URLSearchParams;
22
+ req: IncomingMessage;
23
+ res: ServerResponse;
24
+ moduleAuth?: ModuleAuth;
25
+ }) => Promise<boolean>;
26
+ //# sourceMappingURL=service-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-routes.d.ts","sourceRoot":"","sources":["../../src/routes/service-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,uBAAuB,EAAE,gBAAgB,CAAC,CAAC;IACvE,IAAI,CAAC,EAAE,OAAO,WAAW,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,IAOtD,KAAK;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,GAAG,EAAE,eAAe,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,KAAG,OAAO,CAAC,OAAO,CAAC,CA8DrB"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * routes/service-routes.ts — /api/sfmc/services/* 处理器
3
+ *
4
+ * 端点:
5
+ * GET /api/sfmc/services → { services: [{name, moduleId}] }
6
+ * GET /api/sfmc/services/:name?input=<urlencoded json>
7
+ * → { ok: true, result } | { ok: false, error, code } (+ HTTP status)
8
+ */
9
+ import { json as defaultJson } from "../lib/http.js";
10
+ import { assertModulePermission, Perm, PermissionDeniedError } from "../permission-gate.js";
11
+ import { jsonV2Fail } from "./_shared.js";
12
+ export function createServiceRoutes(depsIn) {
13
+ const deps = depsIn;
14
+ if (!deps.serviceRegistry || !deps.enabled) {
15
+ throw new Error("createServiceRoutes: 缺少 serviceRegistry / enabled map");
16
+ }
17
+ const json = deps.json || defaultJson;
18
+ return async (ctx) => {
19
+ const { path, method, params, res } = ctx;
20
+ if (!path.startsWith("/api/sfmc/services"))
21
+ return false;
22
+ if (method !== "GET")
23
+ return false;
24
+ const auth = ctx.moduleAuth ?? null;
25
+ if (!auth) {
26
+ // LSP: 与成功路径同一 ok 方言(文档约定 { ok:false, error, code })
27
+ jsonV2Fail(res, "unauthorized: module identity missing", 401, "unauthorized");
28
+ return true;
29
+ }
30
+ if (path === "/api/sfmc/services") {
31
+ json(res, { services: deps.serviceRegistry.list() });
32
+ return true;
33
+ }
34
+ const m = path.match(/^\/api\/sfmc\/services\/([A-Za-z0-9_.]+)$/);
35
+ if (m && m[1]) {
36
+ const name = m[1];
37
+ const rawInput = params.get("input");
38
+ let payload = {};
39
+ if (rawInput) {
40
+ try {
41
+ payload = JSON.parse(rawInput);
42
+ }
43
+ catch (e) {
44
+ json(res, { ok: false, error: `invalid input json: ${e.message}`, code: "bad_request" }, 400);
45
+ return true;
46
+ }
47
+ }
48
+ try {
49
+ // 与 tx-runner.doService 对齐(LSP):HTTP service.get 与 tx.call 必须同契约 —
50
+ // 既要在 services.requires 里(由 dispatch 校验),也要有 service:<name> 权限。
51
+ assertModulePermission(auth.id, auth.permissions, Perm.service(name));
52
+ const out = await deps.serviceRegistry.dispatch(deps.enabled, auth.id, name, payload);
53
+ json(res, { ok: true, result: out.result });
54
+ }
55
+ catch (e) {
56
+ if (e instanceof PermissionDeniedError) {
57
+ json(res, { ok: false, error: e.message, code: "permission_denied" }, 403);
58
+ return true;
59
+ }
60
+ const err = e;
61
+ json(res, { ok: false, error: err.message, code: err.code ?? "internal" }, err.status ?? 500);
62
+ }
63
+ return true;
64
+ }
65
+ return false;
66
+ };
67
+ }
68
+ //# sourceMappingURL=service-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-routes.js","sourceRoot":"","sources":["../../src/routes/service-routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.test.d.ts","sourceRoot":"","sources":["../src/runtime.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ import { deepEqual, equal, rejects, throws } from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { SQL } from "sql-template-strings";
4
+ import { assertIdentifier, quoteIdentifier } from "./lib/identifiers.js";
5
+ import { parseNodeVersion } from "./lib/runtime.js";
6
+ import { raw, sql } from "./lib/sql-helpers.js";
7
+ import { DispatchError, ServiceRegistry } from "./service-registry.js";
8
+ test("parses Node versions", () => {
9
+ deepEqual(parseNodeVersion("22.5.1"), { major: 22, minor: 5, patch: 1 });
10
+ deepEqual(parseNodeVersion("v22.5.1"), { major: 22, minor: 5, patch: 1 });
11
+ });
12
+ test("accepts only safe SQL identifiers", () => {
13
+ equal(quoteIdentifier("sfmc_players", "table"), '"sfmc_players"');
14
+ throws(() => assertIdentifier("users; DROP TABLE x"), /Invalid SQL/);
15
+ });
16
+ test("sql() embeds trusted identifiers and binds values", () => {
17
+ const TABLE = "sfmc_economy_accounts";
18
+ const q = sql(`SELECT * FROM ${TABLE} WHERE player_id = ?`, ["p1"]);
19
+ equal(q.sql, "SELECT * FROM sfmc_economy_accounts WHERE player_id = ?");
20
+ deepEqual(q.values, ["p1"]);
21
+ });
22
+ test("raw() only works via append — template interpolation still becomes ?", () => {
23
+ const TABLE = "sfmc_economy_accounts";
24
+ const broken = SQL `SELECT * FROM ${raw(TABLE)} WHERE id = ${"p1"}`;
25
+ equal(broken.sql, "SELECT * FROM ? WHERE id = ?");
26
+ const ok = SQL `SELECT * FROM `.append(raw(TABLE)).append(SQL ` WHERE id = ${"p1"}`);
27
+ equal(ok.sql, "SELECT * FROM sfmc_economy_accounts WHERE id = ?");
28
+ deepEqual(ok.values, ["p1"]);
29
+ });
30
+ test("ServiceRegistry: provider self-call skips requires (LSP with tx.call path)", async () => {
31
+ const reg = new ServiceRegistry();
32
+ reg.registerHandler("feature-economy", "economy.stats.monthly", async () => ({ ok: true }));
33
+ const enabled = new Map([
34
+ [
35
+ "feature-economy",
36
+ {
37
+ id: "feature-economy",
38
+ version: "1.0.0",
39
+ permissions: [],
40
+ services: { provides: [{ name: "economy.stats.monthly" }], requires: [] },
41
+ db: { tables: [] },
42
+ config: { key: "economy" },
43
+ },
44
+ ],
45
+ ]);
46
+ const out = await reg.dispatch(enabled, "feature-economy", "economy.stats.monthly", {});
47
+ deepEqual(out, { ok: true, result: { ok: true } });
48
+ });
49
+ test("ServiceRegistry: missing service → no_such_service (not not_in_requires)", async () => {
50
+ const reg = new ServiceRegistry();
51
+ const enabled = new Map([
52
+ [
53
+ "feature-chat",
54
+ {
55
+ id: "feature-chat",
56
+ version: "1.0.0",
57
+ permissions: [],
58
+ services: { provides: [], requires: [] },
59
+ db: { tables: [] },
60
+ config: { key: "chat" },
61
+ },
62
+ ],
63
+ ]);
64
+ await rejects(() => reg.dispatch(enabled, "feature-chat", "economy.account.get", {}), (e) => e instanceof DispatchError && e.code === "no_such_service");
65
+ });
66
+ //# sourceMappingURL=runtime.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.test.js","sourceRoot":"","sources":["../src/runtime.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * schema-registry.ts — 收集模块 defineTable,统一建表
3
+ *
4
+ * 协议:
5
+ * - 模块 SAPI 启动时调 db.defineTable(name, columns, {softDelete})
6
+ * → POST /api/sfmc/db/define-table {moduleId, name, columns, softDelete}
7
+ * - schema-registry 收集所有调用,CAS 锁
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
13
+ *
14
+ * 列类型白名单:
15
+ * text / integer / real / blob
16
+ * 修饰: primary / notNull / unique / default / index
17
+ * softDelete = 自动加 _deleted_at INTEGER(列名带下划线) 和 _version INTEGER
18
+ */
19
+ import type { DatabaseSync } from "node:sqlite";
20
+ export type ColumnType = "text" | "integer" | "real" | "blob";
21
+ export interface ColumnDef {
22
+ type: ColumnType;
23
+ primary?: boolean;
24
+ notNull?: boolean;
25
+ unique?: boolean;
26
+ default?: Primitive;
27
+ index?: boolean;
28
+ ref?: string;
29
+ }
30
+ export type Primitive = string | number | boolean | null;
31
+ export interface DefineTableRequest {
32
+ name: string;
33
+ columns: Record<string, ColumnDef>;
34
+ softDelete?: boolean;
35
+ }
36
+ export interface DefineTableResult {
37
+ table: string;
38
+ created: boolean;
39
+ indices: string[];
40
+ }
41
+ export declare const PLATFORM_MODULE_ID = "__platform__";
42
+ export declare class SchemaRegistry {
43
+ private readonly db;
44
+ private readonly tables;
45
+ private finalized;
46
+ constructor(db: DatabaseSync);
47
+ /** 模块 SAPI 启动时调。 */
48
+ define(moduleId: string, req: DefineTableRequest): DefineTableResult;
49
+ /** 幂等地物理建表 + 建索引(供 define / finalize 共用)。 */
50
+ private createPhysical;
51
+ /**
52
+ * 真正建表的时刻。所有 enabled 模块 init 完成(或明确调)后再 finalize。
53
+ * CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS,幂等。
54
+ */
55
+ finalize(): {
56
+ tableNames: string[];
57
+ created: string[];
58
+ };
59
+ listTables(): string[];
60
+ isRegistered(name: string): boolean;
61
+ getOwner(name: string): string | undefined;
62
+ }
63
+ //# sourceMappingURL=schema-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-registry.d.ts","sourceRoot":"","sources":["../src/schema-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9D,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;AA6DjD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAe;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAC1D,OAAO,CAAC,SAAS,CAAS;gBAEd,EAAE,EAAE,YAAY;IAI5B,oBAAoB;IACpB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,iBAAiB;IAwCpE,6CAA6C;IAC7C,OAAO,CAAC,cAAc;IAWtB;;;OAGG;IACH,QAAQ,IAAI;QAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IA4BvD,UAAU,IAAI,MAAM,EAAE;IAItB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAInC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAG3C"}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * schema-registry.ts — 收集模块 defineTable,统一建表
3
+ *
4
+ * 协议:
5
+ * - 模块 SAPI 启动时调 db.defineTable(name, columns, {softDelete})
6
+ * → POST /api/sfmc/db/define-table {moduleId, name, columns, softDelete}
7
+ * - schema-registry 收集所有调用,CAS 锁
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
13
+ *
14
+ * 列类型白名单:
15
+ * text / integer / real / blob
16
+ * 修饰: primary / notNull / unique / default / index
17
+ * softDelete = 自动加 _deleted_at INTEGER(列名带下划线) 和 _version INTEGER
18
+ */
19
+ import { log } from "./lib/log.js";
20
+ export const PLATFORM_MODULE_ID = "__platform__";
21
+ const IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
22
+ const VALID_TYPES = new Set(["text", "integer", "real", "blob"]);
23
+ function assertValid(req, moduleId) {
24
+ if (!IDENT.test(req.name))
25
+ throw new Error(`[schema] ${moduleId}: invalid table name "${req.name}"`);
26
+ if (!req.columns || Object.keys(req.columns).length === 0) {
27
+ throw new Error(`[schema] ${moduleId}: ${req.name} has no columns`);
28
+ }
29
+ let pks = 0;
30
+ for (const [colName, def] of Object.entries(req.columns)) {
31
+ if (!IDENT.test(colName))
32
+ throw new Error(`[schema] ${moduleId}: invalid column name "${colName}" in ${req.name}`);
33
+ if (!VALID_TYPES.has(def.type)) {
34
+ throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} bad type "${def.type}"`);
35
+ }
36
+ if (def.primary)
37
+ pks++;
38
+ if (def.ref) {
39
+ const [t, c] = def.ref.split(".");
40
+ if (!t || !c || !IDENT.test(t) || !IDENT.test(c)) {
41
+ throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} invalid ref "${def.ref}"`);
42
+ }
43
+ }
44
+ }
45
+ if (pks > 1)
46
+ throw new Error(`[schema] ${moduleId}: ${req.name} has ${pks} primary keys (max 1)`);
47
+ }
48
+ /**
49
+ * 一次性把 columns 翻译成 CREATE TABLE。
50
+ * 例如 {id:{type:"text",primary:true},name:{type:"text",notNull:true}}
51
+ * → "id" TEXT PRIMARY KEY, "name" TEXT NOT NULL
52
+ */
53
+ function buildColumnClause(name, def) {
54
+ const parts = [`"${name}"`, def.type.toUpperCase()];
55
+ if (def.primary)
56
+ parts.push("PRIMARY KEY");
57
+ if (def.notNull && !def.primary)
58
+ parts.push("NOT NULL");
59
+ if (def.unique)
60
+ parts.push("UNIQUE");
61
+ if (def.default !== undefined && def.default !== null) {
62
+ if (typeof def.default === "string")
63
+ parts.push(`DEFAULT '${def.default.replace(/'/g, "''")}'`);
64
+ else
65
+ parts.push(`DEFAULT ${def.default}`);
66
+ }
67
+ if (def.ref)
68
+ parts.push(`REFERENCES ${def.ref}`);
69
+ return parts.join(" ");
70
+ }
71
+ /**
72
+ * 把一张表的所有列(含 softDelete 隐式列)拼成 CREATE TABLE 的列定义数组。
73
+ * 隐式 _deleted_at / _version 作为独立列在末尾追加「一次」(此前误在每个列
74
+ * 子句里各追加一次,且无逗号分隔,导致 softDelete 表建表 SQL 语法错误)。
75
+ */
76
+ function buildColumnList(columns, softDelete) {
77
+ const cols = [];
78
+ for (const [n, def] of Object.entries(columns)) {
79
+ cols.push(buildColumnClause(n, def));
80
+ }
81
+ if (softDelete) {
82
+ cols.push('"_deleted_at" INTEGER', '"_version" INTEGER DEFAULT 0');
83
+ }
84
+ return cols;
85
+ }
86
+ export class SchemaRegistry {
87
+ db;
88
+ tables = new Map();
89
+ finalized = false;
90
+ constructor(db) {
91
+ this.db = db;
92
+ }
93
+ /** 模块 SAPI 启动时调。 */
94
+ define(moduleId, req) {
95
+ if (this.finalized) {
96
+ throw new Error(`[schema] registry 已 finalize,模块 ${moduleId} 不能再 defineTable`);
97
+ }
98
+ assertValid(req, moduleId);
99
+ const existing = this.tables.get(req.name);
100
+ if (existing) {
101
+ if (existing.moduleId === moduleId) {
102
+ // 同模块重复定义:校验 columns 一致
103
+ if (JSON.stringify(existing.columns) !== JSON.stringify(req.columns)) {
104
+ throw new Error(`[schema] ${moduleId} 重复 defineTable "${req.name}",columns 不一致`);
105
+ }
106
+ if (existing.softDelete !== (req.softDelete ?? true)) {
107
+ throw new Error(`[schema] ${moduleId} 重复 defineTable "${req.name}",softDelete 不一致`);
108
+ }
109
+ log.info(`[schema] ${moduleId} 重复 define "${req.name}" 跳过`);
110
+ return { table: req.name, created: false, indices: [] };
111
+ }
112
+ throw new Error(`[schema] 表 "${req.name}" 已被模块 ${existing.moduleId} 定义,${moduleId} 不能抢同名表`);
113
+ }
114
+ const softDelete = req.softDelete ?? true;
115
+ const defined = {
116
+ moduleId,
117
+ name: req.name,
118
+ columns: req.columns,
119
+ softDelete,
120
+ };
121
+ this.tables.set(req.name, defined);
122
+ // 立即物理建表(幂等 CREATE TABLE IF NOT EXISTS)。
123
+ // 原设计是「define 收集 → finalize 统一建表」,但 finalize() 从未被调用,
124
+ // 导致模块表永远不会落地、后续 insert/query 全部 no such table。
125
+ // 采用 define 即建表:碰撞检测已在上方按内存表完成,不影响单一 owner 保证。
126
+ this.createPhysical(defined);
127
+ return { table: req.name, created: true, indices: [] };
128
+ }
129
+ /** 幂等地物理建表 + 建索引(供 define / finalize 共用)。 */
130
+ createPhysical(t) {
131
+ const cols = buildColumnList(t.columns, t.softDelete);
132
+ this.db.exec(`CREATE TABLE IF NOT EXISTS "${t.name}" (${cols.join(", ")})`);
133
+ for (const [n, def] of Object.entries(t.columns)) {
134
+ if (def.index) {
135
+ const idxName = `idx_${t.name}_${n}`.slice(0, 60);
136
+ this.db.exec(`CREATE INDEX IF NOT EXISTS "${idxName}" ON "${t.name}"("${n}")`);
137
+ }
138
+ }
139
+ }
140
+ /**
141
+ * 真正建表的时刻。所有 enabled 模块 init 完成(或明确调)后再 finalize。
142
+ * CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS,幂等。
143
+ */
144
+ finalize() {
145
+ if (this.finalized)
146
+ return { tableNames: [...this.tables.keys()], created: [] };
147
+ this.finalized = true;
148
+ const created = [];
149
+ for (const t of this.tables.values()) {
150
+ const cols = buildColumnList(t.columns, t.softDelete);
151
+ const ddl = `CREATE TABLE IF NOT EXISTS "${t.name}" (${cols.join(", ")})`;
152
+ this.db.exec(ddl);
153
+ for (const [n, def] of Object.entries(t.columns)) {
154
+ if (def.index) {
155
+ const idxName = `idx_${t.name}_${n}`.slice(0, 60);
156
+ this.db.exec(`CREATE INDEX IF NOT EXISTS "${idxName}" ON "${t.name}"("${n}")`);
157
+ }
158
+ }
159
+ const exists = this.db
160
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?")
161
+ .get(t.name)?.name === t.name;
162
+ if (exists)
163
+ created.push(t.name);
164
+ log.success(`[schema] ${t.moduleId} → ${t.name} (softDelete=${t.softDelete})`);
165
+ }
166
+ return { tableNames: [...this.tables.keys()], created };
167
+ }
168
+ listTables() {
169
+ return [...this.tables.keys()];
170
+ }
171
+ isRegistered(name) {
172
+ return this.tables.has(name);
173
+ }
174
+ getOwner(name) {
175
+ return this.tables.get(name)?.moduleId;
176
+ }
177
+ }
178
+ //# sourceMappingURL=schema-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-registry.js","sourceRoot":"","sources":["../src/schema-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * server.ts — HTTP 服务 + 控制台
3
+ */
4
+ import http from "node:http";
5
+ import readline from "node:readline";
6
+ import type { DatabaseSync } from "node:sqlite";
7
+ interface CheckResult {
8
+ ok: boolean;
9
+ port: number;
10
+ error?: string;
11
+ }
12
+ export declare function checkPortConflict(port: number): Promise<CheckResult>;
13
+ export type ServerEnv = {
14
+ PORT: number;
15
+ HOST: string;
16
+ AUTH_TOKEN?: string;
17
+ };
18
+ export type Handler = (req: http.IncomingMessage, res: http.ServerResponse) => void | Promise<void>;
19
+ export interface ServerOptions {
20
+ env: ServerEnv;
21
+ handle: Handler;
22
+ onListening?: (server: http.Server) => void;
23
+ }
24
+ export declare function createServer({ handle, env, onListening }: ServerOptions): http.Server;
25
+ export declare function startConsole(server: http.Server, db: DatabaseSync | null): readline.Interface;
26
+ export {};
27
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,UAAU,WAAW;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAgBpE;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEpG,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;CAC7C;AAED,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,aAAa,GAAG,IAAI,CAAC,MAAM,CA2BrF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,YAAY,GAAG,IAAI,GAAG,QAAQ,CAAC,SAAS,CAyB7F"}