@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
package/dist/server.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * server.ts — HTTP 服务 + 控制台
3
+ */
4
+ import http from "node:http";
5
+ import net from "node:net";
6
+ import readline from "node:readline";
7
+ import { log } from "./lib/log.js";
8
+ export function checkPortConflict(port) {
9
+ return new Promise((resolve) => {
10
+ const server = net.createServer();
11
+ server.once("error", (err) => {
12
+ const result = { ok: false, port };
13
+ if (err.code) {
14
+ result.error = err.code;
15
+ }
16
+ resolve(result);
17
+ });
18
+ server.once("listening", () => {
19
+ server.close();
20
+ resolve({ ok: true, port });
21
+ });
22
+ server.listen(port, "127.0.0.1");
23
+ });
24
+ }
25
+ export function createServer({ handle, env, onListening }) {
26
+ const server = http.createServer((req, res) => {
27
+ const startedAt = Date.now();
28
+ let bodyStr = "";
29
+ const chunks = [];
30
+ req.on("data", (c) => chunks.push(c));
31
+ req.on("end", () => {
32
+ bodyStr = Buffer.concat(chunks).toString("utf-8").slice(0, 300);
33
+ req.bodyBuffer = bodyStr;
34
+ });
35
+ res.once("finish", () => {
36
+ const quietPoll = (req.url ?? "").startsWith("/api/sfmc/messages?") && res.statusCode < 400;
37
+ const bodySnippet = req.method === "GET" ? "" : ` ${bodyStr.slice(0, 200)}`;
38
+ if (!quietPoll)
39
+ log.info(`[HTTP] ${req.method} ${req.url} ${res.statusCode} ${Date.now() - startedAt}ms${bodySnippet}`);
40
+ });
41
+ void handle(req, res);
42
+ });
43
+ server.listen(env.PORT, env.HOST, () => {
44
+ log.success(`HTTP 服务已启动,端口 ${env.PORT} (loopback only)`);
45
+ log.info(`API 健康检查: http://${env.HOST}:${env.PORT}/api/health`);
46
+ log.info(`鉴权: ${env.AUTH_TOKEN ? "已启用 token" : "未启用"}`);
47
+ if (onListening)
48
+ onListening(server);
49
+ });
50
+ return server;
51
+ }
52
+ export function startConsole(server, db) {
53
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
54
+ rl.on("line", (line) => {
55
+ const cmd = line.trim().toLowerCase();
56
+ if (cmd === "help") {
57
+ console.log("DB Server 可用命令:");
58
+ console.log(" help — 显示帮助");
59
+ console.log(" status — 显示服务状态");
60
+ console.log(" stop — 停止服务");
61
+ }
62
+ else if (cmd === "status") {
63
+ console.log(`状态: 运行中`);
64
+ console.log(` HTTP 端口: ${server.address().port}`);
65
+ console.log(` 数据库: ${db ? "(已连接)" : "(未连接)"}`);
66
+ }
67
+ else if (cmd === "stop") {
68
+ log.info("正在停止服务...");
69
+ rl.close();
70
+ server.close();
71
+ if (db)
72
+ db.close();
73
+ process.exit(0);
74
+ }
75
+ else {
76
+ log.warn(`未知命令: ${cmd},输入 help 查看帮助`);
77
+ }
78
+ });
79
+ rl.on("SIGINT", () => process.exit());
80
+ return rl;
81
+ }
82
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * service-registry.ts — 跨模块 service.get 后端
3
+ *
4
+ * 协议:
5
+ * HTTP GET /api/sfmc/services/<name>?input=<urlencoded-json>
6
+ * 鉴权:Bearer module_token + ?moduleId=<callerId>
7
+ *
8
+ * 派发:
9
+ * handler 注册在 db-server 进程内存。外层 db.tx 内的 tx.call 会注入
10
+ * ctx.tx = { query, db },handler 不得再 BEGIN(方案 A)。
11
+ */
12
+ import type { DatabaseSync } from "node:sqlite";
13
+ import type { ModuleManifestV2 } from "./manifest-loader.js";
14
+ import type { QueryFn } from "./lib/sqlite.js";
15
+ export interface ServiceCallResult {
16
+ ok: true;
17
+ result: unknown;
18
+ }
19
+ export interface ServiceTxContext {
20
+ query: QueryFn;
21
+ db: DatabaseSync;
22
+ }
23
+ export interface ServiceDispatchContext {
24
+ callerModuleId: string;
25
+ payload: unknown;
26
+ /** 外层 db.tx 已打开时注入;handler 复用连接,禁止嵌套 BEGIN */
27
+ tx?: ServiceTxContext;
28
+ }
29
+ export type ServiceHandler = (ctx: ServiceDispatchContext) => Promise<unknown>;
30
+ export declare class ServiceRegistry {
31
+ private readonly handlers;
32
+ /** 提供方在 db-server 启动期注册 handler */
33
+ registerHandler(moduleId: string, name: string, handle: ServiceHandler): void;
34
+ unregisterHandler(name: string): void;
35
+ list(): Array<{
36
+ name: string;
37
+ moduleId: string;
38
+ }>;
39
+ dispatch(enabled: Map<string, ModuleManifestV2>, callerModuleId: string, name: string, payload: unknown, tx?: ServiceTxContext): Promise<ServiceCallResult>;
40
+ }
41
+ export declare class DispatchError extends Error {
42
+ code: "no_such_service" | "not_in_requires" | "forbidden" | "internal" | "domain_error";
43
+ status: number;
44
+ constructor(message: string, code: "no_such_service" | "not_in_requires" | "forbidden" | "internal" | "domain_error", status: number);
45
+ }
46
+ //# sourceMappingURL=service-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-registry.d.ts","sourceRoot":"","sources":["../src/service-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,EAAE,EAAE,YAAY,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,EAAE,CAAC,EAAE,gBAAgB,CAAC;CACvB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,sBAAsB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAO/E,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IAEjE,mCAAmC;IACnC,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI;IAO7E,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIrC,IAAI,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAI3C,QAAQ,CACZ,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACtC,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,EAAE,CAAC,EAAE,gBAAgB,GACpB,OAAO,CAAC,iBAAiB,CAAC;CAuC9B;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,CAAC;IACxF,MAAM,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,EACvF,MAAM,EAAE,MAAM;CAMjB"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * service-registry.ts — 跨模块 service.get 后端
3
+ *
4
+ * 协议:
5
+ * HTTP GET /api/sfmc/services/<name>?input=<urlencoded-json>
6
+ * 鉴权:Bearer module_token + ?moduleId=<callerId>
7
+ *
8
+ * 派发:
9
+ * handler 注册在 db-server 进程内存。外层 db.tx 内的 tx.call 会注入
10
+ * ctx.tx = { query, db },handler 不得再 BEGIN(方案 A)。
11
+ */
12
+ export class ServiceRegistry {
13
+ handlers = new Map();
14
+ /** 提供方在 db-server 启动期注册 handler */
15
+ registerHandler(moduleId, name, handle) {
16
+ if (this.handlers.has(name)) {
17
+ throw new Error(`[service] "${name}" 已被 ${this.handlers.get(name)?.moduleId} 注册,${moduleId} 抢注`);
18
+ }
19
+ this.handlers.set(name, { moduleId, handle });
20
+ }
21
+ unregisterHandler(name) {
22
+ this.handlers.delete(name);
23
+ }
24
+ list() {
25
+ return [...this.handlers.entries()].map(([name, h]) => ({ name, moduleId: h.moduleId }));
26
+ }
27
+ async dispatch(enabled, callerModuleId, name, payload, tx) {
28
+ const handler = this.handlers.get(name);
29
+ if (!handler) {
30
+ throw new DispatchError(`service "${name}" 未注册`, "no_such_service", 404);
31
+ }
32
+ if (!enabled.has(handler.moduleId)) {
33
+ throw new DispatchError(`service "${name}" 提供方 ${handler.moduleId} 未 enabled`, "forbidden", 403);
34
+ }
35
+ const caller = enabled.get(callerModuleId);
36
+ if (!caller) {
37
+ throw new DispatchError(`调用方 ${callerModuleId} 未 enabled`, "forbidden", 403);
38
+ }
39
+ const declared = caller.services.requires.find((r) => r.name === name);
40
+ // 提供方调自己的 service 免 requires(例如 economy 白皮书调 stats.monthly)
41
+ if (!declared && callerModuleId !== handler.moduleId) {
42
+ throw new DispatchError(`${callerModuleId} 的 manifest.services.requires 未声明 "${name}"`, "not_in_requires", 403);
43
+ }
44
+ try {
45
+ const result = await handler.handle({
46
+ callerModuleId,
47
+ payload,
48
+ ...(tx ? { tx } : {}),
49
+ });
50
+ return { ok: true, result };
51
+ }
52
+ catch (e) {
53
+ // 保留 handler 抛出的领域/鉴权错误契约,勿压成 500 internal(LSP)
54
+ if (e instanceof DispatchError)
55
+ throw e;
56
+ const err = e;
57
+ if (typeof err.status === "number" && err.code) {
58
+ throw new DispatchError(err.message, err.code, err.status);
59
+ }
60
+ throw new DispatchError(`handler 抛错: ${err.message}`, "internal", 500);
61
+ }
62
+ }
63
+ }
64
+ export class DispatchError extends Error {
65
+ code;
66
+ status;
67
+ constructor(message, code, status) {
68
+ super(message);
69
+ this.code = code;
70
+ this.status = status;
71
+ }
72
+ }
73
+ //# sourceMappingURL=service-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-registry.js","sourceRoot":"","sources":["../src/service-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * services/builtin-handlers.ts — 进程内置 service 插件注册表
3
+ *
4
+ * 新增内置 handler 只追加 BUILTIN_SERVICE_PLUGINS(OCP),
5
+ * 勿在 index.ts 再写 if (enabledSet.has(...)) registerXxx 链。
6
+ */
7
+ import type { DatabaseSync } from "node:sqlite";
8
+ import type { QueryFn } from "../lib/sqlite.js";
9
+ import type { ServiceRegistry } from "../service-registry.js";
10
+ export type BuiltinServiceDeps = {
11
+ query: QueryFn;
12
+ db: DatabaseSync;
13
+ };
14
+ export type BuiltinServicePlugin = {
15
+ moduleId: string;
16
+ register: (registry: ServiceRegistry, deps: BuiltinServiceDeps) => void;
17
+ };
18
+ /** 内置 service 插件清单 — 唯一扩展点 */
19
+ export declare const BUILTIN_SERVICE_PLUGINS: BuiltinServicePlugin[];
20
+ /** 按 enabledSet 注册内置插件,返回已注册插件数 */
21
+ export declare function registerEnabledBuiltinServices(registry: ServiceRegistry, deps: BuiltinServiceDeps, enabledSet: Set<string>): number;
22
+ //# sourceMappingURL=builtin-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin-handlers.d.ts","sourceRoot":"","sources":["../../src/services/builtin-handlers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,MAAM,MAAM,kBAAkB,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,YAAY,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACzE,CAAC;AAEF,8BAA8B;AAC9B,eAAO,MAAM,uBAAuB,EAAE,oBAAoB,EAEzD,CAAC;AAEF,mCAAmC;AACnC,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,kBAAkB,EACxB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB,MAAM,CAQR"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * services/builtin-handlers.ts — 进程内置 service 插件注册表
3
+ *
4
+ * 新增内置 handler 只追加 BUILTIN_SERVICE_PLUGINS(OCP),
5
+ * 勿在 index.ts 再写 if (enabledSet.has(...)) registerXxx 链。
6
+ */
7
+ import { registerEconomyHandlers } from "./economy-handlers.js";
8
+ /** 内置 service 插件清单 — 唯一扩展点 */
9
+ export const BUILTIN_SERVICE_PLUGINS = [
10
+ { moduleId: "feature-economy", register: registerEconomyHandlers },
11
+ ];
12
+ /** 按 enabledSet 注册内置插件,返回已注册插件数 */
13
+ export function registerEnabledBuiltinServices(registry, deps, enabledSet) {
14
+ let n = 0;
15
+ for (const plugin of BUILTIN_SERVICE_PLUGINS) {
16
+ if (!enabledSet.has(plugin.moduleId))
17
+ continue;
18
+ plugin.register(registry, deps);
19
+ n += 1;
20
+ }
21
+ return n;
22
+ }
23
+ //# sourceMappingURL=builtin-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin-handlers.js","sourceRoot":"","sources":["../../src/services/builtin-handlers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAShE,8BAA8B;AAC9B,MAAM,CAAC,MAAM,uBAAuB,GAA2B;IAC7D,EAAE,QAAQ,EAAE,iBAAiB,EAAE,QAAQ,EAAE,uBAAuB,EAAE;CACnE,CAAC;AAEF,mCAAmC;AACnC,MAAM,UAAU,8BAA8B,CAC5C,QAAyB,EACzB,IAAwB,EACxB,UAAuB;IAEvB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,MAAM,IAAI,uBAAuB,EAAE,CAAC;QAC7C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,SAAS;QAC/C,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * services/economy-handlers.ts — feature-economy 的 7 个 service handler
3
+ *
4
+ * 全部跑在 db-server 进程内,复用 domain/economy.ts。
5
+ * ctx.tx 存在时 alreadyInTx=true,与外层 db.tx 共享同一 SQLite 事务。
6
+ */
7
+ import type { DatabaseSync } from "node:sqlite";
8
+ import type { QueryFn } from "../lib/sqlite.js";
9
+ import { type ServiceRegistry } from "../service-registry.js";
10
+ export declare function registerEconomyHandlers(registry: ServiceRegistry, deps: {
11
+ query: QueryFn;
12
+ db: DatabaseSync;
13
+ }): void;
14
+ //# sourceMappingURL=economy-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"economy-handlers.d.ts","sourceRoot":"","sources":["../../src/services/economy-handlers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAyC7E,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,YAAY,CAAA;CAAE,GACzC,IAAI,CAgIN"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * services/economy-handlers.ts — feature-economy 的 7 个 service handler
3
+ *
4
+ * 全部跑在 db-server 进程内,复用 domain/economy.ts。
5
+ * ctx.tx 存在时 alreadyInTx=true,与外层 db.tx 共享同一 SQLite 事务。
6
+ */
7
+ import { DispatchError } from "../service-registry.js";
8
+ import { applyEconomyTransaction, getEconomyAccount, listDailyTasks, monthlyEconomyStats, submitDailyTaskTx, } from "../domain/economy.js";
9
+ const MODULE_ID = "feature-economy";
10
+ function asObj(payload) {
11
+ return payload && typeof payload === "object" ? payload : {};
12
+ }
13
+ function str(v, fallback = "") {
14
+ return v === undefined || v === null ? fallback : String(v);
15
+ }
16
+ function num(v) {
17
+ return Number(v);
18
+ }
19
+ /** 领域失败保留 status/code,避免被 registry 压成 500 internal(LSP) */
20
+ function unwrapOk(result) {
21
+ if (!result.ok) {
22
+ throw new DispatchError(result.error, "domain_error", result.status);
23
+ }
24
+ return {
25
+ transactionId: result.transactionId,
26
+ replayed: result.replayed ?? false,
27
+ source: result.source,
28
+ target: result.target,
29
+ // 方便调用方读单边余额
30
+ balance: result.target?.balanceAfter ?? result.source?.balanceAfter,
31
+ version: result.target?.version ?? result.source?.version,
32
+ };
33
+ }
34
+ export function registerEconomyHandlers(registry, deps) {
35
+ const standaloneQuery = deps.query;
36
+ registry.registerHandler(MODULE_ID, "economy.account.get", async (ctx) => {
37
+ const p = asObj(ctx.payload);
38
+ const playerId = str(p.playerId ?? p.player_id);
39
+ if (!playerId)
40
+ throw new DispatchError("missing_playerId", "domain_error", 400);
41
+ const q = ctx.tx?.query ?? standaloneQuery;
42
+ return getEconomyAccount(q, playerId, str(p.playerName ?? p.player_name));
43
+ });
44
+ registry.registerHandler(MODULE_ID, "economy.account.debit", async (ctx) => {
45
+ const p = asObj(ctx.payload);
46
+ const playerId = str(p.playerId ?? p.player_id ?? p.actorId);
47
+ const amount = num(p.amount);
48
+ if (!playerId)
49
+ throw new DispatchError("missing_playerId", "domain_error", 400);
50
+ const q = ctx.tx?.query ?? standaloneQuery;
51
+ const db = ctx.tx?.db ?? deps.db;
52
+ const idem = str(p.idempotencyKey ?? p.idempotency_key);
53
+ const result = applyEconomyTransaction(q, db, {
54
+ actorId: playerId,
55
+ sourcePlayerId: playerId,
56
+ sourcePlayerName: str(p.playerName ?? p.player_name),
57
+ amount,
58
+ type: "debit",
59
+ reason: str(p.reason),
60
+ referenceType: str(p.referenceType ?? p.meta?.type),
61
+ referenceId: str(p.referenceId ?? p.meta?.redpacketId),
62
+ ...(idem ? { idempotencyKey: idem } : {}),
63
+ }, { alreadyInTx: !!ctx.tx });
64
+ return unwrapOk(result);
65
+ });
66
+ registry.registerHandler(MODULE_ID, "economy.account.credit", async (ctx) => {
67
+ const p = asObj(ctx.payload);
68
+ const playerId = str(p.playerId ?? p.player_id ?? p.actorId);
69
+ const amount = num(p.amount);
70
+ if (!playerId)
71
+ throw new DispatchError("missing_playerId", "domain_error", 400);
72
+ const q = ctx.tx?.query ?? standaloneQuery;
73
+ const db = ctx.tx?.db ?? deps.db;
74
+ const idem = str(p.idempotencyKey ?? p.idempotency_key);
75
+ const result = applyEconomyTransaction(q, db, {
76
+ actorId: playerId,
77
+ targetPlayerId: playerId,
78
+ targetPlayerName: str(p.playerName ?? p.player_name),
79
+ amount,
80
+ type: "credit",
81
+ reason: str(p.reason),
82
+ referenceType: str(p.referenceType ?? p.meta?.type),
83
+ referenceId: str(p.referenceId ?? p.meta?.redpacketId),
84
+ ...(idem ? { idempotencyKey: idem } : {}),
85
+ }, { alreadyInTx: !!ctx.tx });
86
+ return unwrapOk(result);
87
+ });
88
+ registry.registerHandler(MODULE_ID, "economy.account.transfer", async (ctx) => {
89
+ const p = asObj(ctx.payload);
90
+ const from = str(p.fromPlayerId ?? p.sourcePlayerId ?? p.source_player_id ?? p.actorId ?? p.actor_id) || "";
91
+ const to = str(p.toPlayerId ?? p.targetPlayerId ?? p.target_player_id) || "";
92
+ const amount = num(p.amount);
93
+ if (!from || !to)
94
+ throw new DispatchError("missing_from_or_to", "domain_error", 400);
95
+ const q = ctx.tx?.query ?? standaloneQuery;
96
+ const db = ctx.tx?.db ?? deps.db;
97
+ const idem = str(p.idempotencyKey ?? p.idempotency_key);
98
+ const result = applyEconomyTransaction(q, db, {
99
+ actorId: from,
100
+ sourcePlayerId: from,
101
+ sourcePlayerName: str(p.fromPlayerName ?? p.sourcePlayerName),
102
+ targetPlayerId: to,
103
+ targetPlayerName: str(p.toPlayerName ?? p.targetPlayerName),
104
+ amount,
105
+ type: "transfer",
106
+ reason: str(p.reason),
107
+ ...(idem ? { idempotencyKey: idem } : {}),
108
+ }, { alreadyInTx: !!ctx.tx });
109
+ return unwrapOk(result);
110
+ });
111
+ // 信封与 daily-task 消费方对齐:{ tasks: [...] }
112
+ registry.registerHandler(MODULE_ID, "economy.dailyTasks.list", async (ctx) => {
113
+ const p = asObj(ctx.payload);
114
+ const q = ctx.tx?.query ?? standaloneQuery;
115
+ return {
116
+ tasks: listDailyTasks(q, {
117
+ status: str(p.status) || "active",
118
+ includeExpired: Boolean(p.includeExpired),
119
+ }),
120
+ };
121
+ });
122
+ // 信封与 daily-task 消费方对齐:成功/失败都带 ok,业务失败不抛(避免物品已扣却无法走 !ok 退还分支)
123
+ registry.registerHandler(MODULE_ID, "economy.dailyTasks.submit", async (ctx) => {
124
+ const p = asObj(ctx.payload);
125
+ const actorId = str(p.actorId ?? p.playerId ?? p.player_id);
126
+ const taskId = str(p.taskId ?? p.task_id);
127
+ const quantity = num(p.quantity ?? 1);
128
+ if (!actorId || !taskId) {
129
+ return { ok: false, error: "missing_actor_or_task", status: 400 };
130
+ }
131
+ const q = ctx.tx?.query ?? standaloneQuery;
132
+ const db = ctx.tx?.db ?? deps.db;
133
+ const result = submitDailyTaskTx(q, db, { actorId, taskId, quantity }, { alreadyInTx: !!ctx.tx });
134
+ if (!result.ok) {
135
+ return { ok: false, error: result.error, status: result.status };
136
+ }
137
+ return { ok: true, ...result.data };
138
+ });
139
+ registry.registerHandler(MODULE_ID, "economy.stats.monthly", async (ctx) => {
140
+ const q = ctx.tx?.query ?? standaloneQuery;
141
+ return monthlyEconomyStats(q);
142
+ });
143
+ }
144
+ //# sourceMappingURL=economy-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"economy-handlers.js","sourceRoot":"","sources":["../../src/services/economy-handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * tx-runner.ts — 事务 RPC 处理器
3
+ *
4
+ * 协议:
5
+ * POST /api/sfmc/db/tx
6
+ * body = { moduleId, steps: TxStep[] }
7
+ * reply: { ok: true } | { ok: false, step: <index>, error, code }
8
+ *
9
+ * 流程:
10
+ * 1. 校验 moduleId in enabled + permission "db:write:*"
11
+ * 2. BEGIN IMMEDIATE
12
+ * 3. 顺序跑 steps — 任一抛错 → ROLLBACK + reply { ok:false, step:<i>, ... }
13
+ * 4. 全过 → COMMIT + reply { ok:true }
14
+ *
15
+ * step.op:
16
+ * - query { table, opts? }
17
+ * - get { table, id }
18
+ * - insert { table, row }
19
+ * - update { table, id, patch }
20
+ * - delete { table, id, hard }
21
+ * - audit { table, rowId, action, data? } → 平台 sfmc__audit 表
22
+ * - service { name, input } → 派发;失败抛错回退整事务
23
+ *
24
+ * 事务内 service call:不持有 db handle 给 handler(避免并发写) —
25
+ * 实现:handler 是"同步纯函数",返回 result 写回 step 的输出。
26
+ * 若 handler 自己也想写 db,应通过其它接口在事务外做(避免嵌套)。
27
+ */
28
+ import type { DatabaseSync } from "node:sqlite";
29
+ import type { SchemaRegistry } from "./schema-registry.js";
30
+ import type { WhereExpr } from "./where.js";
31
+ import { type ServiceRegistry } from "./service-registry.js";
32
+ import type { ModuleManifestV2 } from "./manifest-loader.js";
33
+ export interface TxStepQuery {
34
+ op: "query";
35
+ table: string;
36
+ opts?: {
37
+ where?: WhereExpr;
38
+ orderBy?: {
39
+ col: string;
40
+ dir?: "asc" | "desc";
41
+ };
42
+ limit?: number;
43
+ offset?: number;
44
+ };
45
+ }
46
+ export interface TxStepGet {
47
+ op: "get";
48
+ table: string;
49
+ id: string;
50
+ }
51
+ export interface TxStepInsert {
52
+ op: "insert";
53
+ table: string;
54
+ row: Record<string, unknown>;
55
+ }
56
+ export interface TxStepUpdate {
57
+ op: "update";
58
+ table: string;
59
+ id: string;
60
+ patch: Record<string, unknown>;
61
+ }
62
+ export interface TxStepDelete {
63
+ op: "delete";
64
+ table: string;
65
+ id: string;
66
+ hard: boolean;
67
+ }
68
+ export interface TxStepAudit {
69
+ op: "audit";
70
+ table: string;
71
+ rowId: string;
72
+ action: string;
73
+ data?: Record<string, unknown>;
74
+ }
75
+ export interface TxStepService {
76
+ op: "service";
77
+ name: string;
78
+ input: Record<string, unknown>;
79
+ }
80
+ export type TxStep = TxStepQuery | TxStepGet | TxStepInsert | TxStepUpdate | TxStepDelete | TxStepAudit | TxStepService;
81
+ export type TxStepResult = {
82
+ op: "query";
83
+ rows: Record<string, unknown>[];
84
+ } | {
85
+ op: "get";
86
+ row: Record<string, unknown> | null;
87
+ } | {
88
+ op: "insert";
89
+ row: Record<string, unknown>;
90
+ } | {
91
+ op: "update";
92
+ row: Record<string, unknown>;
93
+ } | {
94
+ op: "delete";
95
+ changes: number;
96
+ } | {
97
+ op: "audit";
98
+ changes: number;
99
+ } | {
100
+ op: "service";
101
+ result: unknown;
102
+ };
103
+ export interface TxResponse {
104
+ ok: true;
105
+ results: TxStepResult[];
106
+ }
107
+ export type TxError = {
108
+ ok: false;
109
+ step: number;
110
+ error: string;
111
+ code: "permission_denied" | "forbidden" | "no_such_service" | "not_in_requires" | "domain_error" | "internal" | "no_such_table";
112
+ };
113
+ export interface TxRequest {
114
+ moduleId: string;
115
+ steps: TxStep[];
116
+ }
117
+ export interface TxRunnerDeps {
118
+ db: DatabaseSync;
119
+ query: import("./lib/sqlite.js").QueryFn;
120
+ schema: SchemaRegistry;
121
+ serviceRegistry: ServiceRegistry;
122
+ enabled: Map<string, ModuleManifestV2>;
123
+ }
124
+ export declare class TxRunner {
125
+ private readonly deps;
126
+ constructor(deps: TxRunnerDeps);
127
+ run(req: TxRequest): Promise<TxResponse | TxError>;
128
+ private rollback;
129
+ /** 单 step 由对应权限检查 + SQL 路径执行 */
130
+ private runOne;
131
+ private requireTableRead;
132
+ private requireTableWrite;
133
+ private assertTableRegistered;
134
+ private doQuery;
135
+ /** 解析主键 WHERE。单列直接等值;联合主键支持 `a|b|c`(与 data-backup 约定一致)。 */
136
+ private pkWhere;
137
+ private doGet;
138
+ private doInsert;
139
+ private doUpdate;
140
+ private doDelete;
141
+ private doAudit;
142
+ private doService;
143
+ }
144
+ //# sourceMappingURL=tx-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tx-runner.d.ts","sourceRoot":"","sources":["../src/tx-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAa7D,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,SAAS,CAAC;QAClB,OAAO,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;SAAE,CAAC;QAChD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,SAAS,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,MAAM,MAAM,GACd,WAAW,GACX,SAAS,GACT,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,WAAW,GACX,aAAa,CAAC;AAElB,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CAAE,GAChD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAAE,GAClD;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC9C;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC9C;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAEvC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EACA,mBAAmB,GACnB,WAAW,GACX,iBAAiB,GACjB,iBAAiB,GACjB,cAAc,GACd,UAAU,GACV,eAAe,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,YAAY,CAAC;IACjB,KAAK,EAAE,OAAO,iBAAiB,EAAE,OAAO,CAAC;IACzC,MAAM,EAAE,cAAc,CAAC;IACvB,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACxC;AAED,qBAAa,QAAQ;IACP,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,YAAY;IAEzC,GAAG,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC;IAyDxD,OAAO,CAAC,QAAQ;IAQhB,gCAAgC;YAClB,MAAM;IAuBpB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,OAAO;IA2Bf,4DAA4D;IAC5D,OAAO,CAAC,OAAO;IA0Bf,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,QAAQ;IAmBhB,OAAO,CAAC,QAAQ;IAkBhB,OAAO,CAAC,QAAQ;IAsBhB,OAAO,CAAC,OAAO;YAYD,SAAS;CAiBxB"}