@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,212 @@
1
+ /**
2
+ * manifest-loader.ts — 读 v2 manifest,失败 = 启动失败
3
+ *
4
+ * v2 schema:
5
+ * {
6
+ * "schemaVersion": 2,
7
+ * "id": "feature-land",
8
+ * "name": "领地",
9
+ * "type": "feature",
10
+ * "configKey": "land",
11
+ * "requires": ["feature-economy"],
12
+ * "permissions": ["db:read:lands", "service:economy.account"],
13
+ * "services": {
14
+ * "provides": [{ "name": "lands.byOwner", "input": {...}, "output": {...} }],
15
+ * "requires": [{ "name": "economy.account" }]
16
+ * },
17
+ * "notes": "..."
18
+ * }
19
+ *
20
+ * 不要 routes / tables / migrations / seeds / handlers / events。
21
+ *
22
+ * 调用方:db-server/src/index.ts 启动时一次 loadManifestV2,
23
+ * throw 即 crash。
24
+ */
25
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
26
+ import { resolve } from "node:path";
27
+ import { defaultPackagesDir } from "./manifest.js";
28
+ import { validateManifestPermissions } from "./permission-gate.js";
29
+ const VALID_TYPES = new Set(["core", "feature"]);
30
+ const IDENT = /^[A-Za-z0-9_-]+$/;
31
+ /** v1 / 缺 schemaVersion:跳过(直到所有模块迁 v2) */
32
+ export class ModuleV1SkippedError extends Error {
33
+ moduleId;
34
+ declaredVersion;
35
+ constructor(moduleId, declaredVersion) {
36
+ super(`moduleId=${moduleId} schemaVersion=${String(declaredVersion)} (需要 2),跳过`);
37
+ this.moduleId = moduleId;
38
+ this.declaredVersion = declaredVersion;
39
+ }
40
+ }
41
+ function assertModuleObject(modId, obj) {
42
+ if (typeof obj.schemaVersion !== "number") {
43
+ throw new Error(`[manifest] ${modId}: schemaVersion 缺失`);
44
+ }
45
+ if (obj.schemaVersion !== 2) {
46
+ throw new Error(`[manifest] ${modId}: schemaVersion=${obj.schemaVersion} (需要 2)`);
47
+ }
48
+ if (typeof obj.id !== "string" || !IDENT.test(obj.id)) {
49
+ throw new Error(`[manifest] ${modId}: id 缺失或非法 "${String(obj.id)}"`);
50
+ }
51
+ if (typeof obj.name !== "string" || obj.name.length === 0) {
52
+ throw new Error(`[manifest] ${modId}: name 缺失`);
53
+ }
54
+ if (typeof obj.type !== "string" || !VALID_TYPES.has(obj.type)) {
55
+ throw new Error(`[manifest] ${modId}: type 必须是 core|feature ("${String(obj.type)}")`);
56
+ }
57
+ if (typeof obj.configKey !== "string" || obj.configKey.length === 0) {
58
+ throw new Error(`[manifest] ${modId}: configKey 缺失`);
59
+ }
60
+ if (Array.isArray(obj.routes)) {
61
+ throw new Error(`[manifest] ${modId}: 不再支持 routes 字段,改用 db.defineTable + platform routes`);
62
+ }
63
+ if (Array.isArray(obj.migrations)) {
64
+ throw new Error(`[manifest] ${modId}: 不再支持 migrations 字段,改用 db.defineTable 自动建表`);
65
+ }
66
+ if (Array.isArray(obj.tables)) {
67
+ throw new Error(`[manifest] ${modId}: 不再支持 tables 字段,改用 db.defineTable`);
68
+ }
69
+ if (Array.isArray(obj.events)) {
70
+ throw new Error(`[manifest] ${modId}: 不再支持 events 字段,改用 platform event-bus`);
71
+ }
72
+ }
73
+ function parseServices(modId, raw) {
74
+ if (raw === undefined)
75
+ return { provides: [], requires: [] };
76
+ if (typeof raw !== "object" || raw === null) {
77
+ throw new Error(`[manifest] ${modId}: services 必须是对象`);
78
+ }
79
+ const obj = raw;
80
+ const provides = parseServiceList(modId, "provides", obj.provides);
81
+ const requires = parseServiceList(modId, "requires", obj.requires);
82
+ for (const r of requires) {
83
+ if (provides.find((p) => p.name === r.name)) {
84
+ throw new Error(`[manifest] ${modId}: service "${r.name}" 既 provides 又 requires`);
85
+ }
86
+ }
87
+ return { provides, requires };
88
+ }
89
+ function parseServiceList(modId, dir, raw) {
90
+ if (raw === undefined)
91
+ return [];
92
+ if (!Array.isArray(raw))
93
+ throw new Error(`[manifest] ${modId}: services.${dir} 必须是数组`);
94
+ const out = [];
95
+ for (const item of raw) {
96
+ if (typeof item !== "object" || item === null) {
97
+ throw new Error(`[manifest] ${modId}: services.${dir} 项必须是对象`);
98
+ }
99
+ const e = item;
100
+ if (typeof e.name !== "string" || e.name.length === 0) {
101
+ throw new Error(`[manifest] ${modId}: services.${dir} 项缺少 name`);
102
+ }
103
+ const seen = new Set();
104
+ if (seen.has(e.name)) {
105
+ throw new Error(`[manifest] ${modId}: services.${dir} name 重复 "${e.name}"`);
106
+ }
107
+ seen.add(e.name);
108
+ out.push({ name: e.name, input: e.input, output: e.output });
109
+ }
110
+ return out;
111
+ }
112
+ function parseMod(modId, raw) {
113
+ if (raw.schemaVersion !== 2) {
114
+ throw new ModuleV1SkippedError(modId, raw.schemaVersion);
115
+ }
116
+ assertModuleObject(modId, raw);
117
+ const requires = Array.isArray(raw.requires) ? raw.requires.filter((x) => typeof x === "string") : [];
118
+ const permissions = Array.isArray(raw.permissions) ? raw.permissions.filter((x) => typeof x === "string") : [];
119
+ validateManifestPermissions(modId, permissions);
120
+ return {
121
+ schemaVersion: 2,
122
+ id: raw.id,
123
+ name: raw.name,
124
+ type: raw.type,
125
+ configKey: raw.configKey,
126
+ requires,
127
+ permissions,
128
+ services: parseServices(modId, raw.services),
129
+ notes: typeof raw.notes === "string" ? raw.notes : undefined,
130
+ };
131
+ }
132
+ /**
133
+ * 启动期调。throw = 启动失败。
134
+ * 注:不读 v1 manifest。只有 schemaVersion=2 才走。
135
+ */
136
+ export function loadManifestV2(packagesDir = defaultPackagesDir()) {
137
+ if (!existsSync(packagesDir)) {
138
+ throw new Error(`[manifest] ${packagesDir} 不存在;无法启动 v2 协议`);
139
+ }
140
+ const ids = readdirSync(packagesDir, { withFileTypes: true })
141
+ .filter((e) => e.isDirectory())
142
+ .map((e) => e.name);
143
+ const modules = {};
144
+ const providesMap = new Map();
145
+ for (const id of ids) {
146
+ const p = resolve(packagesDir, id, "sapi", "manifest.json");
147
+ if (!existsSync(p))
148
+ continue;
149
+ let raw;
150
+ try {
151
+ raw = readFileSync(p, "utf8");
152
+ }
153
+ catch (e) {
154
+ throw new Error(`[manifest] ${id}: 读 ${p} 失败: ${e.message}`);
155
+ }
156
+ let parsed;
157
+ try {
158
+ parsed = JSON.parse(raw);
159
+ }
160
+ catch (e) {
161
+ throw new Error(`[manifest] ${id}: ${p} 非合法 JSON: ${e.message}`);
162
+ }
163
+ try {
164
+ const m = parseMod(id, parsed);
165
+ modules[m.id] = m;
166
+ for (const s of m.services.provides) {
167
+ const prev = providesMap.get(s.name);
168
+ if (prev) {
169
+ throw new Error(`[manifest] service "${s.name}" 已被 ${prev} provides,${m.id} 抢注`);
170
+ }
171
+ providesMap.set(s.name, m.id);
172
+ }
173
+ }
174
+ catch (e) {
175
+ if (e instanceof ModuleV1SkippedError) {
176
+ // PoC 兼容:不是 v2 的模块跳过,等到所有模块迁 v2 后改为 throw
177
+ console.warn(`[manifest] ${id}: ${e.message}; v1 跳过`);
178
+ continue;
179
+ }
180
+ throw e;
181
+ }
182
+ }
183
+ // requires:校验目标 service 在某个 provides 里
184
+ for (const m of Object.values(modules)) {
185
+ for (const r of m.services.requires) {
186
+ const owner = providesMap.get(r.name);
187
+ if (!owner) {
188
+ throw new Error(`[manifest] ${m.id}.services.requires.${r.name} 没有模块 provides`);
189
+ }
190
+ }
191
+ // requires 模块依赖也要么 enabled 要么本身 installed (启动期不强求 enabled;运行时再查)
192
+ for (const dep of m.requires) {
193
+ if (!modules[dep]) {
194
+ throw new Error(`[manifest] ${m.id}.requires.${dep} 没找到对应模块`);
195
+ }
196
+ }
197
+ }
198
+ return { modules, providesMap };
199
+ }
200
+ /**
201
+ * 给定 moduleId 集合,过滤出 enabled (= lock 文件 enabled 的) 子集。
202
+ * map(moduleId → manifest) — 没装则跳过。
203
+ */
204
+ export function filterEnabled(loaded, enabledIds) {
205
+ const out = {};
206
+ for (const [id, m] of Object.entries(loaded.modules)) {
207
+ if (enabledIds.has(id))
208
+ out[id] = m;
209
+ }
210
+ return out;
211
+ }
212
+ //# sourceMappingURL=manifest-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-loader.js","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * manifest.ts — runtime SAPI module manifest reader.
3
+ *
4
+ * At db-server startup, scan `modules/packages/<id>/sapi/manifest.json` for
5
+ * every installed module and aggregate them into a single in-memory manifest.
6
+ * No pre-built `modules/_manifests/module-manifests.json` is required; the
7
+ * per-module files are the single source of truth (the SEA reads them at
8
+ * runtime too).
9
+ *
10
+ * Behavior:
11
+ * - Missing file: skip that module (WARN).
12
+ * - Corrupt JSON: skip + WARN.
13
+ * - schemaVersion > supported: WARN, continue best-effort.
14
+ * - schemaVersion < supported: refuse to load that module.
15
+ *
16
+ * Reconcile: every route declared in a module's manifest is checked against
17
+ * the prefix table; mismatches WARN but do not block startup.
18
+ */
19
+ export interface ModuleManifestRoute {
20
+ method: string;
21
+ path: string;
22
+ handler: string;
23
+ }
24
+ export interface ModuleManifestMigration {
25
+ name: string;
26
+ version: number;
27
+ }
28
+ export interface ModuleManifestEntry {
29
+ name?: string | undefined;
30
+ type?: string | undefined;
31
+ configKey?: string | undefined;
32
+ requires?: string[] | undefined;
33
+ handlers?: string[] | undefined;
34
+ routes: ModuleManifestRoute[];
35
+ migrations: ModuleManifestMigration[];
36
+ notes?: string | undefined;
37
+ }
38
+ export interface ModuleManifest {
39
+ schemaVersion: number;
40
+ generatedAt: string;
41
+ modules: Record<string, ModuleManifestEntry>;
42
+ }
43
+ export type PackagesDirResolver = () => string;
44
+ /** Resolve where db-server should look for `modules/packages/`.
45
+ *
46
+ * 优先级:
47
+ * 1. `process.env.SFMC_PACKAGES_DIR` —— sfmc/SEA 的 spawnService 在启动 db 时注入
48
+ * (SEA 模式下 exe 目录, npm 模式下 process.cwd())
49
+ * 2. `__dirname` 上溯两级到仓库根 —— 仅当 db-server 独立启动时兜底(CLI 调试)
50
+ *
51
+ * SEA 模式必须走 env 路径:SEA CJS bundle 里 `__dirname` 是虚拟路径,
52
+ * 不能用 `import.meta.url` 解析;靠 SFMC_PACKAGES_DIR 才是稳定的。
53
+ */
54
+ export declare function defaultPackagesDir(): string;
55
+ /**
56
+ * Read every `modules/packages/<id>/sapi/manifest.json` and aggregate.
57
+ * Modules without a manifest are silently skipped (the SAPI side will fall
58
+ * back to a default-empty surface); modules with a corrupt or too-new
59
+ * manifest produce warnings.
60
+ */
61
+ export declare function loadManifest(packagesDir?: string): ModuleManifest;
62
+ export interface ManifestSummary {
63
+ moduleCount: number;
64
+ routeCount: number;
65
+ handlerCount: number;
66
+ migrationCount: number;
67
+ moduleIds: string[];
68
+ }
69
+ export declare function summarize(m: ModuleManifest): ManifestSummary;
70
+ /**
71
+ * Reconcile manifest routes against the routes the db-server currently serves.
72
+ * Returns a list of warnings for routes declared in the manifest that no route
73
+ * file covers. Routes already covered by `routes/*.ts` files are not flagged.
74
+ */
75
+ export declare function reconcile(m: ModuleManifest, knownRoutePrefixes: string[]): string[];
76
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAChC,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,UAAU,EAAE,uBAAuB,EAAE,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAC9C;AAKD,MAAM,MAAM,mBAAmB,GAAG,MAAM,MAAM,CAAC;AAE/C;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,WAAW,GAAE,MAA6B,GAAG,cAAc,CA8CvF;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,cAAc,GAAG,eAAe,CAgB5D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAYnF"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * manifest.ts — runtime SAPI module manifest reader.
3
+ *
4
+ * At db-server startup, scan `modules/packages/<id>/sapi/manifest.json` for
5
+ * every installed module and aggregate them into a single in-memory manifest.
6
+ * No pre-built `modules/_manifests/module-manifests.json` is required; the
7
+ * per-module files are the single source of truth (the SEA reads them at
8
+ * runtime too).
9
+ *
10
+ * Behavior:
11
+ * - Missing file: skip that module (WARN).
12
+ * - Corrupt JSON: skip + WARN.
13
+ * - schemaVersion > supported: WARN, continue best-effort.
14
+ * - schemaVersion < supported: refuse to load that module.
15
+ *
16
+ * Reconcile: every route declared in a module's manifest is checked against
17
+ * the prefix table; mismatches WARN but do not block startup.
18
+ */
19
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
20
+ import { fileURLToPath } from "node:url";
21
+ import { dirname, resolve } from "node:path";
22
+ const SUPPORTED_SCHEMA = 1;
23
+ const __dirname = dirname(fileURLToPath(import.meta.url));
24
+ /** Resolve where db-server should look for `modules/packages/`.
25
+ *
26
+ * 优先级:
27
+ * 1. `process.env.SFMC_PACKAGES_DIR` —— sfmc/SEA 的 spawnService 在启动 db 时注入
28
+ * (SEA 模式下 exe 目录, npm 模式下 process.cwd())
29
+ * 2. `__dirname` 上溯两级到仓库根 —— 仅当 db-server 独立启动时兜底(CLI 调试)
30
+ *
31
+ * SEA 模式必须走 env 路径:SEA CJS bundle 里 `__dirname` 是虚拟路径,
32
+ * 不能用 `import.meta.url` 解析;靠 SFMC_PACKAGES_DIR 才是稳定的。
33
+ */
34
+ export function defaultPackagesDir() {
35
+ return process.env.SFMC_PACKAGES_DIR || resolve(__dirname, "..", "..", "modules", "packages");
36
+ }
37
+ /**
38
+ * Read every `modules/packages/<id>/sapi/manifest.json` and aggregate.
39
+ * Modules without a manifest are silently skipped (the SAPI side will fall
40
+ * back to a default-empty surface); modules with a corrupt or too-new
41
+ * manifest produce warnings.
42
+ */
43
+ export function loadManifest(packagesDir = defaultPackagesDir()) {
44
+ const modules = {};
45
+ if (!existsSync(packagesDir)) {
46
+ console.warn(`[manifest] ${packagesDir} does not exist; db-server will run with zero modules`);
47
+ return { schemaVersion: SUPPORTED_SCHEMA, generatedAt: new Date().toISOString(), modules };
48
+ }
49
+ const ids = readdirSync(packagesDir, { withFileTypes: true })
50
+ .filter((e) => e.isDirectory())
51
+ .map((e) => e.name)
52
+ .sort();
53
+ for (const id of ids) {
54
+ const manifestPath = resolve(packagesDir, id, "sapi", "manifest.json");
55
+ if (!existsSync(manifestPath))
56
+ continue;
57
+ let raw;
58
+ try {
59
+ raw = readFileSync(manifestPath, "utf8");
60
+ }
61
+ catch (err) {
62
+ console.warn(`[manifest] ${id}: cannot read ${manifestPath}: ${err.message}`);
63
+ continue;
64
+ }
65
+ let parsed;
66
+ try {
67
+ parsed = JSON.parse(raw);
68
+ }
69
+ catch (err) {
70
+ console.warn(`[manifest] ${id}: ${manifestPath} is not valid JSON: ${err.message}`);
71
+ continue;
72
+ }
73
+ if (typeof parsed.schemaVersion === "number" && parsed.schemaVersion > SUPPORTED_SCHEMA) {
74
+ console.warn(`[manifest] ${id}: schemaVersion=${parsed.schemaVersion} > supported ${SUPPORTED_SCHEMA}; parsing best-effort`);
75
+ }
76
+ modules[id] = {
77
+ name: parsed.name,
78
+ type: parsed.type,
79
+ configKey: parsed.configKey,
80
+ requires: Array.isArray(parsed.requires) ? parsed.requires : [],
81
+ handlers: Array.isArray(parsed.handlers) ? parsed.handlers : [],
82
+ routes: Array.isArray(parsed.routes) ? parsed.routes : [],
83
+ migrations: Array.isArray(parsed.migrations) ? parsed.migrations : [],
84
+ notes: parsed.notes,
85
+ };
86
+ }
87
+ return {
88
+ schemaVersion: SUPPORTED_SCHEMA,
89
+ generatedAt: new Date().toISOString(),
90
+ modules,
91
+ };
92
+ }
93
+ export function summarize(m) {
94
+ let routeCount = 0;
95
+ let handlerCount = 0;
96
+ let migrationCount = 0;
97
+ for (const entry of Object.values(m.modules)) {
98
+ routeCount += entry.routes.length;
99
+ handlerCount += entry.handlers?.length ?? 0;
100
+ migrationCount += entry.migrations.length;
101
+ }
102
+ return {
103
+ moduleCount: Object.keys(m.modules).length,
104
+ routeCount,
105
+ handlerCount,
106
+ migrationCount,
107
+ moduleIds: Object.keys(m.modules).sort(),
108
+ };
109
+ }
110
+ /**
111
+ * Reconcile manifest routes against the routes the db-server currently serves.
112
+ * Returns a list of warnings for routes declared in the manifest that no route
113
+ * file covers. Routes already covered by `routes/*.ts` files are not flagged.
114
+ */
115
+ export function reconcile(m, knownRoutePrefixes) {
116
+ const warnings = [];
117
+ for (const [moduleId, entry] of Object.entries(m.modules)) {
118
+ for (const route of entry.routes) {
119
+ const prefix = route.path.split("/").slice(0, 4).join("/");
120
+ const matched = knownRoutePrefixes.some((p) => prefix.startsWith(p.split("/").slice(0, 4).join("/")));
121
+ if (!matched) {
122
+ warnings.push(`${moduleId}: route ${route.method} ${route.path} is not covered by db-server`);
123
+ }
124
+ }
125
+ }
126
+ return warnings;
127
+ }
128
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * module-auth.ts — 模块身份的 HMAC token 派生与校验
3
+ *
4
+ * 两套并存:
5
+ * - `env.AUTH_TOKEN` (config db_config.json http_auth):sfmc 管控 / 平台内部用
6
+ * - `moduleTokens[moduleId]`:db-server 给每个 enabled 模块在启动时派生,
7
+ * 写到 data/module-tokens.json,SAPI host-bootstrap 启动时读,按
8
+ * `setDbModuleContext(moduleId, token)` 注入。
9
+ *
10
+ * 派生公式:
11
+ * module_token = HMAC-SHA256(auth_secret, "sfmc-module:" + moduleId).hex
12
+ *
13
+ * auth_secret 优先级:
14
+ * 1. env.AUTH_TOKEN
15
+ * 2. 启动时随机生成(无 AUTH_TOKEN 时),写到 data/module-tokens.json,
16
+ * 只警告一次(每次重启模块 token 都变,SAPI host 必须重读文件)
17
+ *
18
+ * 校验:
19
+ * - HTTP `Authorization: Bearer <module_token>` 头
20
+ * - Query `?moduleId=<id>`
21
+ * - 两都匹配 = 调用方身份
22
+ * - 校验调用方 moduleId 在 enabled 列表(否则 403)
23
+ */
24
+ export interface ModuleAuthMap {
25
+ /** moduleId → hex token */
26
+ tokens: Record<string, string>;
27
+ /** 用于派生 token 的 secret (env.AUTH_TOKEN 或本次启动随机生成) */
28
+ secret: string;
29
+ }
30
+ export declare function deriveToken(moduleId: string, secret: string): string;
31
+ /**
32
+ * 启动时调用一次:
33
+ * - 把 env.AUTH_TOKEN 当 secret
34
+ * - 给每个 enabled moduleId 派生 token
35
+ * - 写 data/module-tokens.json (SAPI host-bootstrap 读)
36
+ * - 控制台打印摘要(env var SFMC_LOG_MODULE_TOKEN=1 时打完整)
37
+ */
38
+ export declare function buildModuleAuth(opts: {
39
+ projectRoot: string;
40
+ envAuthToken: string;
41
+ enabledModuleIds: string[];
42
+ }): ModuleAuthMap;
43
+ export declare function loadModuleAuth(projectRoot: string): ModuleAuthMap | null;
44
+ /**
45
+ * 校验请求方身份:
46
+ * - Bearer 头 / X-Module-Token 二选一
47
+ * - ?moduleId=<id> 必须存在
48
+ * - 模块必须在 enabledModuleIds 集合中
49
+ * - token 必须 = deriveToken(moduleId, secret)
50
+ * 返回模块 ID 或 null。
51
+ */
52
+ export declare function verifyModuleAuth(opts: {
53
+ headers: NodeJS.Dict<string | string[]>;
54
+ params: URLSearchParams;
55
+ auth: ModuleAuthMap;
56
+ enabledModuleIds: Set<string>;
57
+ }): string | null;
58
+ //# sourceMappingURL=module-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-auth.d.ts","sourceRoot":"","sources":["../src/module-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B,GAAG,aAAa,CAsChB;AAED,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAIxE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IACxC,MAAM,EAAE,eAAe,CAAC;IACxB,IAAI,EAAE,aAAa,CAAC;IACpB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC/B,GAAG,MAAM,GAAG,IAAI,CAuBhB"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * module-auth.ts — 模块身份的 HMAC token 派生与校验
3
+ *
4
+ * 两套并存:
5
+ * - `env.AUTH_TOKEN` (config db_config.json http_auth):sfmc 管控 / 平台内部用
6
+ * - `moduleTokens[moduleId]`:db-server 给每个 enabled 模块在启动时派生,
7
+ * 写到 data/module-tokens.json,SAPI host-bootstrap 启动时读,按
8
+ * `setDbModuleContext(moduleId, token)` 注入。
9
+ *
10
+ * 派生公式:
11
+ * module_token = HMAC-SHA256(auth_secret, "sfmc-module:" + moduleId).hex
12
+ *
13
+ * auth_secret 优先级:
14
+ * 1. env.AUTH_TOKEN
15
+ * 2. 启动时随机生成(无 AUTH_TOKEN 时),写到 data/module-tokens.json,
16
+ * 只警告一次(每次重启模块 token 都变,SAPI host 必须重读文件)
17
+ *
18
+ * 校验:
19
+ * - HTTP `Authorization: Bearer <module_token>` 头
20
+ * - Query `?moduleId=<id>`
21
+ * - 两都匹配 = 调用方身份
22
+ * - 校验调用方 moduleId 在 enabled 列表(否则 403)
23
+ */
24
+ import { readJson, writeJson } from "@sfmc-bds/sdk/node/config";
25
+ import { createHmac, randomBytes } from "node:crypto";
26
+ import { join } from "node:path";
27
+ import { log } from "./lib/log.js";
28
+ const IDENT = /^[A-Za-z0-9_\-]+$/;
29
+ function safeId(id) {
30
+ if (!IDENT.test(id))
31
+ throw new Error(`[auth] invalid module id: ${id}`);
32
+ return id;
33
+ }
34
+ export function deriveToken(moduleId, secret) {
35
+ return createHmac("sha256", secret).update(`sfmc-module:${safeId(moduleId)}`).digest("hex");
36
+ }
37
+ /**
38
+ * 启动时调用一次:
39
+ * - 把 env.AUTH_TOKEN 当 secret
40
+ * - 给每个 enabled moduleId 派生 token
41
+ * - 写 data/module-tokens.json (SAPI host-bootstrap 读)
42
+ * - 控制台打印摘要(env var SFMC_LOG_MODULE_TOKEN=1 时打完整)
43
+ */
44
+ export function buildModuleAuth(opts) {
45
+ const secret = opts.envAuthToken || randomBytes(32).toString("hex");
46
+ const tokens = {};
47
+ for (const id of opts.enabledModuleIds) {
48
+ tokens[id] = deriveToken(id, secret);
49
+ }
50
+ const outDir = join(opts.projectRoot, "data");
51
+ const outFile = join(outDir, "module-tokens.json");
52
+ writeJson(outFile, {
53
+ tokens,
54
+ secret,
55
+ generatedAt: new Date().toISOString(),
56
+ secretGenerated: !opts.envAuthToken,
57
+ });
58
+ log.success(`[auth] 已为 ${opts.enabledModuleIds.length} 个模块派生 HMAC token,写入 ${outFile}`);
59
+ if (!opts.envAuthToken) {
60
+ log.warn("[auth] 未配置 env.AUTH_TOKEN(冲突 config http_auth),本次启动使用了随机 secret;"
61
+ + " 重启后 token 会变,host-bootstrap 必须每次重新读 module-tokens.json");
62
+ }
63
+ if (process.env.SFMC_LOG_MODULE_TOKEN === "1") {
64
+ log.info("[auth] module tokens (SFMC_LOG_MODULE_TOKEN=1):");
65
+ for (const [id, t] of Object.entries(tokens)) {
66
+ console.log(` ${id} = ${t}`);
67
+ }
68
+ }
69
+ else {
70
+ const lines = Object.entries(tokens)
71
+ .map(([id, t]) => ` ${id} = ${t.slice(0, 6)}…${t.slice(-4)}`)
72
+ .join("\n");
73
+ log.info(`[auth] token 摘要:\n${lines}`);
74
+ }
75
+ return { tokens, secret };
76
+ }
77
+ export function loadModuleAuth(projectRoot) {
78
+ const parsed = readJson(join(projectRoot, "data", "module-tokens.json"));
79
+ if (!parsed || !parsed.tokens || !parsed.secret)
80
+ return null;
81
+ return { tokens: parsed.tokens, secret: parsed.secret };
82
+ }
83
+ /**
84
+ * 校验请求方身份:
85
+ * - Bearer 头 / X-Module-Token 二选一
86
+ * - ?moduleId=<id> 必须存在
87
+ * - 模块必须在 enabledModuleIds 集合中
88
+ * - token 必须 = deriveToken(moduleId, secret)
89
+ * 返回模块 ID 或 null。
90
+ */
91
+ export function verifyModuleAuth(opts) {
92
+ const moduleId = opts.params.get("moduleId") || "";
93
+ if (!moduleId || !opts.enabledModuleIds.has(moduleId))
94
+ return null;
95
+ let provided = "";
96
+ const auth = opts.headers["authorization"];
97
+ const token = opts.headers["x-module-token"];
98
+ if (typeof auth === "string" && auth.startsWith("Bearer ")) {
99
+ provided = auth.slice(7);
100
+ }
101
+ else if (typeof token === "string") {
102
+ provided = token;
103
+ }
104
+ if (!provided)
105
+ return null;
106
+ const expected = opts.auth.tokens[moduleId];
107
+ if (!expected)
108
+ return null;
109
+ // constant-time 比较
110
+ if (provided.length !== expected.length)
111
+ return null;
112
+ let diff = 0;
113
+ for (let i = 0; i < provided.length; i++) {
114
+ diff |= provided.charCodeAt(i) ^ expected.charCodeAt(i);
115
+ }
116
+ return diff === 0 ? moduleId : null;
117
+ }
118
+ //# sourceMappingURL=module-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-auth.js","sourceRoot":"","sources":["../src/module-auth.ts"],"names":[],"mappings":"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"}