@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.
- package/README.md +21 -0
- package/dist/db-tables.d.ts +13 -0
- package/dist/db-tables.d.ts.map +1 -0
- package/dist/db-tables.js +35 -0
- package/dist/db-tables.js.map +1 -0
- package/dist/domain/bridge.d.ts +48 -0
- package/dist/domain/bridge.d.ts.map +1 -0
- package/dist/domain/bridge.js +82 -0
- package/dist/domain/bridge.js.map +1 -0
- package/dist/domain/economy.d.ts +120 -0
- package/dist/domain/economy.d.ts.map +1 -0
- package/dist/domain/economy.js +321 -0
- package/dist/domain/economy.js.map +1 -0
- package/dist/domain/schema.d.ts +26 -0
- package/dist/domain/schema.d.ts.map +1 -0
- package/dist/domain/schema.js +462 -0
- package/dist/domain/schema.js.map +1 -0
- package/dist/domain/transaction.d.ts +67 -0
- package/dist/domain/transaction.d.ts.map +1 -0
- package/dist/domain/transaction.js +85 -0
- package/dist/domain/transaction.js.map +1 -0
- package/dist/env.d.ts +22 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +94 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +313 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/http.d.ts +18 -0
- package/dist/lib/http.d.ts.map +1 -0
- package/dist/lib/http.js +43 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/idempotency-store.d.ts +28 -0
- package/dist/lib/idempotency-store.d.ts.map +1 -0
- package/dist/lib/idempotency-store.js +49 -0
- package/dist/lib/idempotency-store.js.map +1 -0
- package/dist/lib/idempotency.d.ts +9 -0
- package/dist/lib/idempotency.d.ts.map +1 -0
- package/dist/lib/idempotency.js +14 -0
- package/dist/lib/idempotency.js.map +1 -0
- package/dist/lib/identifiers.d.ts +3 -0
- package/dist/lib/identifiers.d.ts.map +1 -0
- package/dist/lib/identifiers.js +18 -0
- package/dist/lib/identifiers.js.map +1 -0
- package/dist/lib/log.d.ts +11 -0
- package/dist/lib/log.d.ts.map +1 -0
- package/dist/lib/log.js +15 -0
- package/dist/lib/log.js.map +1 -0
- package/dist/lib/module-state.d.ts +15 -0
- package/dist/lib/module-state.d.ts.map +1 -0
- package/dist/lib/module-state.js +27 -0
- package/dist/lib/module-state.js.map +1 -0
- package/dist/lib/runtime.d.ts +21 -0
- package/dist/lib/runtime.d.ts.map +1 -0
- package/dist/lib/runtime.js +34 -0
- package/dist/lib/runtime.js.map +1 -0
- package/dist/lib/sql-helpers.d.ts +31 -0
- package/dist/lib/sql-helpers.d.ts.map +1 -0
- package/dist/lib/sql-helpers.js +30 -0
- package/dist/lib/sql-helpers.js.map +1 -0
- package/dist/lib/sqlite.d.ts +20 -0
- package/dist/lib/sqlite.d.ts.map +1 -0
- package/dist/lib/sqlite.js +59 -0
- package/dist/lib/sqlite.js.map +1 -0
- package/dist/manifest-loader.d.ts +69 -0
- package/dist/manifest-loader.d.ts.map +1 -0
- package/dist/manifest-loader.js +212 -0
- package/dist/manifest-loader.js.map +1 -0
- package/dist/manifest.d.ts +76 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +128 -0
- package/dist/manifest.js.map +1 -0
- package/dist/module-auth.d.ts +58 -0
- package/dist/module-auth.d.ts.map +1 -0
- package/dist/module-auth.js +118 -0
- package/dist/module-auth.js.map +1 -0
- package/dist/permission-gate.d.ts +53 -0
- package/dist/permission-gate.d.ts.map +1 -0
- package/dist/permission-gate.js +94 -0
- package/dist/permission-gate.js.map +1 -0
- package/dist/routes/_shared.d.ts +68 -0
- package/dist/routes/_shared.d.ts.map +1 -0
- package/dist/routes/_shared.js +27 -0
- package/dist/routes/_shared.js.map +1 -0
- package/dist/routes/config.d.ts +27 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +236 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/db-routes.d.ts +38 -0
- package/dist/routes/db-routes.d.ts.map +1 -0
- package/dist/routes/db-routes.js +186 -0
- package/dist/routes/db-routes.js.map +1 -0
- package/dist/routes/health.d.ts +10 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +23 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/messages.d.ts +23 -0
- package/dist/routes/messages.d.ts.map +1 -0
- package/dist/routes/messages.js +72 -0
- package/dist/routes/messages.js.map +1 -0
- package/dist/routes/module-config-routes.d.ts +31 -0
- package/dist/routes/module-config-routes.d.ts.map +1 -0
- package/dist/routes/module-config-routes.js +128 -0
- package/dist/routes/module-config-routes.js.map +1 -0
- package/dist/routes/modules.d.ts +34 -0
- package/dist/routes/modules.d.ts.map +1 -0
- package/dist/routes/modules.js +92 -0
- package/dist/routes/modules.js.map +1 -0
- package/dist/routes/service-routes.d.ts +26 -0
- package/dist/routes/service-routes.d.ts.map +1 -0
- package/dist/routes/service-routes.js +68 -0
- package/dist/routes/service-routes.js.map +1 -0
- package/dist/runtime.test.d.ts +2 -0
- package/dist/runtime.test.d.ts.map +1 -0
- package/dist/runtime.test.js +66 -0
- package/dist/runtime.test.js.map +1 -0
- package/dist/schema-registry.d.ts +63 -0
- package/dist/schema-registry.d.ts.map +1 -0
- package/dist/schema-registry.js +178 -0
- package/dist/schema-registry.js.map +1 -0
- package/dist/server.d.ts +27 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +82 -0
- package/dist/server.js.map +1 -0
- package/dist/service-registry.d.ts +46 -0
- package/dist/service-registry.d.ts.map +1 -0
- package/dist/service-registry.js +73 -0
- package/dist/service-registry.js.map +1 -0
- package/dist/services/builtin-handlers.d.ts +22 -0
- package/dist/services/builtin-handlers.d.ts.map +1 -0
- package/dist/services/builtin-handlers.js +23 -0
- package/dist/services/builtin-handlers.js.map +1 -0
- package/dist/services/economy-handlers.d.ts +14 -0
- package/dist/services/economy-handlers.d.ts.map +1 -0
- package/dist/services/economy-handlers.js +144 -0
- package/dist/services/economy-handlers.js.map +1 -0
- package/dist/tx-runner.d.ts +144 -0
- package/dist/tx-runner.d.ts.map +1 -0
- package/dist/tx-runner.js +273 -0
- package/dist/tx-runner.js.map +1 -0
- package/dist/where.d.ts +48 -0
- package/dist/where.d.ts.map +1 -0
- package/dist/where.js +107 -0
- package/dist/where.js.map +1 -0
- 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":"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"}
|