@sfmc-bds/db-server 0.2.0-beta.4 → 0.2.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +663 -0
- package/dist/domain/bridge.d.ts +57 -23
- package/dist/domain/bridge.d.ts.map +1 -1
- package/dist/domain/bridge.js +77 -6
- package/dist/domain/bridge.js.map +2 -2
- package/dist/domain/bridge.test.d.ts +2 -0
- package/dist/domain/bridge.test.d.ts.map +1 -0
- package/dist/domain/bridge.test.js +87 -0
- package/dist/domain/bridge.test.js.map +7 -0
- package/dist/domain/economy.d.ts +38 -7
- package/dist/domain/economy.d.ts.map +1 -1
- package/dist/domain/economy.js.map +2 -2
- package/dist/domain/messages-bridge-filter.test.d.ts +5 -0
- package/dist/domain/messages-bridge-filter.test.d.ts.map +1 -0
- package/dist/domain/messages-bridge-filter.test.js +20 -0
- package/dist/domain/messages-bridge-filter.test.js.map +7 -0
- package/dist/domain/qq-bind-status.test.d.ts +2 -0
- package/dist/domain/qq-bind-status.test.d.ts.map +1 -0
- package/dist/domain/qq-bind-status.test.js +211 -0
- package/dist/domain/qq-bind-status.test.js.map +7 -0
- package/dist/domain/qq-events.d.ts +84 -0
- package/dist/domain/qq-events.d.ts.map +1 -0
- package/dist/domain/qq-events.js +193 -0
- package/dist/domain/qq-events.js.map +7 -0
- package/dist/domain/qq-events.test.d.ts +5 -0
- package/dist/domain/qq-events.test.d.ts.map +1 -0
- package/dist/domain/qq-events.test.js +170 -0
- package/dist/domain/qq-events.test.js.map +7 -0
- package/dist/domain/qq-join.test.d.ts +2 -0
- package/dist/domain/qq-join.test.d.ts.map +1 -0
- package/dist/domain/qq-join.test.js +353 -0
- package/dist/domain/qq-join.test.js.map +7 -0
- package/dist/domain/schema.d.ts +17 -19
- package/dist/domain/schema.d.ts.map +1 -1
- package/dist/domain/schema.js +69 -0
- package/dist/domain/schema.js.map +2 -2
- package/dist/domain/system-status.d.ts +75 -0
- package/dist/domain/system-status.d.ts.map +1 -0
- package/dist/domain/system-status.js +181 -0
- package/dist/domain/system-status.js.map +7 -0
- package/dist/domain/system-status.test.d.ts +5 -0
- package/dist/domain/system-status.test.d.ts.map +1 -0
- package/dist/domain/system-status.test.js +19 -0
- package/dist/domain/system-status.test.js.map +7 -0
- package/dist/domain/transaction.d.ts +5 -4
- package/dist/domain/transaction.d.ts.map +1 -1
- package/dist/domain/transaction.js.map +2 -2
- package/dist/env.d.ts +7 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +55 -3
- package/dist/env.js.map +2 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -19
- package/dist/index.js.map +2 -2
- package/dist/lib/idempotency-store.d.ts +17 -11
- package/dist/lib/idempotency-store.d.ts.map +1 -1
- package/dist/lib/idempotency-store.js.map +2 -2
- package/dist/lib/idempotency.d.ts +3 -4
- package/dist/lib/idempotency.d.ts.map +1 -1
- package/dist/lib/idempotency.js.map +2 -2
- package/dist/lib/module-state.d.ts +38 -8
- package/dist/lib/module-state.d.ts.map +1 -1
- package/dist/lib/module-state.js.map +2 -2
- package/dist/lib/order-by.d.ts +8 -6
- package/dist/lib/order-by.d.ts.map +1 -1
- package/dist/lib/order-by.js.map +2 -2
- package/dist/lib/sql-helpers.d.ts +25 -13
- package/dist/lib/sql-helpers.d.ts.map +1 -1
- package/dist/lib/sql-helpers.js.map +2 -2
- package/dist/lib/sqlite.d.ts +14 -5
- package/dist/lib/sqlite.d.ts.map +1 -1
- package/dist/lib/sqlite.js.map +2 -2
- package/dist/manifest-loader.d.ts +5 -2
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +16 -6
- package/dist/manifest-loader.js.map +2 -2
- package/dist/manifest.d.ts +1 -2
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +13 -8
- package/dist/manifest.js.map +2 -2
- package/dist/project-root.d.ts +1 -1
- package/dist/project-root.d.ts.map +1 -1
- package/dist/project-root.js +2 -2
- package/dist/project-root.js.map +2 -2
- package/dist/routes/_shared.d.ts +14 -5
- package/dist/routes/_shared.d.ts.map +1 -1
- package/dist/routes/_shared.js.map +2 -2
- package/dist/routes/config.d.ts +8 -11
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +17 -23
- package/dist/routes/config.js.map +2 -2
- package/dist/routes/db-routes.d.ts +6 -0
- package/dist/routes/db-routes.d.ts.map +1 -1
- package/dist/routes/db-routes.js.map +2 -2
- package/dist/routes/messages.d.ts +6 -2
- package/dist/routes/messages.d.ts.map +1 -1
- package/dist/routes/messages.js +17 -3
- package/dist/routes/messages.js.map +2 -2
- package/dist/routes/module-config-routes.d.ts +13 -9
- package/dist/routes/module-config-routes.d.ts.map +1 -1
- package/dist/routes/module-config-routes.js.map +2 -2
- package/dist/routes/modules.d.ts +6 -0
- package/dist/routes/modules.d.ts.map +1 -1
- package/dist/routes/modules.js.map +2 -2
- package/dist/routes/qq-bind.d.ts +24 -0
- package/dist/routes/qq-bind.d.ts.map +1 -0
- package/dist/routes/qq-bind.js +179 -0
- package/dist/routes/qq-bind.js.map +7 -0
- package/dist/routes/qq-events.d.ts +20 -0
- package/dist/routes/qq-events.d.ts.map +1 -0
- package/dist/routes/qq-events.js +39 -0
- package/dist/routes/qq-events.js.map +7 -0
- package/dist/routes/qq-join.d.ts +46 -0
- package/dist/routes/qq-join.d.ts.map +1 -0
- package/dist/routes/qq-join.js +277 -0
- package/dist/routes/qq-join.js.map +7 -0
- package/dist/routes/service-routes.d.ts +6 -0
- package/dist/routes/service-routes.d.ts.map +1 -1
- package/dist/routes/service-routes.js.map +2 -2
- package/dist/routes/status.d.ts +19 -0
- package/dist/routes/status.d.ts.map +1 -0
- package/dist/routes/status.js +74 -0
- package/dist/routes/status.js.map +7 -0
- package/dist/schema-registry.d.ts +16 -15
- package/dist/schema-registry.d.ts.map +1 -1
- package/dist/schema-registry.js +7 -1
- package/dist/schema-registry.js.map +2 -2
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js.map +2 -2
- package/dist/service-registry.d.ts +39 -9
- package/dist/service-registry.d.ts.map +1 -1
- package/dist/service-registry.js +30 -2
- package/dist/service-registry.js.map +2 -2
- package/dist/services/builtin-handlers.d.ts +27 -7
- package/dist/services/builtin-handlers.d.ts.map +1 -1
- package/dist/services/builtin-handlers.js.map +2 -2
- package/dist/tx-runner.d.ts +29 -3
- package/dist/tx-runner.d.ts.map +1 -1
- package/dist/tx-runner.js +29 -3
- package/dist/tx-runner.js.map +2 -2
- package/package.json +17 -15
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/schema-registry.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * schema-registry.ts \u2014 \u6536\u96C6\u6A21\u5757 defineTable,\u7EDF\u4E00\u5EFA\u8868\n *\n * \u534F\u8BAE:\n * - \u6A21\u5757 SAPI \u542F\u52A8\u65F6\u8C03 db.defineTable(name, columns, {softDelete})\n * \u2192 POST /api/sfmc/db/define-table {moduleId, name, columns, softDelete}\n * - schema-registry \u6536\u96C6\u6240\u6709\u8C03\u7528,CAS \u9501\n * - \u7B49\u6240\u6709\"\u5DF2\u6CE8\u518C\u6A21\u5757\"\u90FD\u8C03\u8FC7(\u8D85\u65F6 fallback \u6216 explicit `finalize()`),\n * \u4E00\u6B21\u6027 CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS\n * - \u540C\u4E00 module \u91CD\u590D define \u540C\u540D\u8868 = \u5E42\u7B49(\u540C\u540D columns \u5141\u8BB8)\n * - \u4E0D\u540C module \u62A2\u540C\u540D\u8868 = \u542F\u52A8\u5931\u8D25\n * - \u5E73\u53F0\u8868(moduleId = \"__platform__\")\u7531 db-tables.ts \u5728 initSchema \u76F4\u63A5 bootstrap\n *\n * \u5217\u7C7B\u578B\u767D\u540D\u5355:\n * text / integer / real / blob\n * \u4FEE\u9970: primary / notNull / unique / default / index\n * softDelete = \u81EA\u52A8\u52A0 _deleted_at INTEGER(\u5217\u540D\u5E26\u4E0B\u5212\u7EBF) \u548C _version INTEGER\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport { log } from \"./lib/log.js\";\n\nexport type ColumnType = \"text\" | \"integer\" | \"real\" | \"blob\";\n\nexport interface ColumnDef {\n type: ColumnType;\n primary?: boolean;\n notNull?: boolean;\n unique?: boolean;\n default?: Primitive;\n index?: boolean;\n ref?: string;\n}\n\nexport type Primitive = string | number | boolean | null;\n\nexport interface DefineTableRequest {\n name: string;\n columns: Record<string, ColumnDef>;\n softDelete?: boolean;\n}\n\nexport interface DefineTableResult {\n table: string;\n created: boolean;\n indices: string[];\n}\n\ninterface DefinedTable {\n moduleId: string;\n name: string;\n columns: Record<string, ColumnDef>;\n softDelete: boolean;\n}\n\nexport const PLATFORM_MODULE_ID = \"__platform__\";\n\nconst IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst VALID_TYPES: Set<ColumnType> = new Set([\"text\", \"integer\", \"real\", \"blob\"]);\n\nfunction assertValid(req: DefineTableRequest, moduleId: string): void {\n if (!IDENT.test(req.name)) throw new Error(`[schema] ${moduleId}: invalid table name \"${req.name}\"`);\n if (!req.columns || Object.keys(req.columns).length === 0) {\n throw new Error(`[schema] ${moduleId}: ${req.name} has no columns`);\n }\n let pks = 0;\n for (const [colName, def] of Object.entries(req.columns)) {\n if (!IDENT.test(colName)) throw new Error(`[schema] ${moduleId}: invalid column name \"${colName}\" in ${req.name}`);\n if (!VALID_TYPES.has(def.type)) {\n throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} bad type \"${def.type}\"`);\n }\n if (def.primary) pks++;\n if (def.ref) {\n const [t, c] = def.ref.split(\".\");\n if (!t || !c || !IDENT.test(t) || !IDENT.test(c)) {\n throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} invalid ref \"${def.ref}\"`);\n }\n }\n }\n if (pks > 1) throw new Error(`[schema] ${moduleId}: ${req.name} has ${pks} primary keys (max 1)`);\n}\n\n/**\n * \u4E00\u6B21\u6027\u628A columns \u7FFB\u8BD1\u6210 CREATE TABLE\u3002\n * \u4F8B\u5982 {id:{type:\"text\",primary:true},name:{type:\"text\",notNull:true}}\n * \u2192 \"id\" TEXT PRIMARY KEY, \"name\" TEXT NOT NULL\n */\nfunction buildColumnClause(name: string, def: ColumnDef): string {\n const parts: string[] = [`\"${name}\"`, def.type.toUpperCase()];\n if (def.primary) parts.push(\"PRIMARY KEY\");\n if (def.notNull && !def.primary) parts.push(\"NOT NULL\");\n if (def.unique) parts.push(\"UNIQUE\");\n if (def.default !== undefined && def.default !== null) {\n if (typeof def.default === \"string\") parts.push(`DEFAULT '${def.default.replace(/'/g, \"''\")}'`);\n else parts.push(`DEFAULT ${def.default}`);\n }\n if (def.ref) parts.push(`REFERENCES ${def.ref}`);\n return parts.join(\" \");\n}\n\n/**\n * \u628A\u4E00\u5F20\u8868\u7684\u6240\u6709\u5217(\u542B softDelete \u9690\u5F0F\u5217)\u62FC\u6210 CREATE TABLE \u7684\u5217\u5B9A\u4E49\u6570\u7EC4\u3002\n * \u9690\u5F0F _deleted_at / _version \u4F5C\u4E3A\u72EC\u7ACB\u5217\u5728\u672B\u5C3E\u8FFD\u52A0\u300C\u4E00\u6B21\u300D(\u6B64\u524D\u8BEF\u5728\u6BCF\u4E2A\u5217\n * \u5B50\u53E5\u91CC\u5404\u8FFD\u52A0\u4E00\u6B21,\u4E14\u65E0\u9017\u53F7\u5206\u9694,\u5BFC\u81F4 softDelete \u8868\u5EFA\u8868 SQL \u8BED\u6CD5\u9519\u8BEF)\u3002\n */\nfunction buildColumnList(columns: Record<string, ColumnDef>, softDelete: boolean): string[] {\n const cols: string[] = [];\n for (const [n, def] of Object.entries(columns)) {\n cols.push(buildColumnClause(n, def));\n }\n if (softDelete) {\n cols.push('\"_deleted_at\" INTEGER', '\"_version\" INTEGER DEFAULT 0');\n }\n return cols;\n}\n\nexport class SchemaRegistry {\n private readonly db: DatabaseSync;\n private readonly tables = new Map<string, DefinedTable>();\n private finalized = false;\n\n constructor(db: DatabaseSync) {\n this.db = db;\n }\n\n /** \u6A21\u5757 SAPI \u542F\u52A8\u65F6\u8C03\u3002 */\n define(moduleId: string, req: DefineTableRequest): DefineTableResult {\n if (this.finalized) {\n throw new Error(`[schema] registry \u5DF2 finalize,\u6A21\u5757 ${moduleId} \u4E0D\u80FD\u518D defineTable`);\n }\n assertValid(req, moduleId);\n\n const existing = this.tables.get(req.name);\n if (existing) {\n if (existing.moduleId === moduleId) {\n // \u540C\u6A21\u5757\u91CD\u590D\u5B9A\u4E49:\u6821\u9A8C columns \u4E00\u81F4\n if (JSON.stringify(existing.columns) !== JSON.stringify(req.columns)) {\n throw new Error(`[schema] ${moduleId} \u91CD\u590D defineTable \"${req.name}\",columns \u4E0D\u4E00\u81F4`);\n }\n if (existing.softDelete !== (req.softDelete ?? true)) {\n throw new Error(`[schema] ${moduleId} \u91CD\u590D defineTable \"${req.name}\",softDelete \u4E0D\u4E00\u81F4`);\n }\n log.info(`[schema] ${moduleId} \u91CD\u590D define \"${req.name}\" \u8DF3\u8FC7`);\n return { table: req.name, created: false, indices: [] };\n }\n throw new Error(\n `[schema] \u8868 \"${req.name}\" \u5DF2\u88AB\u6A21\u5757 ${existing.moduleId} \u5B9A\u4E49,${moduleId} \u4E0D\u80FD\u62A2\u540C\u540D\u8868`\n );\n }\n\n const softDelete = req.softDelete ?? true;\n const defined: DefinedTable = {\n moduleId,\n name: req.name,\n columns: req.columns,\n softDelete,\n };\n this.tables.set(req.name, defined);\n // \u7ACB\u5373\u7269\u7406\u5EFA\u8868(\u5E42\u7B49 CREATE TABLE IF NOT EXISTS)\u3002\n // \u539F\u8BBE\u8BA1\u662F\u300Cdefine \u6536\u96C6 \u2192 finalize \u7EDF\u4E00\u5EFA\u8868\u300D,\u4F46 finalize() \u4ECE\u672A\u88AB\u8C03\u7528,\n // \u5BFC\u81F4\u6A21\u5757\u8868\u6C38\u8FDC\u4E0D\u4F1A\u843D\u5730\u3001\u540E\u7EED insert/query \u5168\u90E8 no such table\u3002\n // \u91C7\u7528 define \u5373\u5EFA\u8868:\u78B0\u649E\u68C0\u6D4B\u5DF2\u5728\u4E0A\u65B9\u6309\u5185\u5B58\u8868\u5B8C\u6210,\u4E0D\u5F71\u54CD\u5355\u4E00 owner \u4FDD\u8BC1\u3002\n this.createPhysical(defined);\n return { table: req.name, created: true, indices: [] };\n }\n\n /** \u5E42\u7B49\u5730\u7269\u7406\u5EFA\u8868 + \u5EFA\u7D22\u5F15(\u4F9B define / finalize \u5171\u7528)\u3002 */\n private createPhysical(t: DefinedTable): void {\n const cols = buildColumnList(t.columns, t.softDelete);\n this.db.exec(`CREATE TABLE IF NOT EXISTS \"${t.name}\" (${cols.join(\", \")})`);\n for (const [n, def] of Object.entries(t.columns)) {\n if (def.index) {\n const idxName = `idx_${t.name}_${n}`.slice(0, 60);\n this.db.exec(`CREATE INDEX IF NOT EXISTS \"${idxName}\" ON \"${t.name}\"(\"${n}\")`);\n }\n }\n }\n\n /**\n * \u771F\u6B63\u5EFA\u8868\u7684\u65F6\u523B\u3002\u6240\u6709 enabled \u6A21\u5757 init \u5B8C\u6210(\u6216\u660E\u786E\u8C03)\u540E\u518D finalize\u3002\n * CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS,\u5E42\u7B49\u3002\n */\n finalize(): { tableNames: string[]; created: string[] } {\n if (this.finalized) return { tableNames: [...this.tables.keys()], created: [] };\n this.finalized = true;\n const created: string[] = [];\n\n for (const t of this.tables.values()) {\n const cols = buildColumnList(t.columns, t.softDelete);\n const ddl = `CREATE TABLE IF NOT EXISTS \"${t.name}\" (${cols.join(\", \")})`;\n this.db.exec(ddl);\n\n for (const [n, def] of Object.entries(t.columns)) {\n if (def.index) {\n const idxName = `idx_${t.name}_${n}`.slice(0, 60);\n this.db.exec(`CREATE INDEX IF NOT EXISTS \"${idxName}\" ON \"${t.name}\"(\"${n}\")`);\n }\n }\n\n const exists =\n (this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name=?\")\n .get(t.name) as { name?: string } | undefined)?.name === t.name;\n if (exists) created.push(t.name);\n log.success(`[schema] ${t.moduleId} \u2192 ${t.name} (softDelete=${t.softDelete})`);\n }\n\n return { tableNames: [...this.tables.keys()], created };\n }\n\n listTables(): string[] {\n return [...this.tables.keys()];\n }\n\n isRegistered(name: string): boolean {\n return this.tables.has(name);\n }\n\n getOwner(name: string): string | undefined {\n return this.tables.get(name)?.moduleId;\n }\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * schema-registry.ts \u2014 \u52A8\u6001\u6570\u636E\u8868\u7ED3\u6784\u6CE8\u518C\u4E0E\u521B\u5EFA\u7BA1\u7406\u5668\n *\n * \u534F\u8BAE\u4E0E\u6D41\u7A0B\uFF1A\n * 1. \u6A21\u5757\u542F\u52A8\u65F6\u8C03\u7528 `db.defineTable(name, columns, { softDelete })` \u5411\u670D\u52A1\u7AEF\u6CE8\u518C\u8868\u7ED3\u6784\u5B9A\u4E49\u3002\n * 2. `SchemaRegistry` \u6821\u9A8C\u5217\u5B9A\u4E49\u5408\u6CD5\u6027\u3001\u5916\u952E\u5F15\u7528\u4E0E\u4E3B\u952E\u552F\u4E00\u6027\u3002\n * 3. \u6821\u9A8C\u901A\u8FC7\u540E\u81EA\u52A8\u6267\u884C `CREATE TABLE IF NOT EXISTS` \u53CA\u7D22\u5F15\u521B\u5EFA\u8BED\u53E5\u3002\n *\n * \u51B2\u7A81\u4E0E\u9694\u79BB\u63A7\u5236\uFF1A\n * - \u76F8\u540C\u6A21\u5757\u91CD\u590D\u5B9A\u4E49\u540C\u540D\u8868\uFF1A\u5177\u5907\u5E42\u7B49\u6027\uFF08\u8981\u6C42\u5217\u7ED3\u6784\u5B8C\u5168\u4E00\u81F4\uFF09\n * - \u4E0D\u540C\u6A21\u5757\u5B9A\u4E49\u76F8\u540C\u8868\u540D\uFF1A\u629B\u51FA\u51B2\u7A81\u5F02\u5E38\u62D2\u7EDD\u6CE8\u518C\n * - \u8F6F\u5220\u9664\u652F\u6301\uFF1A\u5F00\u542F `softDelete`\uFF08\u9ED8\u8BA4\u5F00\u542F\uFF09\u65F6\u81EA\u52A8\u5728\u672B\u5C3E\u7EDF\u4E00\u8FFD\u52A0 `_deleted_at` \u4E0E\u4E50\u89C2\u9501 `_version`\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport { log } from \"./lib/log.js\";\n\nexport type ColumnType = \"text\" | \"integer\" | \"real\" | \"blob\";\n\nexport interface ColumnDef {\n type: ColumnType;\n primary?: boolean;\n notNull?: boolean;\n unique?: boolean;\n default?: Primitive;\n index?: boolean;\n ref?: string;\n}\n\nexport type Primitive = string | number | boolean | null;\n\nexport interface DefineTableRequest {\n name: string;\n columns: Record<string, ColumnDef>;\n softDelete?: boolean;\n}\n\nexport interface DefineTableResult {\n table: string;\n created: boolean;\n indices: string[];\n}\n\ninterface DefinedTable {\n moduleId: string;\n name: string;\n columns: Record<string, ColumnDef>;\n softDelete: boolean;\n}\n\nexport const PLATFORM_MODULE_ID = \"__platform__\";\n\nconst IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst VALID_TYPES: Set<ColumnType> = new Set([\"text\", \"integer\", \"real\", \"blob\"]);\n\nfunction assertValid(req: DefineTableRequest, moduleId: string): void {\n if (!IDENT.test(req.name)) throw new Error(`[schema] ${moduleId}: invalid table name \"${req.name}\"`);\n if (!req.columns || Object.keys(req.columns).length === 0) {\n throw new Error(`[schema] ${moduleId}: ${req.name} has no columns`);\n }\n let pks = 0;\n for (const [colName, def] of Object.entries(req.columns)) {\n if (!IDENT.test(colName)) throw new Error(`[schema] ${moduleId}: invalid column name \"${colName}\" in ${req.name}`);\n if (!VALID_TYPES.has(def.type)) {\n throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} bad type \"${def.type}\"`);\n }\n if (def.primary) pks++;\n if (def.ref) {\n const [t, c] = def.ref.split(\".\");\n if (!t || !c || !IDENT.test(t) || !IDENT.test(c)) {\n throw new Error(`[schema] ${moduleId}: ${req.name}.${colName} invalid ref \"${def.ref}\"`);\n }\n }\n }\n if (pks > 1) throw new Error(`[schema] ${moduleId}: ${req.name} has ${pks} primary keys (max 1)`);\n}\n\n/**\n * \u5C06\u5355\u5217\u5B9A\u4E49\u7F16\u8BD1\u4E3A SQL \u5217\u5B9A\u4E49\u5B50\u53E5\uFF08\u5982 `\"id\" TEXT PRIMARY KEY`\uFF09\u3002\n *\n * @param name \u5217\u540D\u3002\n * @param def \u5217\u5C5E\u6027\u5B9A\u4E49\u3002\n * @returns \u7F16\u8BD1\u540E\u7684 SQL \u7247\u6BB5\u3002\n */\nfunction buildColumnClause(name: string, def: ColumnDef): string {\n const parts: string[] = [`\"${name}\"`, def.type.toUpperCase()];\n if (def.primary) parts.push(\"PRIMARY KEY\");\n if (def.notNull && !def.primary) parts.push(\"NOT NULL\");\n if (def.unique) parts.push(\"UNIQUE\");\n if (def.default !== undefined && def.default !== null) {\n if (typeof def.default === \"string\") parts.push(`DEFAULT '${def.default.replace(/'/g, \"''\")}'`);\n else parts.push(`DEFAULT ${def.default}`);\n }\n if (def.ref) parts.push(`REFERENCES ${def.ref}`);\n return parts.join(\" \");\n}\n\n/**\n * \u5C06\u6574\u5F20\u8868\u7684\u6240\u6709\u5217\u5B9A\u4E49\uFF08\u5305\u542B softDelete \u9690\u5F0F\u5217\uFF09\u7F16\u8BD1\u4E3A\u5217\u5B9A\u4E49\u5B57\u7B26\u4E32\u6570\u7EC4\u3002\n * \u8F6F\u5220\u9664\u5217 `_deleted_at` \u4E0E\u4E50\u89C2\u9501\u7248\u672C\u53F7 `_version` \u4F5C\u4E3A\u72EC\u7ACB\u5217\u5728\u672B\u5C3E\u7EDF\u4E00\u8FFD\u52A0\u3002\n *\n * @param columns \u5217\u7ED3\u6784\u5B9A\u4E49\u5B57\u5178\u3002\n * @param softDelete \u662F\u5426\u542F\u7528\u8F6F\u5220\u9664\u6269\u5C55\u5217\u3002\n * @returns \u7F16\u8BD1\u540E\u7684\u5217\u5B9A\u4E49\u5B50\u53E5\u6570\u7EC4\u3002\n */\nfunction buildColumnList(columns: Record<string, ColumnDef>, softDelete: boolean): string[] {\n const cols: string[] = [];\n for (const [n, def] of Object.entries(columns)) {\n cols.push(buildColumnClause(n, def));\n }\n if (softDelete) {\n cols.push('\"_deleted_at\" INTEGER', '\"_version\" INTEGER DEFAULT 0');\n }\n return cols;\n}\n\nexport class SchemaRegistry {\n private readonly db: DatabaseSync;\n private readonly tables = new Map<string, DefinedTable>();\n private finalized = false;\n\n constructor(db: DatabaseSync) {\n this.db = db;\n }\n\n /**\n * \u6CE8\u518C\u5E76\u521B\u5EFA\u6A21\u5757\u6570\u636E\u8868\u7ED3\u6784\u3002\n *\n * @param moduleId \u6CE8\u518C\u6A21\u5757\u552F\u4E00\u6807\u8BC6\u7B26\u3002\n * @param req \u8868\u7ED3\u6784\u5B9A\u4E49\u8BF7\u6C42\u5BF9\u8C61\u3002\n * @returns \u5EFA\u8868\u7ED3\u679C\u4E0E\u521B\u5EFA\u7684\u7D22\u5F15\u5217\u8868\u3002\n */\n define(moduleId: string, req: DefineTableRequest): DefineTableResult {\n\n if (this.finalized) {\n throw new Error(`[schema] registry \u5DF2 finalize,\u6A21\u5757 ${moduleId} \u4E0D\u80FD\u518D defineTable`);\n }\n assertValid(req, moduleId);\n\n const existing = this.tables.get(req.name);\n if (existing) {\n if (existing.moduleId === moduleId) {\n // \u540C\u6A21\u5757\u91CD\u590D\u5B9A\u4E49:\u6821\u9A8C columns \u4E00\u81F4\n if (JSON.stringify(existing.columns) !== JSON.stringify(req.columns)) {\n throw new Error(`[schema] ${moduleId} \u91CD\u590D defineTable \"${req.name}\",columns \u4E0D\u4E00\u81F4`);\n }\n if (existing.softDelete !== (req.softDelete ?? true)) {\n throw new Error(`[schema] ${moduleId} \u91CD\u590D defineTable \"${req.name}\",softDelete \u4E0D\u4E00\u81F4`);\n }\n log.info(`[schema] ${moduleId} \u91CD\u590D define \"${req.name}\" \u8DF3\u8FC7`);\n return { table: req.name, created: false, indices: [] };\n }\n throw new Error(\n `[schema] \u8868 \"${req.name}\" \u5DF2\u88AB\u6A21\u5757 ${existing.moduleId} \u5B9A\u4E49,${moduleId} \u4E0D\u80FD\u62A2\u540C\u540D\u8868`\n );\n }\n\n const softDelete = req.softDelete ?? true;\n const defined: DefinedTable = {\n moduleId,\n name: req.name,\n columns: req.columns,\n softDelete,\n };\n this.tables.set(req.name, defined);\n // \u7ACB\u5373\u7269\u7406\u5EFA\u8868(\u5E42\u7B49 CREATE TABLE IF NOT EXISTS)\u3002\n // \u539F\u8BBE\u8BA1\u662F\u300Cdefine \u6536\u96C6 \u2192 finalize \u7EDF\u4E00\u5EFA\u8868\u300D,\u4F46 finalize() \u4ECE\u672A\u88AB\u8C03\u7528,\n // \u5BFC\u81F4\u6A21\u5757\u8868\u6C38\u8FDC\u4E0D\u4F1A\u843D\u5730\u3001\u540E\u7EED insert/query \u5168\u90E8 no such table\u3002\n // \u91C7\u7528 define \u5373\u5EFA\u8868:\u78B0\u649E\u68C0\u6D4B\u5DF2\u5728\u4E0A\u65B9\u6309\u5185\u5B58\u8868\u5B8C\u6210,\u4E0D\u5F71\u54CD\u5355\u4E00 owner \u4FDD\u8BC1\u3002\n this.createPhysical(defined);\n return { table: req.name, created: true, indices: [] };\n }\n\n /** \u5E42\u7B49\u5730\u7269\u7406\u5EFA\u8868 + \u5EFA\u7D22\u5F15(\u4F9B define / finalize \u5171\u7528)\u3002 */\n private createPhysical(t: DefinedTable): void {\n const cols = buildColumnList(t.columns, t.softDelete);\n this.db.exec(`CREATE TABLE IF NOT EXISTS \"${t.name}\" (${cols.join(\", \")})`);\n for (const [n, def] of Object.entries(t.columns)) {\n if (def.index) {\n const idxName = `idx_${t.name}_${n}`.slice(0, 60);\n this.db.exec(`CREATE INDEX IF NOT EXISTS \"${idxName}\" ON \"${t.name}\"(\"${n}\")`);\n }\n }\n }\n\n /**\n * \u771F\u6B63\u5EFA\u8868\u7684\u65F6\u523B\u3002\u6240\u6709 enabled \u6A21\u5757 init \u5B8C\u6210(\u6216\u660E\u786E\u8C03)\u540E\u518D finalize\u3002\n * CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS,\u5E42\u7B49\u3002\n */\n finalize(): { tableNames: string[]; created: string[] } {\n if (this.finalized) return { tableNames: [...this.tables.keys()], created: [] };\n this.finalized = true;\n const created: string[] = [];\n\n for (const t of this.tables.values()) {\n const cols = buildColumnList(t.columns, t.softDelete);\n const ddl = `CREATE TABLE IF NOT EXISTS \"${t.name}\" (${cols.join(\", \")})`;\n this.db.exec(ddl);\n\n for (const [n, def] of Object.entries(t.columns)) {\n if (def.index) {\n const idxName = `idx_${t.name}_${n}`.slice(0, 60);\n this.db.exec(`CREATE INDEX IF NOT EXISTS \"${idxName}\" ON \"${t.name}\"(\"${n}\")`);\n }\n }\n\n const exists =\n (this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name=?\")\n .get(t.name) as { name?: string } | undefined)?.name === t.name;\n if (exists) created.push(t.name);\n log.success(`[schema] ${t.moduleId} \u2192 ${t.name} (softDelete=${t.softDelete})`);\n }\n\n return { tableNames: [...this.tables.keys()], created };\n }\n\n listTables(): string[] {\n return [...this.tables.keys()];\n }\n\n isRegistered(name: string): boolean {\n return this.tables.has(name);\n }\n\n getOwner(name: string): string | undefined {\n return this.tables.get(name)?.moduleId;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/server.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ interface CheckResult {
|
|
|
9
9
|
port: number;
|
|
10
10
|
error?: string;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* 检测指定端口是否冲突可用。
|
|
14
|
+
*
|
|
15
|
+
* @param port 待检测的端口号。
|
|
16
|
+
* @returns 包含端口可用性状态的检测结果对象。
|
|
17
|
+
*/
|
|
12
18
|
export declare function checkPortConflict(port: number): Promise<CheckResult>;
|
|
13
19
|
export type ServerEnv = {
|
|
14
20
|
PORT: number;
|
|
@@ -21,7 +27,20 @@ export interface ServerOptions {
|
|
|
21
27
|
handle: Handler;
|
|
22
28
|
onListening?: (server: http.Server) => void;
|
|
23
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* 创建并配置 db-server 的原生 HTTP 服务实例(集成请求耗时监控与状态码日志)。
|
|
32
|
+
*
|
|
33
|
+
* @param options 服务端启动配置选项。
|
|
34
|
+
* @returns 原生 http.Server 实例。
|
|
35
|
+
*/
|
|
24
36
|
export declare function createServer({ handle, env, onListening }: ServerOptions): http.Server;
|
|
37
|
+
/**
|
|
38
|
+
* 启动交互式控制台 REPL 界面,支持在终端输入 `help`、`status`、`stop` 运维管理指令。
|
|
39
|
+
*
|
|
40
|
+
* @param server HTTP 服务实例。
|
|
41
|
+
* @param db 数据库连接实例。
|
|
42
|
+
* @returns 控制台 Readline 接口对象。
|
|
43
|
+
*/
|
|
25
44
|
export declare function startConsole(server: http.Server, db: DatabaseSync | null): readline.Interface;
|
|
26
45
|
export {};
|
|
27
46
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,UAAU,WAAW;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAgBpE;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEpG,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;CAC7C;AAED,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,aAAa,GAAG,IAAI,CAAC,MAAM,CA2BrF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,YAAY,GAAG,IAAI,GAAG,QAAQ,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,UAAU,WAAW;IACnB,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAgBpE;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEpG,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,SAAS,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC;CAC7C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,aAAa,GAAG,IAAI,CAAC,MAAM,CA2BrF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,YAAY,GAAG,IAAI,GAAG,QAAQ,CAAC,SAAS,CA0B7F"}
|
package/dist/server.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/server.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * server.ts \u2014 HTTP \u670D\u52A1 + \u63A7\u5236\u53F0\n */\n\nimport http from \"node:http\";\nimport net from \"node:net\";\nimport readline from \"node:readline\";\nimport type { DatabaseSync } from \"node:sqlite\";\n\nimport { log } from \"./lib/log.js\";\n\ninterface CheckResult {\n ok: boolean;\n port: number;\n error?: string;\n}\n\nexport function checkPortConflict(port: number): Promise<CheckResult> {\n return new Promise((resolve) => {\n const server = net.createServer();\n server.once(\"error\", (err: NodeJS.ErrnoException) => {\n const result: { ok: false; port: number; error?: string } = { ok: false, port };\n if (err.code) {\n result.error = err.code;\n }\n resolve(result);\n });\n server.once(\"listening\", () => {\n server.close();\n resolve({ ok: true, port });\n });\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nexport type ServerEnv = {\n PORT: number;\n HOST: string;\n AUTH_TOKEN?: string;\n};\n\nexport type Handler = (req: http.IncomingMessage, res: http.ServerResponse) => void | Promise<void>;\n\nexport interface ServerOptions {\n env: ServerEnv;\n handle: Handler;\n onListening?: (server: http.Server) => void;\n}\n\nexport function createServer({ handle, env, onListening }: ServerOptions): http.Server {\n const server = http.createServer((req, res) => {\n const startedAt = Date.now();\n let bodyStr = \"\";\n const chunks: Buffer[] = [];\n req.on(\"data\", (c: Buffer) => chunks.push(c));\n req.on(\"end\", () => {\n bodyStr = Buffer.concat(chunks).toString(\"utf-8\").slice(0, 300);\n (req as http.IncomingMessage & { bodyBuffer: string }).bodyBuffer = bodyStr;\n });\n res.once(\"finish\", () => {\n const quietPoll = (req.url ?? \"\").startsWith(\"/api/sfmc/messages?\") && res.statusCode < 400;\n const bodySnippet = req.method === \"GET\" ? \"\" : ` ${bodyStr.slice(0, 200)}`;\n if (!quietPoll)\n log.info(`[HTTP] ${req.method} ${req.url} ${res.statusCode} ${Date.now() - startedAt}ms${bodySnippet}`);\n });\n void handle(req, res);\n });\n\n server.listen(env.PORT, env.HOST, () => {\n log.success(`HTTP \u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u7AEF\u53E3 ${env.PORT} (loopback only)`);\n log.info(`API \u5065\u5EB7\u68C0\u67E5: http://${env.HOST}:${env.PORT}/api/health`);\n log.info(`\u9274\u6743: ${env.AUTH_TOKEN ? \"\u5DF2\u542F\u7528 token\" : \"\u672A\u542F\u7528\"}`);\n if (onListening) onListening(server);\n });\n\n return server;\n}\n\nexport function startConsole(server: http.Server, db: DatabaseSync | null): readline.Interface {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n rl.on(\"line\", (line: string) => {\n const cmd = line.trim().toLowerCase();\n if (cmd === \"help\") {\n console.log(\"DB Server \u53EF\u7528\u547D\u4EE4:\");\n console.log(\" help \u2014 \u663E\u793A\u5E2E\u52A9\");\n console.log(\" status \u2014 \u663E\u793A\u670D\u52A1\u72B6\u6001\");\n console.log(\" stop \u2014 \u505C\u6B62\u670D\u52A1\");\n } else if (cmd === \"status\") {\n console.log(`\u72B6\u6001: \u8FD0\u884C\u4E2D`);\n console.log(` HTTP \u7AEF\u53E3: ${(server.address() as { port: number }).port}`);\n console.log(` \u6570\u636E\u5E93: ${db ? \"(\u5DF2\u8FDE\u63A5)\" : \"(\u672A\u8FDE\u63A5)\"}`);\n } else if (cmd === \"stop\") {\n log.info(\"\u6B63\u5728\u505C\u6B62\u670D\u52A1...\");\n rl.close();\n server.close();\n if (db) db.close();\n process.exit(0);\n } else {\n log.warn(`\u672A\u77E5\u547D\u4EE4: ${cmd}\uFF0C\u8F93\u5165 help \u67E5\u770B\u5E2E\u52A9`);\n }\n });\n rl.on(\"SIGINT\", () => process.exit());\n return rl;\n}\n\n"],
|
|
5
|
-
"mappings": "AAIA,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,cAAc;AAGrB,SAAS,WAAW;
|
|
4
|
+
"sourcesContent": ["/**\n * server.ts \u2014 HTTP \u670D\u52A1 + \u63A7\u5236\u53F0\n */\n\nimport http from \"node:http\";\nimport net from \"node:net\";\nimport readline from \"node:readline\";\nimport type { DatabaseSync } from \"node:sqlite\";\n\nimport { log } from \"./lib/log.js\";\n\ninterface CheckResult {\n ok: boolean;\n port: number;\n error?: string;\n}\n\n/**\n * \u68C0\u6D4B\u6307\u5B9A\u7AEF\u53E3\u662F\u5426\u51B2\u7A81\u53EF\u7528\u3002\n *\n * @param port \u5F85\u68C0\u6D4B\u7684\u7AEF\u53E3\u53F7\u3002\n * @returns \u5305\u542B\u7AEF\u53E3\u53EF\u7528\u6027\u72B6\u6001\u7684\u68C0\u6D4B\u7ED3\u679C\u5BF9\u8C61\u3002\n */\nexport function checkPortConflict(port: number): Promise<CheckResult> {\n return new Promise((resolve) => {\n const server = net.createServer();\n server.once(\"error\", (err: NodeJS.ErrnoException) => {\n const result: { ok: false; port: number; error?: string } = { ok: false, port };\n if (err.code) {\n result.error = err.code;\n }\n resolve(result);\n });\n server.once(\"listening\", () => {\n server.close();\n resolve({ ok: true, port });\n });\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nexport type ServerEnv = {\n PORT: number;\n HOST: string;\n AUTH_TOKEN?: string;\n};\n\nexport type Handler = (req: http.IncomingMessage, res: http.ServerResponse) => void | Promise<void>;\n\nexport interface ServerOptions {\n env: ServerEnv;\n handle: Handler;\n onListening?: (server: http.Server) => void;\n}\n\n/**\n * \u521B\u5EFA\u5E76\u914D\u7F6E db-server \u7684\u539F\u751F HTTP \u670D\u52A1\u5B9E\u4F8B\uFF08\u96C6\u6210\u8BF7\u6C42\u8017\u65F6\u76D1\u63A7\u4E0E\u72B6\u6001\u7801\u65E5\u5FD7\uFF09\u3002\n *\n * @param options \u670D\u52A1\u7AEF\u542F\u52A8\u914D\u7F6E\u9009\u9879\u3002\n * @returns \u539F\u751F http.Server \u5B9E\u4F8B\u3002\n */\nexport function createServer({ handle, env, onListening }: ServerOptions): http.Server {\n const server = http.createServer((req, res) => {\n const startedAt = Date.now();\n let bodyStr = \"\";\n const chunks: Buffer[] = [];\n req.on(\"data\", (c: Buffer) => chunks.push(c));\n req.on(\"end\", () => {\n bodyStr = Buffer.concat(chunks).toString(\"utf-8\").slice(0, 300);\n (req as http.IncomingMessage & { bodyBuffer: string }).bodyBuffer = bodyStr;\n });\n res.once(\"finish\", () => {\n const quietPoll = (req.url ?? \"\").startsWith(\"/api/sfmc/messages?\") && res.statusCode < 400;\n const bodySnippet = req.method === \"GET\" ? \"\" : ` ${bodyStr.slice(0, 200)}`;\n if (!quietPoll)\n log.info(`[HTTP] ${req.method} ${req.url} ${res.statusCode} ${Date.now() - startedAt}ms${bodySnippet}`);\n });\n void handle(req, res);\n });\n\n server.listen(env.PORT, env.HOST, () => {\n log.success(`HTTP \u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u7AEF\u53E3 ${env.PORT} (loopback only)`);\n log.info(`API \u5065\u5EB7\u68C0\u67E5: http://${env.HOST}:${env.PORT}/api/health`);\n log.info(`\u9274\u6743: ${env.AUTH_TOKEN ? \"\u5DF2\u542F\u7528 token\" : \"\u672A\u542F\u7528\"}`);\n if (onListening) onListening(server);\n });\n\n return server;\n}\n\n/**\n * \u542F\u52A8\u4EA4\u4E92\u5F0F\u63A7\u5236\u53F0 REPL \u754C\u9762\uFF0C\u652F\u6301\u5728\u7EC8\u7AEF\u8F93\u5165 `help`\u3001`status`\u3001`stop` \u8FD0\u7EF4\u7BA1\u7406\u6307\u4EE4\u3002\n *\n * @param server HTTP \u670D\u52A1\u5B9E\u4F8B\u3002\n * @param db \u6570\u636E\u5E93\u8FDE\u63A5\u5B9E\u4F8B\u3002\n * @returns \u63A7\u5236\u53F0 Readline \u63A5\u53E3\u5BF9\u8C61\u3002\n */\nexport function startConsole(server: http.Server, db: DatabaseSync | null): readline.Interface {\n\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n rl.on(\"line\", (line: string) => {\n const cmd = line.trim().toLowerCase();\n if (cmd === \"help\") {\n console.log(\"DB Server \u53EF\u7528\u547D\u4EE4:\");\n console.log(\" help \u2014 \u663E\u793A\u5E2E\u52A9\");\n console.log(\" status \u2014 \u663E\u793A\u670D\u52A1\u72B6\u6001\");\n console.log(\" stop \u2014 \u505C\u6B62\u670D\u52A1\");\n } else if (cmd === \"status\") {\n console.log(`\u72B6\u6001: \u8FD0\u884C\u4E2D`);\n console.log(` HTTP \u7AEF\u53E3: ${(server.address() as { port: number }).port}`);\n console.log(` \u6570\u636E\u5E93: ${db ? \"(\u5DF2\u8FDE\u63A5)\" : \"(\u672A\u8FDE\u63A5)\"}`);\n } else if (cmd === \"stop\") {\n log.info(\"\u6B63\u5728\u505C\u6B62\u670D\u52A1...\");\n rl.close();\n server.close();\n if (db) db.close();\n process.exit(0);\n } else {\n log.warn(`\u672A\u77E5\u547D\u4EE4: ${cmd}\uFF0C\u8F93\u5165 help \u67E5\u770B\u5E2E\u52A9`);\n }\n });\n rl.on(\"SIGINT\", () => process.exit());\n return rl;\n}\n\n"],
|
|
5
|
+
"mappings": "AAIA,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,cAAc;AAGrB,SAAS,WAAW;AAcb,SAAS,kBAAkB,MAAoC;AACpE,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,SAAS,IAAI,aAAa;AAChC,WAAO,KAAK,SAAS,CAAC,QAA+B;AACnD,YAAM,SAAsD,EAAE,IAAI,OAAO,KAAK;AAC9E,UAAI,IAAI,MAAM;AACZ,eAAO,QAAQ,IAAI;AAAA,MACrB;AACA,cAAQ,MAAM;AAAA,IAChB,CAAC;AACD,WAAO,KAAK,aAAa,MAAM;AAC7B,aAAO,MAAM;AACb,cAAQ,EAAE,IAAI,MAAM,KAAK,CAAC;AAAA,IAC5B,CAAC;AACD,WAAO,OAAO,MAAM,WAAW;AAAA,EACjC,CAAC;AACH;AAsBO,SAAS,aAAa,EAAE,QAAQ,KAAK,YAAY,GAA+B;AACrF,QAAM,SAAS,KAAK,aAAa,CAAC,KAAK,QAAQ;AAC7C,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI,UAAU;AACd,UAAM,SAAmB,CAAC;AAC1B,QAAI,GAAG,QAAQ,CAAC,MAAc,OAAO,KAAK,CAAC,CAAC;AAC5C,QAAI,GAAG,OAAO,MAAM;AAClB,gBAAU,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO,EAAE,MAAM,GAAG,GAAG;AAC9D,MAAC,IAAsD,aAAa;AAAA,IACtE,CAAC;AACD,QAAI,KAAK,UAAU,MAAM;AACvB,YAAM,aAAa,IAAI,OAAO,IAAI,WAAW,qBAAqB,KAAK,IAAI,aAAa;AACxF,YAAM,cAAc,IAAI,WAAW,QAAQ,KAAK,IAAI,QAAQ,MAAM,GAAG,GAAG,CAAC;AACzE,UAAI,CAAC;AACH,YAAI,KAAK,UAAU,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,UAAU,IAAI,KAAK,IAAI,IAAI,SAAS,KAAK,WAAW,EAAE;AAAA,IAC1G,CAAC;AACD,SAAK,OAAO,KAAK,GAAG;AAAA,EACtB,CAAC;AAED,SAAO,OAAO,IAAI,MAAM,IAAI,MAAM,MAAM;AACtC,QAAI,QAAQ,yDAAiB,IAAI,IAAI,kBAAkB;AACvD,QAAI,KAAK,wCAAoB,IAAI,IAAI,IAAI,IAAI,IAAI,aAAa;AAC9D,QAAI,KAAK,iBAAO,IAAI,aAAa,6BAAc,oBAAK,EAAE;AACtD,QAAI,YAAa,aAAY,MAAM;AAAA,EACrC,CAAC;AAED,SAAO;AACT;AASO,SAAS,aAAa,QAAqB,IAA6C;AAE7F,QAAM,KAAK,SAAS,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AACpF,KAAG,GAAG,QAAQ,CAAC,SAAiB;AAC9B,UAAM,MAAM,KAAK,KAAK,EAAE,YAAY;AACpC,QAAI,QAAQ,QAAQ;AAClB,cAAQ,IAAI,qCAAiB;AAC7B,cAAQ,IAAI,2CAAkB;AAC9B,cAAQ,IAAI,uDAAoB;AAChC,cAAQ,IAAI,2CAAkB;AAAA,IAChC,WAAW,QAAQ,UAAU;AAC3B,cAAQ,IAAI,kCAAS;AACrB,cAAQ,IAAI,wBAAe,OAAO,QAAQ,EAAuB,IAAI,EAAE;AACvE,cAAQ,IAAI,yBAAU,KAAK,yBAAU,sBAAO,EAAE;AAAA,IAChD,WAAW,QAAQ,QAAQ;AACzB,UAAI,KAAK,yCAAW;AACpB,SAAG,MAAM;AACT,aAAO,MAAM;AACb,UAAI,GAAI,IAAG,MAAM;AACjB,cAAQ,KAAK,CAAC;AAAA,IAChB,OAAO;AACL,UAAI,KAAK,6BAAS,GAAG,kDAAe;AAAA,IACtC;AAAA,EACF,CAAC;AACD,KAAG,GAAG,UAAU,MAAM,QAAQ,KAAK,CAAC;AACpC,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* service-registry.ts —
|
|
2
|
+
* service-registry.ts — 跨模块服务注册表与调度中心(Service Registry)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* 协议规范:
|
|
5
|
+
* - HTTP GET `/api/sfmc/services/<name>?input=<urlencoded-json>`
|
|
6
|
+
* - 鉴权机制:Bearer module_token + 查询参数 `?moduleId=<callerId>`
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* 调度与事务边界:
|
|
9
|
+
* 服务处理器注册在 db-server 进程内存中。
|
|
10
|
+
* 在外层 `db.tx` 事务会话内调用 `tx.call` 时,会通过 `ctx.tx = { query, db }` 注入现有事务上下文,
|
|
11
|
+
* 处理器复用当前事务连接,严禁重复开启嵌套事务。
|
|
11
12
|
*/
|
|
12
13
|
import type { DatabaseSync } from "node:sqlite";
|
|
13
14
|
import type { ModuleManifestV2 } from "./manifest-loader.js";
|
|
@@ -23,19 +24,48 @@ export interface ServiceTxContext {
|
|
|
23
24
|
export interface ServiceDispatchContext {
|
|
24
25
|
callerModuleId: string;
|
|
25
26
|
payload: unknown;
|
|
26
|
-
/** 外层 db.tx
|
|
27
|
+
/** 外层 db.tx 已打开时注入;处理器复用此连接,禁止开启嵌套 BEGIN。 */
|
|
27
28
|
tx?: ServiceTxContext;
|
|
28
29
|
}
|
|
29
30
|
export type ServiceHandler = (ctx: ServiceDispatchContext) => Promise<unknown>;
|
|
31
|
+
/** 跨模块服务注册表管理器。 */
|
|
30
32
|
export declare class ServiceRegistry {
|
|
31
33
|
private readonly handlers;
|
|
32
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* 注册指定模块提供的跨模块服务处理器。
|
|
36
|
+
*
|
|
37
|
+
* @param moduleId 服务提供方模块 ID。
|
|
38
|
+
* @param name 服务完整名称。
|
|
39
|
+
* @param handle 业务处理函数。
|
|
40
|
+
*/
|
|
33
41
|
registerHandler(moduleId: string, name: string, handle: ServiceHandler): void;
|
|
42
|
+
/**
|
|
43
|
+
* 注销指定名称的服务处理器。
|
|
44
|
+
*
|
|
45
|
+
* @param name 待注销的服务名称。
|
|
46
|
+
*/
|
|
34
47
|
unregisterHandler(name: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* 获取当前已注册的全部跨模块服务列表。
|
|
50
|
+
*
|
|
51
|
+
* @returns 包含服务名与所属模块 ID 的列表。
|
|
52
|
+
*/
|
|
35
53
|
list(): Array<{
|
|
36
54
|
name: string;
|
|
37
55
|
moduleId: string;
|
|
38
56
|
}>;
|
|
57
|
+
/**
|
|
58
|
+
* 调度并执行指定的跨模块服务。
|
|
59
|
+
* 包含调用方与提供方启停状态校验、声明式契约(`services.requires`)匹配及异常分流。
|
|
60
|
+
*
|
|
61
|
+
* @param enabled 当前所有已启用的模块清单字典。
|
|
62
|
+
* @param callerModuleId 发起调用的模块唯一标识符。
|
|
63
|
+
* @param name 目标服务完整名称。
|
|
64
|
+
* @param payload 传入调用的参数数据。
|
|
65
|
+
* @param tx 可选的当前事务上下文。
|
|
66
|
+
* @returns 服务执行返回结果。
|
|
67
|
+
* @throws {DispatchError} 服务未找到、未声明依赖或鉴权失败时抛出。
|
|
68
|
+
*/
|
|
39
69
|
dispatch(enabled: Map<string, ModuleManifestV2>, callerModuleId: string, name: string, payload: unknown, tx?: ServiceTxContext): Promise<ServiceCallResult>;
|
|
40
70
|
}
|
|
41
71
|
export declare class DispatchError extends Error {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-registry.d.ts","sourceRoot":"","sources":["../src/service-registry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"service-registry.d.ts","sourceRoot":"","sources":["../src/service-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;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,6CAA6C;IAC7C,EAAE,CAAC,EAAE,gBAAgB,CAAC;CACvB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,sBAAsB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAO/E,mBAAmB;AACnB,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IAEjE;;;;;;OAMG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAK5E;IAED;;;;OAIG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEpC;IAED;;;;OAIG;IACH,IAAI,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAEhD;IAED;;;;;;;;;;;OAWG;IACG,QAAQ,CAEZ,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,CAqC5B;CAEF;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,CAAC;IACxF,MAAM,EAAE,MAAM,CAAC;IACf,YACE,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,EACvF,MAAM,EAAE,MAAM,EAKf;CACF"}
|
package/dist/service-registry.js
CHANGED
|
@@ -1,18 +1,46 @@
|
|
|
1
1
|
class ServiceRegistry {
|
|
2
2
|
handlers = /* @__PURE__ */ new Map();
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* 注册指定模块提供的跨模块服务处理器。
|
|
5
|
+
*
|
|
6
|
+
* @param moduleId 服务提供方模块 ID。
|
|
7
|
+
* @param name 服务完整名称。
|
|
8
|
+
* @param handle 业务处理函数。
|
|
9
|
+
*/
|
|
4
10
|
registerHandler(moduleId, name, handle) {
|
|
5
11
|
if (this.handlers.has(name)) {
|
|
6
|
-
throw new Error(`[service] "${name}" \u5DF2\u88AB ${this.handlers.get(name)?.moduleId} \u6CE8\u518C
|
|
12
|
+
throw new Error(`[service] "${name}" \u5DF2\u88AB ${this.handlers.get(name)?.moduleId} \u6CE8\u518C, ${moduleId} \u62A2\u6CE8`);
|
|
7
13
|
}
|
|
8
14
|
this.handlers.set(name, { moduleId, handle });
|
|
9
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* 注销指定名称的服务处理器。
|
|
18
|
+
*
|
|
19
|
+
* @param name 待注销的服务名称。
|
|
20
|
+
*/
|
|
10
21
|
unregisterHandler(name) {
|
|
11
22
|
this.handlers.delete(name);
|
|
12
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* 获取当前已注册的全部跨模块服务列表。
|
|
26
|
+
*
|
|
27
|
+
* @returns 包含服务名与所属模块 ID 的列表。
|
|
28
|
+
*/
|
|
13
29
|
list() {
|
|
14
30
|
return [...this.handlers.entries()].map(([name, h]) => ({ name, moduleId: h.moduleId }));
|
|
15
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* 调度并执行指定的跨模块服务。
|
|
34
|
+
* 包含调用方与提供方启停状态校验、声明式契约(`services.requires`)匹配及异常分流。
|
|
35
|
+
*
|
|
36
|
+
* @param enabled 当前所有已启用的模块清单字典。
|
|
37
|
+
* @param callerModuleId 发起调用的模块唯一标识符。
|
|
38
|
+
* @param name 目标服务完整名称。
|
|
39
|
+
* @param payload 传入调用的参数数据。
|
|
40
|
+
* @param tx 可选的当前事务上下文。
|
|
41
|
+
* @returns 服务执行返回结果。
|
|
42
|
+
* @throws {DispatchError} 服务未找到、未声明依赖或鉴权失败时抛出。
|
|
43
|
+
*/
|
|
16
44
|
async dispatch(enabled, callerModuleId, name, payload, tx) {
|
|
17
45
|
const handler = this.handlers.get(name);
|
|
18
46
|
if (!handler) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/service-registry.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * service-registry.ts \u2014 \u8DE8\u6A21\u5757
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * service-registry.ts \u2014 \u8DE8\u6A21\u5757\u670D\u52A1\u6CE8\u518C\u8868\u4E0E\u8C03\u5EA6\u4E2D\u5FC3\uFF08Service Registry\uFF09\n *\n * \u534F\u8BAE\u89C4\u8303\uFF1A\n * - HTTP GET `/api/sfmc/services/<name>?input=<urlencoded-json>`\n * - \u9274\u6743\u673A\u5236\uFF1ABearer module_token + \u67E5\u8BE2\u53C2\u6570 `?moduleId=<callerId>`\n *\n * \u8C03\u5EA6\u4E0E\u4E8B\u52A1\u8FB9\u754C\uFF1A\n * \u670D\u52A1\u5904\u7406\u5668\u6CE8\u518C\u5728 db-server \u8FDB\u7A0B\u5185\u5B58\u4E2D\u3002\n * \u5728\u5916\u5C42 `db.tx` \u4E8B\u52A1\u4F1A\u8BDD\u5185\u8C03\u7528 `tx.call` \u65F6\uFF0C\u4F1A\u901A\u8FC7 `ctx.tx = { query, db }` \u6CE8\u5165\u73B0\u6709\u4E8B\u52A1\u4E0A\u4E0B\u6587\uFF0C\n * \u5904\u7406\u5668\u590D\u7528\u5F53\u524D\u4E8B\u52A1\u8FDE\u63A5\uFF0C\u4E25\u7981\u91CD\u590D\u5F00\u542F\u5D4C\u5957\u4E8B\u52A1\u3002\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport type { ModuleManifestV2 } from \"./manifest-loader.js\";\nimport type { QueryFn } from \"./lib/sqlite.js\";\n\nexport interface ServiceCallResult {\n ok: true;\n result: unknown;\n}\n\nexport interface ServiceTxContext {\n query: QueryFn;\n db: DatabaseSync;\n}\n\nexport interface ServiceDispatchContext {\n callerModuleId: string;\n payload: unknown;\n /** \u5916\u5C42 db.tx \u5DF2\u6253\u5F00\u65F6\u6CE8\u5165\uFF1B\u5904\u7406\u5668\u590D\u7528\u6B64\u8FDE\u63A5\uFF0C\u7981\u6B62\u5F00\u542F\u5D4C\u5957 BEGIN\u3002 */\n tx?: ServiceTxContext;\n}\n\nexport type ServiceHandler = (ctx: ServiceDispatchContext) => Promise<unknown>;\n\ninterface RegisteredHandler {\n moduleId: string;\n handle: ServiceHandler;\n}\n\n/** \u8DE8\u6A21\u5757\u670D\u52A1\u6CE8\u518C\u8868\u7BA1\u7406\u5668\u3002 */\nexport class ServiceRegistry {\n private readonly handlers = new Map<string, RegisteredHandler>();\n\n /**\n * \u6CE8\u518C\u6307\u5B9A\u6A21\u5757\u63D0\u4F9B\u7684\u8DE8\u6A21\u5757\u670D\u52A1\u5904\u7406\u5668\u3002\n *\n * @param moduleId \u670D\u52A1\u63D0\u4F9B\u65B9\u6A21\u5757 ID\u3002\n * @param name \u670D\u52A1\u5B8C\u6574\u540D\u79F0\u3002\n * @param handle \u4E1A\u52A1\u5904\u7406\u51FD\u6570\u3002\n */\n registerHandler(moduleId: string, name: string, handle: ServiceHandler): void {\n if (this.handlers.has(name)) {\n throw new Error(`[service] \"${name}\" \u5DF2\u88AB ${this.handlers.get(name)?.moduleId} \u6CE8\u518C, ${moduleId} \u62A2\u6CE8`);\n }\n this.handlers.set(name, { moduleId, handle });\n }\n\n /**\n * \u6CE8\u9500\u6307\u5B9A\u540D\u79F0\u7684\u670D\u52A1\u5904\u7406\u5668\u3002\n *\n * @param name \u5F85\u6CE8\u9500\u7684\u670D\u52A1\u540D\u79F0\u3002\n */\n unregisterHandler(name: string): void {\n this.handlers.delete(name);\n }\n\n /**\n * \u83B7\u53D6\u5F53\u524D\u5DF2\u6CE8\u518C\u7684\u5168\u90E8\u8DE8\u6A21\u5757\u670D\u52A1\u5217\u8868\u3002\n *\n * @returns \u5305\u542B\u670D\u52A1\u540D\u4E0E\u6240\u5C5E\u6A21\u5757 ID \u7684\u5217\u8868\u3002\n */\n list(): Array<{ name: string; moduleId: string }> {\n return [...this.handlers.entries()].map(([name, h]) => ({ name, moduleId: h.moduleId }));\n }\n\n /**\n * \u8C03\u5EA6\u5E76\u6267\u884C\u6307\u5B9A\u7684\u8DE8\u6A21\u5757\u670D\u52A1\u3002\n * \u5305\u542B\u8C03\u7528\u65B9\u4E0E\u63D0\u4F9B\u65B9\u542F\u505C\u72B6\u6001\u6821\u9A8C\u3001\u58F0\u660E\u5F0F\u5951\u7EA6\uFF08`services.requires`\uFF09\u5339\u914D\u53CA\u5F02\u5E38\u5206\u6D41\u3002\n *\n * @param enabled \u5F53\u524D\u6240\u6709\u5DF2\u542F\u7528\u7684\u6A21\u5757\u6E05\u5355\u5B57\u5178\u3002\n * @param callerModuleId \u53D1\u8D77\u8C03\u7528\u7684\u6A21\u5757\u552F\u4E00\u6807\u8BC6\u7B26\u3002\n * @param name \u76EE\u6807\u670D\u52A1\u5B8C\u6574\u540D\u79F0\u3002\n * @param payload \u4F20\u5165\u8C03\u7528\u7684\u53C2\u6570\u6570\u636E\u3002\n * @param tx \u53EF\u9009\u7684\u5F53\u524D\u4E8B\u52A1\u4E0A\u4E0B\u6587\u3002\n * @returns \u670D\u52A1\u6267\u884C\u8FD4\u56DE\u7ED3\u679C\u3002\n * @throws {DispatchError} \u670D\u52A1\u672A\u627E\u5230\u3001\u672A\u58F0\u660E\u4F9D\u8D56\u6216\u9274\u6743\u5931\u8D25\u65F6\u629B\u51FA\u3002\n */\n async dispatch(\n\n enabled: Map<string, ModuleManifestV2>,\n callerModuleId: string,\n name: string,\n payload: unknown,\n tx?: ServiceTxContext\n ): Promise<ServiceCallResult> {\n const handler = this.handlers.get(name);\n if (!handler) {\n throw new DispatchError(`service \"${name}\" \u672A\u6CE8\u518C`, \"no_such_service\", 404);\n }\n if (!enabled.has(handler.moduleId)) {\n throw new DispatchError(`service \"${name}\" \u63D0\u4F9B\u65B9 ${handler.moduleId} \u672A enabled`, \"forbidden\", 403);\n }\n const caller = enabled.get(callerModuleId);\n if (!caller) {\n throw new DispatchError(`\u8C03\u7528\u65B9 ${callerModuleId} \u672A enabled`, \"forbidden\", 403);\n }\n const declared = caller.services.requires.find((r) => r.name === name);\n // \u63D0\u4F9B\u65B9\u8C03\u81EA\u5DF1\u7684 service \u514D requires(\u4F8B\u5982 economy \u767D\u76AE\u4E66\u8C03 stats.monthly)\n if (!declared && callerModuleId !== handler.moduleId) {\n throw new DispatchError(\n `${callerModuleId} \u7684 manifest.services.requires \u672A\u58F0\u660E \"${name}\"`,\n \"not_in_requires\",\n 403\n );\n }\n try {\n const result = await handler.handle({\n callerModuleId,\n payload,\n ...(tx ? { tx } : {}),\n });\n return { ok: true, result };\n } catch (e) {\n // \u4FDD\u7559 handler \u629B\u51FA\u7684\u9886\u57DF/\u9274\u6743\u9519\u8BEF\u5951\u7EA6,\u52FF\u538B\u6210 500 internal(LSP)\n if (e instanceof DispatchError) throw e;\n const err = e as Error & { code?: string; status?: number };\n if (typeof err.status === \"number\" && err.code) {\n throw new DispatchError(err.message, err.code as DispatchError[\"code\"], err.status);\n }\n throw new DispatchError(`handler \u629B\u9519: ${err.message}`, \"internal\", 500);\n }\n }\n\n}\n\nexport class DispatchError extends Error {\n code: \"no_such_service\" | \"not_in_requires\" | \"forbidden\" | \"internal\" | \"domain_error\";\n status: number;\n constructor(\n message: string,\n code: \"no_such_service\" | \"not_in_requires\" | \"forbidden\" | \"internal\" | \"domain_error\",\n status: number\n ) {\n super(message);\n this.code = code;\n this.status = status;\n }\n}\n"],
|
|
5
|
+
"mappings": "AA0CO,MAAM,gBAAgB;AAAA,EACV,WAAW,oBAAI,IAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS/D,gBAAgB,UAAkB,MAAc,QAA8B;AAC5E,QAAI,KAAK,SAAS,IAAI,IAAI,GAAG;AAC3B,YAAM,IAAI,MAAM,cAAc,IAAI,kBAAQ,KAAK,SAAS,IAAI,IAAI,GAAG,QAAQ,kBAAQ,QAAQ,eAAK;AAAA,IAClG;AACA,SAAK,SAAS,IAAI,MAAM,EAAE,UAAU,OAAO,CAAC;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,MAAoB;AACpC,SAAK,SAAS,OAAO,IAAI;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAkD;AAChD,WAAO,CAAC,GAAG,KAAK,SAAS,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,UAAU,EAAE,SAAS,EAAE;AAAA,EACzF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,SAEJ,SACA,gBACA,MACA,SACA,IAC4B;AAC5B,UAAM,UAAU,KAAK,SAAS,IAAI,IAAI;AACtC,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,cAAc,YAAY,IAAI,wBAAS,mBAAmB,GAAG;AAAA,IACzE;AACA,QAAI,CAAC,QAAQ,IAAI,QAAQ,QAAQ,GAAG;AAClC,YAAM,IAAI,cAAc,YAAY,IAAI,wBAAS,QAAQ,QAAQ,mBAAc,aAAa,GAAG;AAAA,IACjG;AACA,UAAM,SAAS,QAAQ,IAAI,cAAc;AACzC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,cAAc,sBAAO,cAAc,mBAAc,aAAa,GAAG;AAAA,IAC7E;AACA,UAAM,WAAW,OAAO,SAAS,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAErE,QAAI,CAAC,YAAY,mBAAmB,QAAQ,UAAU;AACpD,YAAM,IAAI;AAAA,QACR,GAAG,cAAc,0DAAsC,IAAI;AAAA,QAC3D;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,OAAO;AAAA,QAClC;AAAA,QACA;AAAA,QACA,GAAI,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,MACrB,CAAC;AACD,aAAO,EAAE,IAAI,MAAM,OAAO;AAAA,IAC5B,SAAS,GAAG;AAEV,UAAI,aAAa,cAAe,OAAM;AACtC,YAAM,MAAM;AACZ,UAAI,OAAO,IAAI,WAAW,YAAY,IAAI,MAAM;AAC9C,cAAM,IAAI,cAAc,IAAI,SAAS,IAAI,MAA+B,IAAI,MAAM;AAAA,MACpF;AACA,YAAM,IAAI,cAAc,yBAAe,IAAI,OAAO,IAAI,YAAY,GAAG;AAAA,IACvE;AAAA,EACF;AAEF;AAEO,MAAM,sBAAsB,MAAM;AAAA,EACvC;AAAA,EACA;AAAA,EACA,YACE,SACA,MACA,QACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* services/builtin-handlers.ts —
|
|
2
|
+
* services/builtin-handlers.ts — 进程内置跨模块服务插件注册表
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* 遵循开闭原则(OCP):新增内置服务仅需向 `BUILTIN_SERVICE_PLUGINS` 追加配置,
|
|
5
|
+
* 避免在主入口硬编码 `if (enabledSet.has(...)) registerXxx` 注册链。
|
|
6
6
|
*/
|
|
7
7
|
import type { DatabaseSync } from "node:sqlite";
|
|
8
8
|
import type { QueryFn } from "../lib/sqlite.js";
|
|
@@ -15,12 +15,32 @@ export type BuiltinServicePlugin = {
|
|
|
15
15
|
moduleId: string;
|
|
16
16
|
register: (registry: ServiceRegistry, deps: BuiltinServiceDeps) => void;
|
|
17
17
|
};
|
|
18
|
-
/** 内置 service 插件清单 —
|
|
18
|
+
/** 内置 service 插件清单 — 扩展注册列表。 */
|
|
19
19
|
export declare const BUILTIN_SERVICE_PLUGINS: BuiltinServicePlugin[];
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* 根据已启用模块集合批量注册内置服务插件。
|
|
22
|
+
*
|
|
23
|
+
* @param registry 服务注册表实例。
|
|
24
|
+
* @param deps 数据库查询与连接依赖。
|
|
25
|
+
* @param enabledSet 当前处于启用状态的模块 ID 集合。
|
|
26
|
+
* @returns 成功注册的插件数量。
|
|
27
|
+
*/
|
|
21
28
|
export declare function registerEnabledBuiltinServices(registry: ServiceRegistry, deps: BuiltinServiceDeps, enabledSet: Set<string>): number;
|
|
22
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* 热启用指定模块的内置服务插件(若尚未注册)。
|
|
31
|
+
*
|
|
32
|
+
* @param registry 服务注册表实例。
|
|
33
|
+
* @param deps 数据库依赖。
|
|
34
|
+
* @param moduleId 目标模块唯一标识符。
|
|
35
|
+
* @returns 若成功注册返回 `true`,若插件不存在或已注册则返回 `false`。
|
|
36
|
+
*/
|
|
23
37
|
export declare function registerBuiltinPluginForModule(registry: ServiceRegistry, deps: BuiltinServiceDeps, moduleId: string): boolean;
|
|
24
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* 热禁用指定模块的全部服务处理器。
|
|
40
|
+
*
|
|
41
|
+
* @param registry 服务注册表实例。
|
|
42
|
+
* @param moduleId 待卸载服务的模块 ID。
|
|
43
|
+
* @returns 成功卸载的服务数量。
|
|
44
|
+
*/
|
|
25
45
|
export declare function unregisterBuiltinPluginForModule(registry: ServiceRegistry, moduleId: string): number;
|
|
26
46
|
//# sourceMappingURL=builtin-handlers.d.ts.map
|
|
@@ -1 +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,
|
|
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,gCAAgC;AAChC,eAAO,MAAM,uBAAuB,EAAE,oBAAoB,EAEzD,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,kBAAkB,EACxB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB,MAAM,CAQR;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,kBAAkB,EACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAOT;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CASpG"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/services/builtin-handlers.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * services/builtin-handlers.ts \u2014 \u8FDB\u7A0B\u5185\u7F6E
|
|
5
|
-
"mappings": "AAUA,SAAS,+BAA+B;AAUjC,MAAM,0BAAkD;AAAA,EAC7D,EAAE,UAAU,mBAAmB,UAAU,wBAAwB;AACnE;
|
|
4
|
+
"sourcesContent": ["/**\n * services/builtin-handlers.ts \u2014 \u8FDB\u7A0B\u5185\u7F6E\u8DE8\u6A21\u5757\u670D\u52A1\u63D2\u4EF6\u6CE8\u518C\u8868\n *\n * \u9075\u5FAA\u5F00\u95ED\u539F\u5219\uFF08OCP\uFF09\uFF1A\u65B0\u589E\u5185\u7F6E\u670D\u52A1\u4EC5\u9700\u5411 `BUILTIN_SERVICE_PLUGINS` \u8FFD\u52A0\u914D\u7F6E\uFF0C\n * \u907F\u514D\u5728\u4E3B\u5165\u53E3\u786C\u7F16\u7801 `if (enabledSet.has(...)) registerXxx` \u6CE8\u518C\u94FE\u3002\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport type { QueryFn } from \"../lib/sqlite.js\";\nimport type { ServiceRegistry } from \"../service-registry.js\";\nimport { registerEconomyHandlers } from \"./economy-handlers.js\";\n\nexport type BuiltinServiceDeps = { query: QueryFn; db: DatabaseSync };\n\nexport type BuiltinServicePlugin = {\n moduleId: string;\n register: (registry: ServiceRegistry, deps: BuiltinServiceDeps) => void;\n};\n\n/** \u5185\u7F6E service \u63D2\u4EF6\u6E05\u5355 \u2014 \u6269\u5C55\u6CE8\u518C\u5217\u8868\u3002 */\nexport const BUILTIN_SERVICE_PLUGINS: BuiltinServicePlugin[] = [\n { moduleId: \"feature-economy\", register: registerEconomyHandlers },\n];\n\n/**\n * \u6839\u636E\u5DF2\u542F\u7528\u6A21\u5757\u96C6\u5408\u6279\u91CF\u6CE8\u518C\u5185\u7F6E\u670D\u52A1\u63D2\u4EF6\u3002\n *\n * @param registry \u670D\u52A1\u6CE8\u518C\u8868\u5B9E\u4F8B\u3002\n * @param deps \u6570\u636E\u5E93\u67E5\u8BE2\u4E0E\u8FDE\u63A5\u4F9D\u8D56\u3002\n * @param enabledSet \u5F53\u524D\u5904\u4E8E\u542F\u7528\u72B6\u6001\u7684\u6A21\u5757 ID \u96C6\u5408\u3002\n * @returns \u6210\u529F\u6CE8\u518C\u7684\u63D2\u4EF6\u6570\u91CF\u3002\n */\nexport function registerEnabledBuiltinServices(\n registry: ServiceRegistry,\n deps: BuiltinServiceDeps,\n enabledSet: Set<string>\n): number {\n let n = 0;\n for (const plugin of BUILTIN_SERVICE_PLUGINS) {\n if (!enabledSet.has(plugin.moduleId)) continue;\n plugin.register(registry, deps);\n n += 1;\n }\n return n;\n}\n\n/**\n * \u70ED\u542F\u7528\u6307\u5B9A\u6A21\u5757\u7684\u5185\u7F6E\u670D\u52A1\u63D2\u4EF6\uFF08\u82E5\u5C1A\u672A\u6CE8\u518C\uFF09\u3002\n *\n * @param registry \u670D\u52A1\u6CE8\u518C\u8868\u5B9E\u4F8B\u3002\n * @param deps \u6570\u636E\u5E93\u4F9D\u8D56\u3002\n * @param moduleId \u76EE\u6807\u6A21\u5757\u552F\u4E00\u6807\u8BC6\u7B26\u3002\n * @returns \u82E5\u6210\u529F\u6CE8\u518C\u8FD4\u56DE `true`\uFF0C\u82E5\u63D2\u4EF6\u4E0D\u5B58\u5728\u6216\u5DF2\u6CE8\u518C\u5219\u8FD4\u56DE `false`\u3002\n */\nexport function registerBuiltinPluginForModule(\n registry: ServiceRegistry,\n deps: BuiltinServiceDeps,\n moduleId: string\n): boolean {\n const plugin = BUILTIN_SERVICE_PLUGINS.find((p) => p.moduleId === moduleId);\n if (!plugin) return false;\n const already = registry.list().some((h) => h.moduleId === moduleId);\n if (already) return false;\n plugin.register(registry, deps);\n return true;\n}\n\n/**\n * \u70ED\u7981\u7528\u6307\u5B9A\u6A21\u5757\u7684\u5168\u90E8\u670D\u52A1\u5904\u7406\u5668\u3002\n *\n * @param registry \u670D\u52A1\u6CE8\u518C\u8868\u5B9E\u4F8B\u3002\n * @param moduleId \u5F85\u5378\u8F7D\u670D\u52A1\u7684\u6A21\u5757 ID\u3002\n * @returns \u6210\u529F\u5378\u8F7D\u7684\u670D\u52A1\u6570\u91CF\u3002\n */\nexport function unregisterBuiltinPluginForModule(registry: ServiceRegistry, moduleId: string): number {\n\n let n = 0;\n for (const h of registry.list()) {\n if (h.moduleId !== moduleId) continue;\n registry.unregisterHandler(h.name);\n n += 1;\n }\n return n;\n}\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,+BAA+B;AAUjC,MAAM,0BAAkD;AAAA,EAC7D,EAAE,UAAU,mBAAmB,UAAU,wBAAwB;AACnE;AAUO,SAAS,+BACd,UACA,MACA,YACQ;AACR,MAAI,IAAI;AACR,aAAW,UAAU,yBAAyB;AAC5C,QAAI,CAAC,WAAW,IAAI,OAAO,QAAQ,EAAG;AACtC,WAAO,SAAS,UAAU,IAAI;AAC9B,SAAK;AAAA,EACP;AACA,SAAO;AACT;AAUO,SAAS,+BACd,UACA,MACA,UACS;AACT,QAAM,SAAS,wBAAwB,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ;AAC1E,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,SAAS,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ;AACnE,MAAI,QAAS,QAAO;AACpB,SAAO,SAAS,UAAU,IAAI;AAC9B,SAAO;AACT;AASO,SAAS,iCAAiC,UAA2B,UAA0B;AAEpG,MAAI,IAAI;AACR,aAAW,KAAK,SAAS,KAAK,GAAG;AAC/B,QAAI,EAAE,aAAa,SAAU;AAC7B,aAAS,kBAAkB,EAAE,IAAI;AACjC,SAAK;AAAA,EACP;AACA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/tx-runner.d.ts
CHANGED
|
@@ -131,19 +131,45 @@ export declare class TxRunner {
|
|
|
131
131
|
constructor(deps: TxRunnerDeps);
|
|
132
132
|
run(req: TxRequest): Promise<TxResponse | TxError>;
|
|
133
133
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
134
|
+
* 开启交互式事务会话(协议流程:begin → step* → commit | rollback)。
|
|
135
|
+
* 供客户端 `db.tx` 在回调内部异步执行并实时读回 query / get / call 的服务端执行结果。
|
|
136
|
+
* 单连接保护:新会话开启时自动清理历史残留会话,避免事务锁冲突。
|
|
137
|
+
*
|
|
138
|
+
* @param moduleId 请求模块唯一标识符。
|
|
139
|
+
* @returns 成功返回分配的会话 txId,失败返回错误响应。
|
|
137
140
|
*/
|
|
138
141
|
beginSession(moduleId: string): {
|
|
139
142
|
ok: true;
|
|
140
143
|
txId: string;
|
|
141
144
|
} | TxError;
|
|
145
|
+
/**
|
|
146
|
+
* 在当前交互式事务会话中顺序执行单个步骤。
|
|
147
|
+
*
|
|
148
|
+
* @param txId 会话唯一标识符。
|
|
149
|
+
* @param moduleId 调用模块标识。
|
|
150
|
+
* @param step 待执行的操作步骤对象。
|
|
151
|
+
* @returns 步骤执行结果或错误响应。
|
|
152
|
+
*/
|
|
142
153
|
stepSession(txId: string, moduleId: string, step: TxStep): Promise<{
|
|
143
154
|
ok: true;
|
|
144
155
|
result: TxStepResult;
|
|
145
156
|
} | TxError>;
|
|
157
|
+
/**
|
|
158
|
+
* 提交当前交互式事务会话,将所有步骤的修改持久化并释放连接。
|
|
159
|
+
*
|
|
160
|
+
* @param txId 会话唯一标识符。
|
|
161
|
+
* @param moduleId 调用模块标识。
|
|
162
|
+
* @returns 提交结果响应。
|
|
163
|
+
*/
|
|
146
164
|
commitSession(txId: string, moduleId: string): TxResponse | TxError;
|
|
165
|
+
/**
|
|
166
|
+
* 回滚当前交互式事务会话,撤销所有未提交的数据库变更。
|
|
167
|
+
* 具有幂等性:会话已结束或已回滚时同样返回成功。
|
|
168
|
+
*
|
|
169
|
+
* @param txId 会话唯一标识符。
|
|
170
|
+
* @param moduleId 调用模块标识。
|
|
171
|
+
* @returns 回滚确认结果。
|
|
172
|
+
*/
|
|
147
173
|
rollbackSession(txId: string, moduleId: string): {
|
|
148
174
|
ok: true;
|
|
149
175
|
} | TxError;
|
package/dist/tx-runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tx-runner.d.ts","sourceRoot":"","sources":["../src/tx-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;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;AAc7D,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,SAAS,CAAC;QAClB,+CAA+C;QAC/C,OAAO,CAAC,EACJ;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;SAAE,GACtD,KAAK,CAAC;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;SAAE,CAAC,CAAC;QAClE,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;AAQD,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;IAGP,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IAEzD,YAA6B,IAAI,EAAE,YAAY,EAAI;IAE7C,GAAG,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,CAwCvD;IAED
|
|
1
|
+
{"version":3,"file":"tx-runner.d.ts","sourceRoot":"","sources":["../src/tx-runner.ts"],"names":[],"mappings":"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"}
|
package/dist/tx-runner.js
CHANGED
|
@@ -51,9 +51,12 @@ class TxRunner {
|
|
|
51
51
|
return { ok: true, results };
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
54
|
+
* 开启交互式事务会话(协议流程:begin → step* → commit | rollback)。
|
|
55
|
+
* 供客户端 `db.tx` 在回调内部异步执行并实时读回 query / get / call 的服务端执行结果。
|
|
56
|
+
* 单连接保护:新会话开启时自动清理历史残留会话,避免事务锁冲突。
|
|
57
|
+
*
|
|
58
|
+
* @param moduleId 请求模块唯一标识符。
|
|
59
|
+
* @returns 成功返回分配的会话 txId,失败返回错误响应。
|
|
57
60
|
*/
|
|
58
61
|
beginSession(moduleId) {
|
|
59
62
|
const manifest = this.deps.enabled.get(moduleId);
|
|
@@ -77,6 +80,14 @@ class TxRunner {
|
|
|
77
80
|
log.info(`[tx-session ${txId.slice(0, 8)}] begin module=${moduleId}`);
|
|
78
81
|
return { ok: true, txId };
|
|
79
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* 在当前交互式事务会话中顺序执行单个步骤。
|
|
85
|
+
*
|
|
86
|
+
* @param txId 会话唯一标识符。
|
|
87
|
+
* @param moduleId 调用模块标识。
|
|
88
|
+
* @param step 待执行的操作步骤对象。
|
|
89
|
+
* @returns 步骤执行结果或错误响应。
|
|
90
|
+
*/
|
|
80
91
|
async stepSession(txId, moduleId, step) {
|
|
81
92
|
const session = this.sessions.get(txId);
|
|
82
93
|
if (!session) {
|
|
@@ -102,6 +113,13 @@ class TxRunner {
|
|
|
102
113
|
return { ok: false, step: stepIndex, error: err.message, code };
|
|
103
114
|
}
|
|
104
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* 提交当前交互式事务会话,将所有步骤的修改持久化并释放连接。
|
|
118
|
+
*
|
|
119
|
+
* @param txId 会话唯一标识符。
|
|
120
|
+
* @param moduleId 调用模块标识。
|
|
121
|
+
* @returns 提交结果响应。
|
|
122
|
+
*/
|
|
105
123
|
commitSession(txId, moduleId) {
|
|
106
124
|
const session = this.sessions.get(txId);
|
|
107
125
|
if (!session) {
|
|
@@ -121,6 +139,14 @@ class TxRunner {
|
|
|
121
139
|
log.info(`[tx-session ${txId.slice(0, 8)}] commit ${results.length} steps OK`);
|
|
122
140
|
return { ok: true, results };
|
|
123
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* 回滚当前交互式事务会话,撤销所有未提交的数据库变更。
|
|
144
|
+
* 具有幂等性:会话已结束或已回滚时同样返回成功。
|
|
145
|
+
*
|
|
146
|
+
* @param txId 会话唯一标识符。
|
|
147
|
+
* @param moduleId 调用模块标识。
|
|
148
|
+
* @returns 回滚确认结果。
|
|
149
|
+
*/
|
|
124
150
|
rollbackSession(txId, moduleId) {
|
|
125
151
|
const session = this.sessions.get(txId);
|
|
126
152
|
if (!session) {
|