@sfmc-bds/db-server 0.2.0-beta.1 → 0.2.0-beta.11
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/player-session.d.ts +8 -0
- package/dist/domain/player-session.d.ts.map +1 -0
- package/dist/domain/player-session.js +16 -0
- package/dist/domain/player-session.js.map +7 -0
- package/dist/domain/player-session.test.d.ts +2 -0
- package/dist/domain/player-session.test.d.ts.map +1 -0
- package/dist/domain/player-session.test.js +33 -0
- package/dist/domain/player-session.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 +15 -18
- package/dist/domain/schema.d.ts.map +1 -1
- package/dist/domain/schema.js +104 -453
- 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 +57 -9
- package/dist/env.js.map +2 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +128 -38
- 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/log.d.ts.map +1 -1
- package/dist/lib/log.js +3 -6
- package/dist/lib/log.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/module-auth.d.ts.map +1 -1
- package/dist/module-auth.js +2 -6
- package/dist/module-auth.js.map +2 -2
- package/dist/permission-gate.d.ts.map +1 -1
- package/dist/permission-gate.js +8 -2
- package/dist/permission-gate.js.map +2 -2
- package/dist/project-root.d.ts +6 -0
- package/dist/project-root.d.ts.map +1 -0
- package/dist/project-root.js +9 -0
- package/dist/project-root.js.map +7 -0
- 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 +8 -2
- package/dist/routes/db-routes.d.ts.map +1 -1
- package/dist/routes/db-routes.js +5 -12
- 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/runtime.test.js +216 -4
- package/dist/runtime.test.js.map +2 -2
- package/dist/schema-registry.d.ts +17 -16
- package/dist/schema-registry.d.ts.map +1 -1
- package/dist/schema-registry.js +76 -6
- 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 +48 -9
- package/dist/tx-runner.d.ts.map +1 -1
- package/dist/tx-runner.js +99 -27
- package/dist/tx-runner.js.map +2 -2
- package/package.json +26 -24
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/routes/qq-join.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * routes/qq-join.ts \u2014 \u5165\u670D\u5BA1\u6279\u72B6\u6001\u673A + BDS \u751F\u6548\u961F\u5217\uFF08\u4E0D\u5199 allowlist \u6587\u4EF6\uFF09\n *\n * GET /api/sfmc/qq/join/settings\n * POST /api/sfmc/qq/join/settings\n * POST /api/sfmc/qq/join/request\n * POST /api/sfmc/qq/join/decide\n * GET /api/sfmc/qq/join/pending\n * GET /api/sfmc/qq/join/apply-queue\n * POST /api/sfmc/qq/join/applied\n * POST /api/sfmc/qq/admin/kick\n * GET /api/sfmc/qq/admin/action-queue\n * POST /api/sfmc/qq/admin/action-done\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { SQL } from \"sql-template-strings\";\nimport type { QueryFn } from \"../lib/sqlite.js\";\n\nexport type JoinFeatureFlags = {\n /** \u5165\u670D\u767D\u540D\u5355\u603B\u5F00\u5173 */\n allowlistEnabled: boolean;\n /** \u662F\u5426\u9700\u8981\u7BA1\u7406\u5458\u5BA1\u6279 */\n requireApproval: boolean;\n /**\n * \u662F\u5426\u5C06 QQ \u7FA4\u4E3B/\u7FA4\u7BA1\u7406\u5458\u89C6\u4F5C SFMC \u7BA1\u7406\u5458\u3002\n * \u4EC5 configs/qq_link.json \u53EF\u6539\uFF0CAPI/\u7FA4\u804A\u4E0D\u53EF\u5199\u3002\n */\n treatGroupAdminsAsAdmins: boolean;\n};\n\ninterface Deps {\n query: QueryFn;\n body: (req: import(\"http\").IncomingMessage) => Promise<Record<string, unknown>>;\n json: (res: import(\"http\").ServerResponse, data: Record<string, unknown>, status?: number) => void;\n /** \u5F53\u524D\u7BA1\u7406\u5458 openid \u5217\u8868\uFF08\u6765\u81EA qq_config\uFF09 */\n getAdminOpenids: () => string[];\n /** \u8BFB\u53D6\u5165\u670D\u5F00\u5173\uFF08\u5E94\u8BFB\u6700\u65B0 qq_config\uFF09 */\n getJoinFlags: () => JoinFeatureFlags;\n /** \u5199\u56DE\u5165\u670D\u5F00\u5173\uFF08\u6301\u4E45\u5316 + \u5185\u5B58\uFF09 */\n setJoinFlags: (partial: Partial<JoinFeatureFlags>) => JoinFeatureFlags;\n}\n\nfunction newId(prefix: string): string {\n return `${prefix}_${Date.now().toString(36)}_${randomBytes(4).toString(\"hex\")}`;\n}\n\nfunction isAdmin(getAdminOpenids: () => string[], openid: string): boolean {\n const list = getAdminOpenids();\n if (!openid || list.length === 0) return false;\n return list.includes(openid);\n}\n\n/** openid \u5728\u540D\u5355\u5185\uFF0C\u6216\uFF08\u5F00\u5173\u5F00\u4E14 bridge \u58F0\u660E as_group_admin\uFF09 */\nfunction authorizeAdmin(\n getAdminOpenids: () => string[],\n getJoinFlags: () => JoinFeatureFlags,\n openid: string,\n data: Record<string, unknown>\n): boolean {\n if (isAdmin(getAdminOpenids, openid)) return true;\n const asGroup =\n data.as_group_admin === true ||\n data.as_group_admin === \"true\" ||\n data.is_group_admin === true ||\n data.is_group_admin === \"true\";\n return asGroup && getJoinFlags().treatGroupAdminsAsAdmins === true;\n}\n\nfunction createQqJoinRoutes({ query, body, json, getAdminOpenids, getJoinFlags, setJoinFlags }: Deps) {\n return async function handle({\n path,\n method,\n params,\n req,\n res,\n }: {\n path: string;\n method: string;\n params: URLSearchParams;\n req: import(\"http\").IncomingMessage;\n res: import(\"http\").ServerResponse;\n }): Promise<boolean> {\n const isJoin = path.startsWith(\"/api/sfmc/qq/join\");\n const isAdminPath = path.startsWith(\"/api/sfmc/qq/admin\");\n if (!isJoin && !isAdminPath) return false;\n\n // \u2500\u2500 \u5165\u670D\u5F00\u5173\uFF08\u6587\u4EF6 + \u673A\u5668\u4EBA\u53EF\u6539\u767D\u540D\u5355/\u5BA1\u6279\uFF1B\u7FA4\u7BA1\u5F00\u5173\u53EA\u8BFB\uFF09\u2500\u2500\n if (path === \"/api/sfmc/qq/join/settings\" && method === \"GET\") {\n const flags = getJoinFlags();\n json(res, {\n success: true,\n allowlist_enabled: flags.allowlistEnabled,\n require_approval: flags.requireApproval,\n treat_group_admins_as_admins: flags.treatGroupAdminsAsAdmins,\n });\n return true;\n }\n\n if (path === \"/api/sfmc/qq/join/settings\" && method === \"POST\") {\n const data = await body(req);\n const openid = String(data.openid ?? data.decided_by ?? \"\").trim();\n if (\n Object.prototype.hasOwnProperty.call(data, \"treat_group_admins_as_admins\") ||\n Object.prototype.hasOwnProperty.call(data, \"treatGroupAdminsAsAdmins\")\n ) {\n json(\n res,\n {\n success: false,\n error: \"immutable_field\",\n note: \"treat_group_admins_as_admins \u53EA\u80FD\u6539 configs/qq_link.json\uFF0C\u7FA4\u804A\u4E0D\u53EF\u6539\",\n },\n 400\n );\n return true;\n }\n if (!authorizeAdmin(getAdminOpenids, getJoinFlags, openid, data)) {\n json(res, { success: false, error: \"not_admin\" }, 403);\n return true;\n }\n const partial: Partial<JoinFeatureFlags> = {};\n if (Object.prototype.hasOwnProperty.call(data, \"allowlist_enabled\")) {\n partial.allowlistEnabled = data.allowlist_enabled === true || data.allowlist_enabled === \"true\";\n }\n if (Object.prototype.hasOwnProperty.call(data, \"require_approval\")) {\n partial.requireApproval = data.require_approval === true || data.require_approval === \"true\";\n }\n if (partial.allowlistEnabled === undefined && partial.requireApproval === undefined) {\n json(res, { success: false, error: \"no_settings\" }, 400);\n return true;\n }\n const flags = setJoinFlags(partial);\n json(res, {\n success: true,\n allowlist_enabled: flags.allowlistEnabled,\n require_approval: flags.requireApproval,\n treat_group_admins_as_admins: flags.treatGroupAdminsAsAdmins,\n });\n return true;\n }\n\n // \u2500\u2500 \u5165\u670D\u7533\u8BF7 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (path === \"/api/sfmc/qq/join/request\" && method === \"POST\") {\n const flags = getJoinFlags();\n if (!flags.allowlistEnabled) {\n json(res, { success: false, error: \"join_allowlist_disabled\" }, 403);\n return true;\n }\n const data = await body(req);\n const openid = String(data.openid ?? data.applicant_openid ?? \"\").trim();\n const playerName = String(data.player_name ?? data.name ?? \"\").trim();\n const backend = String(data.qq_backend ?? \"official\").trim() || \"official\";\n if (!openid) {\n json(res, { success: false, error: \"openid_required\" }, 400);\n return true;\n }\n if (!playerName || playerName.length > 32) {\n json(res, { success: false, error: \"player_name_required\" }, 400);\n return true;\n }\n const id = newId(\"join\");\n const now = Date.now();\n const autoApproved = !flags.requireApproval;\n const status = autoApproved ? \"approved\" : \"pending\";\n if (autoApproved) {\n query(SQL`\n INSERT INTO sfmc_qq_join_requests\n (id, applicant_openid, player_name, status, qq_backend, created_at, decided_at, decided_by)\n VALUES (${id}, ${openid}, ${playerName}, ${status}, ${backend}, ${now}, ${now}, 'auto')\n `);\n } else {\n query(SQL`\n INSERT INTO sfmc_qq_join_requests\n (id, applicant_openid, player_name, status, qq_backend, created_at)\n VALUES (${id}, ${openid}, ${playerName}, ${status}, ${backend}, ${now})\n `);\n }\n const admins = getAdminOpenids();\n json(res, {\n success: true,\n id,\n player_name: playerName,\n status,\n auto_approved: autoApproved,\n admin_notify: !autoApproved && admins.length > 0,\n note: autoApproved\n ? \"\u5DF2\u5173\u95ED\u5BA1\u6279\uFF0C\u7533\u8BF7\u5DF2\u81EA\u52A8\u901A\u8FC7\uFF0C\u7B49\u5F85 BDS \u5199\u5165\u767D\u540D\u5355\"\n : admins.length === 0\n ? \"\u672A\u914D\u7F6E qq_admin_openids\uFF0C\u65E0\u6CD5\u901A\u77E5\u7BA1\u7406\u5458\"\n : undefined,\n });\n return true;\n }\n\n if (path === \"/api/sfmc/qq/join/decide\" && method === \"POST\") {\n const data = await body(req);\n const id = String(data.id ?? data.request_id ?? \"\").trim();\n const decision = String(data.decision ?? \"\").trim().toLowerCase();\n const decidedBy = String(data.decided_by ?? data.openid ?? \"\").trim();\n if (!id || (decision !== \"approve\" && decision !== \"reject\")) {\n json(res, { success: false, error: \"id_and_decision_required\" }, 400);\n return true;\n }\n if (!authorizeAdmin(getAdminOpenids, getJoinFlags, decidedBy, data)) {\n json(res, { success: false, error: \"not_admin\" }, 403);\n return true;\n }\n const rows = query(SQL`SELECT * FROM sfmc_qq_join_requests WHERE id = ${id} LIMIT 1`) as Array<\n Record<string, unknown>\n >;\n const row = rows[0];\n if (!row) {\n json(res, { success: false, error: \"not_found\" }, 404);\n return true;\n }\n if (String(row.status) !== \"pending\") {\n json(res, { success: false, error: \"already_decided\", status: row.status }, 409);\n return true;\n }\n const next = decision === \"approve\" ? \"approved\" : \"rejected\";\n const now = Date.now();\n query(SQL`\n UPDATE sfmc_qq_join_requests\n SET status = ${next}, decided_at = ${now}, decided_by = ${decidedBy}\n WHERE id = ${id}\n `);\n json(res, {\n success: true,\n id,\n status: next,\n player_name: row.player_name,\n applicant_openid: row.applicant_openid,\n });\n return true;\n }\n\n if (path === \"/api/sfmc/qq/join/pending\" && method === \"GET\") {\n const openid = String(params.get(\"openid\") ?? \"\").trim();\n if (openid) {\n const asGroup =\n params.get(\"as_group_admin\") === \"true\" || params.get(\"is_group_admin\") === \"true\";\n if (!authorizeAdmin(getAdminOpenids, getJoinFlags, openid, { as_group_admin: asGroup })) {\n json(res, { success: false, error: \"not_admin\" }, 403);\n return true;\n }\n }\n const rows = query(\n SQL`SELECT id, applicant_openid, player_name, status, created_at\n FROM sfmc_qq_join_requests WHERE status = 'pending'\n ORDER BY created_at ASC LIMIT 50`\n ) as Array<Record<string, unknown>>;\n json(res, { success: true, pending: rows });\n return true;\n }\n\n if (path === \"/api/sfmc/qq/join/apply-queue\" && method === \"GET\") {\n const flags = getJoinFlags();\n if (!flags.allowlistEnabled) {\n json(res, { success: true, queue: [], note: \"join_allowlist_disabled\" });\n return true;\n }\n const rows = query(\n SQL`SELECT id, player_name, applicant_openid, decided_at\n FROM sfmc_qq_join_requests WHERE status = 'approved'\n ORDER BY decided_at ASC LIMIT 32`\n ) as Array<Record<string, unknown>>;\n json(res, { success: true, queue: rows });\n return true;\n }\n\n if (path === \"/api/sfmc/qq/join/applied\" && method === \"POST\") {\n const data = await body(req);\n const id = String(data.id ?? \"\").trim();\n const ok = data.ok !== false && data.error == null;\n const err = String(data.error ?? \"\").slice(0, 200);\n if (!id) {\n json(res, { success: false, error: \"id_required\" }, 400);\n return true;\n }\n const now = Date.now();\n if (ok) {\n query(SQL`\n UPDATE sfmc_qq_join_requests\n SET status = 'applied', applied_at = ${now}, apply_error = NULL\n WHERE id = ${id} AND status = 'approved'\n `);\n } else {\n query(SQL`\n UPDATE sfmc_qq_join_requests\n SET status = 'apply_failed', applied_at = ${now}, apply_error = ${err}\n WHERE id = ${id} AND status = 'approved'\n `);\n }\n json(res, { success: true });\n return true;\n }\n\n // \u2500\u2500 \u8E22\u4EBA\u961F\u5217 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (path === \"/api/sfmc/qq/admin/kick\" && method === \"POST\") {\n const data = await body(req);\n const openid = String(data.openid ?? data.requested_by ?? \"\").trim();\n const target = String(data.target_name ?? data.name ?? \"\").trim();\n const reason = String(data.reason ?? \"QQ \u7BA1\u7406\u5458\u8E22\u51FA\").trim().slice(0, 80);\n if (!authorizeAdmin(getAdminOpenids, getJoinFlags, openid, data)) {\n json(res, { success: false, error: \"not_admin\" }, 403);\n return true;\n }\n if (!target) {\n json(res, { success: false, error: \"target_name_required\" }, 400);\n return true;\n }\n const id = newId(\"act\");\n const now = Date.now();\n query(SQL`\n INSERT INTO sfmc_qq_admin_actions\n (id, kind, target_name, reason, requested_by, status, created_at)\n VALUES (${id}, 'kick', ${target}, ${reason}, ${openid}, 'pending', ${now})\n `);\n json(res, { success: true, id });\n return true;\n }\n\n if (path === \"/api/sfmc/qq/admin/action-queue\" && method === \"GET\") {\n const rows = query(\n SQL`SELECT id, kind, target_name, reason, created_at\n FROM sfmc_qq_admin_actions WHERE status = 'pending'\n ORDER BY created_at ASC LIMIT 32`\n ) as Array<Record<string, unknown>>;\n json(res, { success: true, queue: rows });\n return true;\n }\n\n if (path === \"/api/sfmc/qq/admin/action-done\" && method === \"POST\") {\n const data = await body(req);\n const id = String(data.id ?? \"\").trim();\n const ok = data.ok !== false && data.error == null;\n const err = String(data.error ?? \"\").slice(0, 200);\n if (!id) {\n json(res, { success: false, error: \"id_required\" }, 400);\n return true;\n }\n const now = Date.now();\n const status = ok ? \"done\" : \"failed\";\n query(SQL`\n UPDATE sfmc_qq_admin_actions\n SET status = ${status}, applied_at = ${now}, apply_error = ${ok ? \"\" : err}\n WHERE id = ${id} AND status = 'pending'\n `);\n json(res, { success: true });\n return true;\n }\n\n json(res, { success: false, error: \"not_found\" }, 404);\n return true;\n };\n}\n\nexport { createQqJoinRoutes };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -15,6 +15,12 @@ export interface ServiceRoutesDeps {
|
|
|
15
15
|
enabled: Map<string, import("../manifest-loader.js").ModuleManifestV2>;
|
|
16
16
|
json?: typeof defaultJson;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* 创建 `/api/sfmc/services/*` 跨模块服务路由请求处理器。
|
|
20
|
+
*
|
|
21
|
+
* @param depsIn 依赖注入项(包含 serviceRegistry、enabled 模块字典等)。
|
|
22
|
+
* @returns 异步路由处理函数。
|
|
23
|
+
*/
|
|
18
24
|
export declare function createServiceRoutes(depsIn: Partial<ServiceRoutesDeps>): (ctx: {
|
|
19
25
|
path: string;
|
|
20
26
|
method: Method | string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-routes.d.ts","sourceRoot":"","sources":["../../src/routes/service-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,uBAAuB,EAAE,gBAAgB,CAAC,CAAC;IACvE,IAAI,CAAC,EAAE,OAAO,WAAW,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,SAOjD;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,GAAG,EAAE,eAAe,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,KAAG,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"service-routes.d.ts","sourceRoot":"","sources":["../../src/routes/service-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,eAAe,CAAC;IACjC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,uBAAuB,EAAE,gBAAgB,CAAC,CAAC;IACvE,IAAI,CAAC,EAAE,OAAO,WAAW,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,SAOjD;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,GAAG,EAAE,eAAe,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,KAAG,OAAO,CAAC,OAAO,CAAC,CAqDrB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/routes/service-routes.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * routes/service-routes.ts \u2014 /api/sfmc/services/* \u5904\u7406\u5668\n *\n * \u7AEF\u70B9:\n * GET /api/sfmc/services \u2192 { services: [{name, moduleId}] }\n * GET /api/sfmc/services/:name?input=<urlencoded json>\n * \u2192 { ok: true, result } | { ok: false, error, code } (+ HTTP status)\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { json as defaultJson, type Method } from \"../lib/http.js\";\nimport { assertModulePermission, Perm, PermissionDeniedError } from \"../permission-gate.js\";\nimport type { ServiceRegistry } from \"../service-registry.js\";\nimport { jsonV2Fail, type ModuleAuth } from \"./_shared.js\";\n\nexport interface ServiceRoutesDeps {\n serviceRegistry: ServiceRegistry;\n enabled: Map<string, import(\"../manifest-loader.js\").ModuleManifestV2>;\n json?: typeof defaultJson;\n}\n\nexport function createServiceRoutes(depsIn: Partial<ServiceRoutesDeps>) {\n const deps = depsIn as Partial<ServiceRoutesDeps>;\n if (!deps.serviceRegistry || !deps.enabled) {\n throw new Error(\"createServiceRoutes: \u7F3A\u5C11 serviceRegistry / enabled map\");\n }\n const json = deps.json || defaultJson;\n\n return async (ctx: {\n path: string;\n method: Method | string;\n params: URLSearchParams;\n req: IncomingMessage;\n res: ServerResponse;\n moduleAuth?: ModuleAuth;\n }): Promise<boolean> => {\n const { path, method, params, res } = ctx;\n if (!path.startsWith(\"/api/sfmc/services\")) return false;\n if (method !== \"GET\") return false;\n\n const auth = ctx.moduleAuth ?? null;\n if (!auth) {\n
|
|
5
|
-
"mappings": "AAUA,SAAS,QAAQ,mBAAgC;AACjD,SAAS,wBAAwB,MAAM,6BAA6B;AAEpE,SAAS,kBAAmC;
|
|
4
|
+
"sourcesContent": ["/**\n * routes/service-routes.ts \u2014 /api/sfmc/services/* \u5904\u7406\u5668\n *\n * \u7AEF\u70B9:\n * GET /api/sfmc/services \u2192 { services: [{name, moduleId}] }\n * GET /api/sfmc/services/:name?input=<urlencoded json>\n * \u2192 { ok: true, result } | { ok: false, error, code } (+ HTTP status)\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { json as defaultJson, type Method } from \"../lib/http.js\";\nimport { assertModulePermission, Perm, PermissionDeniedError } from \"../permission-gate.js\";\nimport type { ServiceRegistry } from \"../service-registry.js\";\nimport { jsonV2Fail, type ModuleAuth } from \"./_shared.js\";\n\nexport interface ServiceRoutesDeps {\n serviceRegistry: ServiceRegistry;\n enabled: Map<string, import(\"../manifest-loader.js\").ModuleManifestV2>;\n json?: typeof defaultJson;\n}\n\n/**\n * \u521B\u5EFA `/api/sfmc/services/*` \u8DE8\u6A21\u5757\u670D\u52A1\u8DEF\u7531\u8BF7\u6C42\u5904\u7406\u5668\u3002\n *\n * @param depsIn \u4F9D\u8D56\u6CE8\u5165\u9879\uFF08\u5305\u542B serviceRegistry\u3001enabled \u6A21\u5757\u5B57\u5178\u7B49\uFF09\u3002\n * @returns \u5F02\u6B65\u8DEF\u7531\u5904\u7406\u51FD\u6570\u3002\n */\nexport function createServiceRoutes(depsIn: Partial<ServiceRoutesDeps>) {\n const deps = depsIn as Partial<ServiceRoutesDeps>;\n if (!deps.serviceRegistry || !deps.enabled) {\n throw new Error(\"createServiceRoutes: \u7F3A\u5C11 serviceRegistry / enabled map\");\n }\n const json = deps.json || defaultJson;\n\n return async (ctx: {\n path: string;\n method: Method | string;\n params: URLSearchParams;\n req: IncomingMessage;\n res: ServerResponse;\n moduleAuth?: ModuleAuth;\n }): Promise<boolean> => {\n const { path, method, params, res } = ctx;\n if (!path.startsWith(\"/api/sfmc/services\")) return false;\n if (method !== \"GET\") return false;\n\n const auth = ctx.moduleAuth ?? null;\n if (!auth) {\n jsonV2Fail(res, \"unauthorized: module identity missing\", 401, \"unauthorized\");\n return true;\n }\n\n if (path === \"/api/sfmc/services\") {\n json(res, { services: deps.serviceRegistry!.list() });\n return true;\n }\n\n const m = path.match(/^\\/api\\/sfmc\\/services\\/([A-Za-z0-9_.]+)$/);\n if (m && m[1]) {\n const name = m[1];\n const rawInput = params.get(\"input\");\n let payload: unknown = {};\n if (rawInput) {\n try {\n payload = JSON.parse(rawInput);\n } catch (e) {\n jsonV2Fail(res, `invalid input json: ${(e as Error).message}`, 400, \"bad_request\");\n return true;\n }\n }\n try {\n // \u4E0E tx-runner \u4FDD\u6301\u5951\u7EA6\u4E00\u81F4\uFF1AHTTP GET \u4E0E tx.call \u5747\u987B\u58F0\u660E\u5728 services.requires \u4E2D\u4E14\u5177\u5907\u5BF9\u5E94\u6743\u9650\n assertModulePermission(auth.id, auth.permissions, Perm.service(name));\n const out = await deps.serviceRegistry!.dispatch(\n deps.enabled!,\n auth.id,\n name,\n payload\n );\n\n json(res, { ok: true, result: out.result });\n } catch (e) {\n if (e instanceof PermissionDeniedError) {\n jsonV2Fail(res, e.message, 403, \"permission_denied\");\n return true;\n }\n const err = e as { status?: number; code?: string; message: string };\n jsonV2Fail(res, err.message, err.status ?? 500, err.code ?? \"internal\");\n }\n return true;\n }\n\n return false;\n };\n}\n"],
|
|
5
|
+
"mappings": "AAUA,SAAS,QAAQ,mBAAgC;AACjD,SAAS,wBAAwB,MAAM,6BAA6B;AAEpE,SAAS,kBAAmC;AAcrC,SAAS,oBAAoB,QAAoC;AACtE,QAAM,OAAO;AACb,MAAI,CAAC,KAAK,mBAAmB,CAAC,KAAK,SAAS;AAC1C,UAAM,IAAI,MAAM,iEAAuD;AAAA,EACzE;AACA,QAAM,OAAO,KAAK,QAAQ;AAE1B,SAAO,OAAO,QAOU;AACtB,UAAM,EAAE,MAAM,QAAQ,QAAQ,IAAI,IAAI;AACtC,QAAI,CAAC,KAAK,WAAW,oBAAoB,EAAG,QAAO;AACnD,QAAI,WAAW,MAAO,QAAO;AAE7B,UAAM,OAAO,IAAI,cAAc;AAC/B,QAAI,CAAC,MAAM;AACT,iBAAW,KAAK,yCAAyC,KAAK,cAAc;AAC5E,aAAO;AAAA,IACT;AAEA,QAAI,SAAS,sBAAsB;AACjC,WAAK,KAAK,EAAE,UAAU,KAAK,gBAAiB,KAAK,EAAE,CAAC;AACpD,aAAO;AAAA,IACT;AAEA,UAAM,IAAI,KAAK,MAAM,2CAA2C;AAChE,QAAI,KAAK,EAAE,CAAC,GAAG;AACb,YAAM,OAAO,EAAE,CAAC;AAChB,YAAM,WAAW,OAAO,IAAI,OAAO;AACnC,UAAI,UAAmB,CAAC;AACxB,UAAI,UAAU;AACZ,YAAI;AACF,oBAAU,KAAK,MAAM,QAAQ;AAAA,QAC/B,SAAS,GAAG;AACV,qBAAW,KAAK,uBAAwB,EAAY,OAAO,IAAI,KAAK,aAAa;AACjF,iBAAO;AAAA,QACT;AAAA,MACF;AACA,UAAI;AAEF,+BAAuB,KAAK,IAAI,KAAK,aAAa,KAAK,QAAQ,IAAI,CAAC;AACpE,cAAM,MAAM,MAAM,KAAK,gBAAiB;AAAA,UACtC,KAAK;AAAA,UACL,KAAK;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAEA,aAAK,KAAK,EAAE,IAAI,MAAM,QAAQ,IAAI,OAAO,CAAC;AAAA,MAC5C,SAAS,GAAG;AACV,YAAI,aAAa,uBAAuB;AACtC,qBAAW,KAAK,EAAE,SAAS,KAAK,mBAAmB;AACnD,iBAAO;AAAA,QACT;AACA,cAAM,MAAM;AACZ,mBAAW,KAAK,IAAI,SAAS,IAAI,UAAU,KAAK,IAAI,QAAQ,UAAU;AAAA,MACxE;AACA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* routes/status.ts — 只读运维摘要(供 QQ status/online)
|
|
3
|
+
*
|
|
4
|
+
* GET /api/sfmc/status — 公开,无需 Bearer
|
|
5
|
+
*/
|
|
6
|
+
import type { QueryFn } from "../lib/sqlite.js";
|
|
7
|
+
import { type SystemStatusSnapshot } from "../domain/system-status.js";
|
|
8
|
+
import { type RouteFactory } from "./_shared.js";
|
|
9
|
+
/** 玩家行新鲜度阈值:超过则视为离线快照过期 */
|
|
10
|
+
declare const FRESH_MS: number;
|
|
11
|
+
interface Deps {
|
|
12
|
+
query: QueryFn;
|
|
13
|
+
/** 测试可注入,避免真实探活 */
|
|
14
|
+
collectSystem?: (projectRoot: string) => Promise<SystemStatusSnapshot>;
|
|
15
|
+
projectRoot?: string;
|
|
16
|
+
}
|
|
17
|
+
declare function createStatusRoutes({ query, collectSystem, projectRoot }: Deps): ReturnType<RouteFactory>;
|
|
18
|
+
export { createStatusRoutes, FRESH_MS };
|
|
19
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/routes/status.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC5F,OAAO,EAAQ,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEvD,2BAA2B;AAC3B,QAAA,MAAM,QAAQ,QAAgB,CAAC;AAE/B,UAAU,IAAI;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,mBAAmB;IACnB,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,iBAAS,kBAAkB,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,EAAE,IAAI,GAAG,UAAU,CAAC,YAAY,CAAC,CA0EjG;AAED,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { SQL } from "sql-template-strings";
|
|
2
|
+
import { PROJECT_ROOT } from "../project-root.js";
|
|
3
|
+
import { collectSystemStatus } from "../domain/system-status.js";
|
|
4
|
+
import { json } from "./_shared.js";
|
|
5
|
+
const FRESH_MS = 5 * 60 * 1e3;
|
|
6
|
+
function createStatusRoutes({ query, collectSystem, projectRoot }) {
|
|
7
|
+
const root = projectRoot ?? PROJECT_ROOT;
|
|
8
|
+
const collect = collectSystem ?? collectSystemStatus;
|
|
9
|
+
return async function handle({ path, method, res }) {
|
|
10
|
+
if (path !== "/api/sfmc/status") return false;
|
|
11
|
+
if (method !== "GET") {
|
|
12
|
+
json(res, { success: false, error: "not_found" }, 404);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
const now = Date.now();
|
|
16
|
+
let world = null;
|
|
17
|
+
let worldUpdatedAt = null;
|
|
18
|
+
try {
|
|
19
|
+
const worlds = query(SQL`SELECT * FROM sfmc_world LIMIT 1`);
|
|
20
|
+
if (worlds.length > 0 && worlds[0]) {
|
|
21
|
+
world = {
|
|
22
|
+
day: worlds[0].day ?? null,
|
|
23
|
+
difficulty: worlds[0].difficulty ?? null,
|
|
24
|
+
absolute_time: worlds[0].absolute_time ?? null,
|
|
25
|
+
moon_phase: worlds[0].moon_phase ?? null
|
|
26
|
+
};
|
|
27
|
+
worldUpdatedAt = worlds[0].updated_at ?? null;
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
world = null;
|
|
31
|
+
}
|
|
32
|
+
let players = [];
|
|
33
|
+
try {
|
|
34
|
+
const rows = query(
|
|
35
|
+
SQL`SELECT id, name, updated_at FROM sfmc_players ORDER BY updated_at DESC LIMIT 64`
|
|
36
|
+
);
|
|
37
|
+
players = rows.map((r) => ({
|
|
38
|
+
id: String(r.id ?? ""),
|
|
39
|
+
name: String(r.name ?? ""),
|
|
40
|
+
updated_at: Number(r.updated_at) || 0
|
|
41
|
+
}));
|
|
42
|
+
} catch {
|
|
43
|
+
players = [];
|
|
44
|
+
}
|
|
45
|
+
const fresh = players.filter((p) => p.updated_at > 0 && now - p.updated_at <= FRESH_MS);
|
|
46
|
+
const online = fresh.map((p) => ({ id: p.id, name: p.name }));
|
|
47
|
+
const newestPlayer = players[0]?.updated_at ?? 0;
|
|
48
|
+
const updatedAt = Math.max(newestPlayer, typeof worldUpdatedAt === "number" ? worldUpdatedAt : 0) || now;
|
|
49
|
+
let system = null;
|
|
50
|
+
try {
|
|
51
|
+
system = await collect(root);
|
|
52
|
+
} catch {
|
|
53
|
+
system = null;
|
|
54
|
+
}
|
|
55
|
+
json(res, {
|
|
56
|
+
online,
|
|
57
|
+
world,
|
|
58
|
+
updatedAt,
|
|
59
|
+
host: system?.host ?? null,
|
|
60
|
+
processes: system ? {
|
|
61
|
+
db: system.db,
|
|
62
|
+
bds: system.bds
|
|
63
|
+
} : null,
|
|
64
|
+
source: "sfmc_players/sfmc_world+os",
|
|
65
|
+
note: online.length === 0 ? "\u6682\u65E0\u65B0\u9C9C\u5728\u7EBF\u6570\u636E\uFF08\u9700 BDS \u540C\u6B65\u73A9\u5BB6\u8868\uFF0C\u6216\u6570\u636E\u8D85\u8FC7 5 \u5206\u949F\uFF09" : void 0
|
|
66
|
+
});
|
|
67
|
+
return true;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
FRESH_MS,
|
|
72
|
+
createStatusRoutes
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/routes/status.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * routes/status.ts \u2014 \u53EA\u8BFB\u8FD0\u7EF4\u6458\u8981\uFF08\u4F9B QQ status/online\uFF09\n *\n * GET /api/sfmc/status \u2014 \u516C\u5F00\uFF0C\u65E0\u9700 Bearer\n */\n\nimport { SQL } from \"sql-template-strings\";\nimport type { QueryFn } from \"../lib/sqlite.js\";\nimport { PROJECT_ROOT } from \"../project-root.js\";\nimport { collectSystemStatus, type SystemStatusSnapshot } from \"../domain/system-status.js\";\nimport { json, type RouteFactory } from \"./_shared.js\";\n\n/** \u73A9\u5BB6\u884C\u65B0\u9C9C\u5EA6\u9608\u503C\uFF1A\u8D85\u8FC7\u5219\u89C6\u4E3A\u79BB\u7EBF\u5FEB\u7167\u8FC7\u671F */\nconst FRESH_MS = 5 * 60 * 1000;\n\ninterface Deps {\n query: QueryFn;\n /** \u6D4B\u8BD5\u53EF\u6CE8\u5165\uFF0C\u907F\u514D\u771F\u5B9E\u63A2\u6D3B */\n collectSystem?: (projectRoot: string) => Promise<SystemStatusSnapshot>;\n projectRoot?: string;\n}\n\nfunction createStatusRoutes({ query, collectSystem, projectRoot }: Deps): ReturnType<RouteFactory> {\n const root = projectRoot ?? PROJECT_ROOT;\n const collect = collectSystem ?? collectSystemStatus;\n\n return async function handle({ path, method, res }): Promise<boolean> {\n if (path !== \"/api/sfmc/status\") return false;\n if (method !== \"GET\") {\n json(res, { success: false, error: \"not_found\" }, 404);\n return true;\n }\n\n const now = Date.now();\n let world: Record<string, unknown> | null = null;\n let worldUpdatedAt: string | number | null = null;\n try {\n const worlds = query(SQL`SELECT * FROM sfmc_world LIMIT 1`) as Array<Record<string, unknown>>;\n if (worlds.length > 0 && worlds[0]) {\n world = {\n day: worlds[0].day ?? null,\n difficulty: worlds[0].difficulty ?? null,\n absolute_time: worlds[0].absolute_time ?? null,\n moon_phase: worlds[0].moon_phase ?? null,\n };\n worldUpdatedAt = (worlds[0].updated_at as string | number | null) ?? null;\n }\n } catch {\n world = null;\n }\n\n let players: Array<{ id: string; name: string; updated_at: number }> = [];\n try {\n const rows = query(\n SQL`SELECT id, name, updated_at FROM sfmc_players ORDER BY updated_at DESC LIMIT 64`\n ) as Array<{ id: string; name: string; updated_at: number }>;\n players = rows.map((r) => ({\n id: String(r.id ?? \"\"),\n name: String(r.name ?? \"\"),\n updated_at: Number(r.updated_at) || 0,\n }));\n } catch {\n players = [];\n }\n\n const fresh = players.filter((p) => p.updated_at > 0 && now - p.updated_at <= FRESH_MS);\n const online = fresh.map((p) => ({ id: p.id, name: p.name }));\n const newestPlayer = players[0]?.updated_at ?? 0;\n const updatedAt = Math.max(newestPlayer, typeof worldUpdatedAt === \"number\" ? worldUpdatedAt : 0) || now;\n\n let system: SystemStatusSnapshot | null = null;\n try {\n system = await collect(root);\n } catch {\n system = null;\n }\n\n json(res, {\n online,\n world,\n updatedAt,\n host: system?.host ?? null,\n processes: system\n ? {\n db: system.db,\n bds: system.bds,\n }\n : null,\n source: \"sfmc_players/sfmc_world+os\",\n note:\n online.length === 0\n ? \"\u6682\u65E0\u65B0\u9C9C\u5728\u7EBF\u6570\u636E\uFF08\u9700 BDS \u540C\u6B65\u73A9\u5BB6\u8868\uFF0C\u6216\u6570\u636E\u8D85\u8FC7 5 \u5206\u949F\uFF09\"\n : undefined,\n });\n return true;\n };\n}\n\nexport { createStatusRoutes, FRESH_MS };\n"],
|
|
5
|
+
"mappings": "AAMA,SAAS,WAAW;AAEpB,SAAS,oBAAoB;AAC7B,SAAS,2BAAsD;AAC/D,SAAS,YAA+B;AAGxC,MAAM,WAAW,IAAI,KAAK;AAS1B,SAAS,mBAAmB,EAAE,OAAO,eAAe,YAAY,GAAmC;AACjG,QAAM,OAAO,eAAe;AAC5B,QAAM,UAAU,iBAAiB;AAEjC,SAAO,eAAe,OAAO,EAAE,MAAM,QAAQ,IAAI,GAAqB;AACpE,QAAI,SAAS,mBAAoB,QAAO;AACxC,QAAI,WAAW,OAAO;AACpB,WAAK,KAAK,EAAE,SAAS,OAAO,OAAO,YAAY,GAAG,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,QAAwC;AAC5C,QAAI,iBAAyC;AAC7C,QAAI;AACF,YAAM,SAAS,MAAM,qCAAqC;AAC1D,UAAI,OAAO,SAAS,KAAK,OAAO,CAAC,GAAG;AAClC,gBAAQ;AAAA,UACN,KAAK,OAAO,CAAC,EAAE,OAAO;AAAA,UACtB,YAAY,OAAO,CAAC,EAAE,cAAc;AAAA,UACpC,eAAe,OAAO,CAAC,EAAE,iBAAiB;AAAA,UAC1C,YAAY,OAAO,CAAC,EAAE,cAAc;AAAA,QACtC;AACA,yBAAkB,OAAO,CAAC,EAAE,cAAyC;AAAA,MACvE;AAAA,IACF,QAAQ;AACN,cAAQ;AAAA,IACV;AAEA,QAAI,UAAmE,CAAC;AACxE,QAAI;AACF,YAAM,OAAO;AAAA,QACX;AAAA,MACF;AACA,gBAAU,KAAK,IAAI,CAAC,OAAO;AAAA,QACzB,IAAI,OAAO,EAAE,MAAM,EAAE;AAAA,QACrB,MAAM,OAAO,EAAE,QAAQ,EAAE;AAAA,QACzB,YAAY,OAAO,EAAE,UAAU,KAAK;AAAA,MACtC,EAAE;AAAA,IACJ,QAAQ;AACN,gBAAU,CAAC;AAAA,IACb;AAEA,UAAM,QAAQ,QAAQ,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK,MAAM,EAAE,cAAc,QAAQ;AACtF,UAAM,SAAS,MAAM,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,MAAM,EAAE,KAAK,EAAE;AAC5D,UAAM,eAAe,QAAQ,CAAC,GAAG,cAAc;AAC/C,UAAM,YAAY,KAAK,IAAI,cAAc,OAAO,mBAAmB,WAAW,iBAAiB,CAAC,KAAK;AAErG,QAAI,SAAsC;AAC1C,QAAI;AACF,eAAS,MAAM,QAAQ,IAAI;AAAA,IAC7B,QAAQ;AACN,eAAS;AAAA,IACX;AAEA,SAAK,KAAK;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM,QAAQ,QAAQ;AAAA,MACtB,WAAW,SACP;AAAA,QACE,IAAI,OAAO;AAAA,QACX,KAAK,OAAO;AAAA,MACd,IACA;AAAA,MACJ,QAAQ;AAAA,MACR,MACE,OAAO,WAAW,IACd,6JACA;AAAA,IACR,CAAC;AACD,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/runtime.test.js
CHANGED
|
@@ -107,9 +107,7 @@ test("jsonV2Ok: \u4E0E Fail \u5BF9\u79F0 ok \u65B9\u8A00(LSP/DRY)", async () =>
|
|
|
107
107
|
test("normalizeOrderBy: SDK field \u4E0E\u9057\u7559 col / \u6570\u7EC4\u4E92\u901A(LSP)", async () => {
|
|
108
108
|
const { normalizeOrderBy } = await import("./lib/order-by.js");
|
|
109
109
|
deepEqual(normalizeOrderBy(void 0), []);
|
|
110
|
-
deepEqual(normalizeOrderBy({ field: "created_at", dir: "desc" }), [
|
|
111
|
-
{ col: "created_at", dir: "desc" }
|
|
112
|
-
]);
|
|
110
|
+
deepEqual(normalizeOrderBy({ field: "created_at", dir: "desc" }), [{ col: "created_at", dir: "desc" }]);
|
|
113
111
|
deepEqual(normalizeOrderBy({ col: "id" }), [{ col: "id", dir: "asc" }]);
|
|
114
112
|
deepEqual(normalizeOrderBy([{ field: "a" }, { col: "b", dir: "desc" }]), [
|
|
115
113
|
{ col: "a", dir: "asc" },
|
|
@@ -236,7 +234,7 @@ test("TxRunner \u4EA4\u4E92\u4F1A\u8BDD: step \u4E2D\u9014\u8BFB\u56DE insert/ge
|
|
|
236
234
|
serviceRegistry: new ServiceRegistry(),
|
|
237
235
|
enabled
|
|
238
236
|
});
|
|
239
|
-
const begin = runner.beginSession("feature-demo");
|
|
237
|
+
const begin = await runner.beginSession("feature-demo");
|
|
240
238
|
equal(begin.ok, true);
|
|
241
239
|
if (!begin.ok) return;
|
|
242
240
|
const { txId } = begin;
|
|
@@ -262,4 +260,218 @@ test("TxRunner \u4EA4\u4E92\u4F1A\u8BDD: step \u4E2D\u9014\u8BFB\u56DE insert/ge
|
|
|
262
260
|
equal(rows[0]?.name, "alpha");
|
|
263
261
|
db.close();
|
|
264
262
|
});
|
|
263
|
+
test("TxRunner \u4EA4\u4E92\u4F1A\u8BDD: \u5E76\u53D1 beginSession \u81EA\u52A8\u6392\u961F\u4E92\u65A5\u6267\u884C", async () => {
|
|
264
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
265
|
+
const { createQuery } = await import("./lib/sqlite.js");
|
|
266
|
+
const { SchemaRegistry } = await import("./schema-registry.js");
|
|
267
|
+
const { TxRunner } = await import("./tx-runner.js");
|
|
268
|
+
const db = new DatabaseSync(":memory:");
|
|
269
|
+
const query = createQuery(db);
|
|
270
|
+
const schema = new SchemaRegistry(db);
|
|
271
|
+
schema.define("feature-demo", {
|
|
272
|
+
name: "queue_items",
|
|
273
|
+
columns: {
|
|
274
|
+
id: { type: "text", primary: true },
|
|
275
|
+
val: { type: "text", notNull: true }
|
|
276
|
+
},
|
|
277
|
+
softDelete: false
|
|
278
|
+
});
|
|
279
|
+
const enabled = /* @__PURE__ */ new Map([
|
|
280
|
+
[
|
|
281
|
+
"feature-demo",
|
|
282
|
+
{
|
|
283
|
+
id: "feature-demo",
|
|
284
|
+
version: "1.0.0",
|
|
285
|
+
permissions: ["db:read:queue_items", "db:write:queue_items"],
|
|
286
|
+
services: { provides: [], requires: [] },
|
|
287
|
+
db: { tables: [] },
|
|
288
|
+
config: { key: "demo" }
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
]);
|
|
292
|
+
const runner = new TxRunner({
|
|
293
|
+
db,
|
|
294
|
+
query,
|
|
295
|
+
schema,
|
|
296
|
+
serviceRegistry: new ServiceRegistry(),
|
|
297
|
+
enabled
|
|
298
|
+
});
|
|
299
|
+
const s1 = await runner.beginSession("feature-demo");
|
|
300
|
+
equal(s1.ok, true);
|
|
301
|
+
if (!s1.ok) return;
|
|
302
|
+
let s2Started = false;
|
|
303
|
+
const s2Promise = runner.beginSession("feature-demo").then((res) => {
|
|
304
|
+
s2Started = true;
|
|
305
|
+
return res;
|
|
306
|
+
});
|
|
307
|
+
equal(s2Started, false);
|
|
308
|
+
const s1Step = await runner.stepSession(s1.txId, "feature-demo", {
|
|
309
|
+
op: "insert",
|
|
310
|
+
table: "queue_items",
|
|
311
|
+
row: { id: "1", val: "one" }
|
|
312
|
+
});
|
|
313
|
+
equal(s1Step.ok, true);
|
|
314
|
+
const s1Commit = runner.commitSession(s1.txId, "feature-demo");
|
|
315
|
+
equal(s1Commit.ok, true);
|
|
316
|
+
const s2 = await s2Promise;
|
|
317
|
+
equal(s2.ok, true);
|
|
318
|
+
equal(s2Started, true);
|
|
319
|
+
if (!s2.ok) return;
|
|
320
|
+
const s2Step = await runner.stepSession(s2.txId, "feature-demo", {
|
|
321
|
+
op: "insert",
|
|
322
|
+
table: "queue_items",
|
|
323
|
+
row: { id: "2", val: "two" }
|
|
324
|
+
});
|
|
325
|
+
equal(s2Step.ok, true);
|
|
326
|
+
const s2Commit = runner.commitSession(s2.txId, "feature-demo");
|
|
327
|
+
equal(s2Commit.ok, true);
|
|
328
|
+
const rows = db.prepare("SELECT val FROM queue_items ORDER BY id ASC").all();
|
|
329
|
+
equal(rows.length, 2);
|
|
330
|
+
equal(rows[0]?.val, "one");
|
|
331
|
+
equal(rows[1]?.val, "two");
|
|
332
|
+
db.close();
|
|
333
|
+
});
|
|
334
|
+
test("TxRunner \u5355\u8FDE\u63A5: run \u4E0E\u4EA4\u4E92\u4F1A\u8BDD\u5171\u7528\u4E8B\u52A1\u961F\u5217", async () => {
|
|
335
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
336
|
+
const { createQuery } = await import("./lib/sqlite.js");
|
|
337
|
+
const { SchemaRegistry } = await import("./schema-registry.js");
|
|
338
|
+
const { TxRunner } = await import("./tx-runner.js");
|
|
339
|
+
const db = new DatabaseSync(":memory:");
|
|
340
|
+
const query = createQuery(db);
|
|
341
|
+
const schema = new SchemaRegistry(db);
|
|
342
|
+
schema.define("feature-demo", {
|
|
343
|
+
name: "queue_items",
|
|
344
|
+
columns: {
|
|
345
|
+
id: { type: "text", primary: true },
|
|
346
|
+
val: { type: "text", notNull: true }
|
|
347
|
+
},
|
|
348
|
+
softDelete: false
|
|
349
|
+
});
|
|
350
|
+
const enabled = /* @__PURE__ */ new Map([
|
|
351
|
+
[
|
|
352
|
+
"feature-demo",
|
|
353
|
+
{
|
|
354
|
+
id: "feature-demo",
|
|
355
|
+
version: "1.0.0",
|
|
356
|
+
permissions: ["db:read:queue_items", "db:write:queue_items"],
|
|
357
|
+
services: { provides: [], requires: [] },
|
|
358
|
+
db: { tables: [] },
|
|
359
|
+
config: { key: "demo" }
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
]);
|
|
363
|
+
const runner = new TxRunner({
|
|
364
|
+
db,
|
|
365
|
+
query,
|
|
366
|
+
schema,
|
|
367
|
+
serviceRegistry: new ServiceRegistry(),
|
|
368
|
+
enabled
|
|
369
|
+
});
|
|
370
|
+
const session = await runner.beginSession("feature-demo");
|
|
371
|
+
equal(session.ok, true);
|
|
372
|
+
if (!session.ok) return;
|
|
373
|
+
let runFinished = false;
|
|
374
|
+
const queuedRun = runner.run({
|
|
375
|
+
moduleId: "feature-demo",
|
|
376
|
+
steps: [{ op: "query", table: "queue_items" }]
|
|
377
|
+
}).then((result) => {
|
|
378
|
+
runFinished = true;
|
|
379
|
+
return result;
|
|
380
|
+
});
|
|
381
|
+
equal(runFinished, false);
|
|
382
|
+
equal(runner.commitSession(session.txId, "feature-demo").ok, true);
|
|
383
|
+
equal((await queuedRun).ok, true);
|
|
384
|
+
equal(runFinished, true);
|
|
385
|
+
const running = runner.run({
|
|
386
|
+
moduleId: "feature-demo",
|
|
387
|
+
steps: [{ op: "query", table: "queue_items" }]
|
|
388
|
+
});
|
|
389
|
+
let sessionStarted = false;
|
|
390
|
+
const queuedSession = runner.beginSession("feature-demo").then((result) => {
|
|
391
|
+
sessionStarted = true;
|
|
392
|
+
return result;
|
|
393
|
+
});
|
|
394
|
+
equal(sessionStarted, false);
|
|
395
|
+
equal((await running).ok, true);
|
|
396
|
+
const nextSession = await queuedSession;
|
|
397
|
+
equal(nextSession.ok, true);
|
|
398
|
+
equal(sessionStarted, true);
|
|
399
|
+
if (nextSession.ok) equal(runner.rollbackSession(nextSession.txId, "feature-demo").ok, true);
|
|
400
|
+
db.close();
|
|
401
|
+
});
|
|
402
|
+
test("TxRunner \u4F1A\u56DE\u6536\u7A7A\u95F2\u4EA4\u4E92\u4E8B\u52A1\u5E76\u91CA\u653E\u7B49\u5F85\u8005", async () => {
|
|
403
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
404
|
+
const { createQuery } = await import("./lib/sqlite.js");
|
|
405
|
+
const { SchemaRegistry } = await import("./schema-registry.js");
|
|
406
|
+
const { TxRunner } = await import("./tx-runner.js");
|
|
407
|
+
const db = new DatabaseSync(":memory:");
|
|
408
|
+
const schema = new SchemaRegistry(db);
|
|
409
|
+
const enabled = /* @__PURE__ */ new Map([
|
|
410
|
+
[
|
|
411
|
+
"feature-demo",
|
|
412
|
+
{
|
|
413
|
+
id: "feature-demo",
|
|
414
|
+
version: "1.0.0",
|
|
415
|
+
permissions: [],
|
|
416
|
+
services: { provides: [], requires: [] },
|
|
417
|
+
db: { tables: [] },
|
|
418
|
+
config: { key: "demo" }
|
|
419
|
+
}
|
|
420
|
+
]
|
|
421
|
+
]);
|
|
422
|
+
const runner = new TxRunner(
|
|
423
|
+
{
|
|
424
|
+
db,
|
|
425
|
+
query: createQuery(db),
|
|
426
|
+
schema,
|
|
427
|
+
serviceRegistry: new ServiceRegistry(),
|
|
428
|
+
enabled
|
|
429
|
+
},
|
|
430
|
+
{ sessionIdleTimeoutMs: 20, slotWaitTimeoutMs: 200 }
|
|
431
|
+
);
|
|
432
|
+
const orphan = await runner.beginSession("feature-demo");
|
|
433
|
+
equal(orphan.ok, true);
|
|
434
|
+
const recovered = await runner.beginSession("feature-demo");
|
|
435
|
+
equal(recovered.ok, true);
|
|
436
|
+
if (recovered.ok) equal(runner.rollbackSession(recovered.txId, "feature-demo").ok, true);
|
|
437
|
+
db.close();
|
|
438
|
+
});
|
|
439
|
+
test("TxRunner \u4E8B\u52A1\u69FD\u7B49\u5F85\u8D85\u65F6\u8FD4\u56DE transaction_busy", async () => {
|
|
440
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
441
|
+
const { createQuery } = await import("./lib/sqlite.js");
|
|
442
|
+
const { SchemaRegistry } = await import("./schema-registry.js");
|
|
443
|
+
const { TxRunner } = await import("./tx-runner.js");
|
|
444
|
+
const db = new DatabaseSync(":memory:");
|
|
445
|
+
const schema = new SchemaRegistry(db);
|
|
446
|
+
const enabled = /* @__PURE__ */ new Map([
|
|
447
|
+
[
|
|
448
|
+
"feature-demo",
|
|
449
|
+
{
|
|
450
|
+
id: "feature-demo",
|
|
451
|
+
version: "1.0.0",
|
|
452
|
+
permissions: [],
|
|
453
|
+
services: { provides: [], requires: [] },
|
|
454
|
+
db: { tables: [] },
|
|
455
|
+
config: { key: "demo" }
|
|
456
|
+
}
|
|
457
|
+
]
|
|
458
|
+
]);
|
|
459
|
+
const runner = new TxRunner(
|
|
460
|
+
{
|
|
461
|
+
db,
|
|
462
|
+
query: createQuery(db),
|
|
463
|
+
schema,
|
|
464
|
+
serviceRegistry: new ServiceRegistry(),
|
|
465
|
+
enabled
|
|
466
|
+
},
|
|
467
|
+
{ sessionIdleTimeoutMs: 200, slotWaitTimeoutMs: 20 }
|
|
468
|
+
);
|
|
469
|
+
const owner = await runner.beginSession("feature-demo");
|
|
470
|
+
equal(owner.ok, true);
|
|
471
|
+
const blocked = await runner.beginSession("feature-demo");
|
|
472
|
+
equal(blocked.ok, false);
|
|
473
|
+
if (!blocked.ok) equal(blocked.code, "transaction_busy");
|
|
474
|
+
if (owner.ok) equal(runner.rollbackSession(owner.txId, "feature-demo").ok, true);
|
|
475
|
+
db.close();
|
|
476
|
+
});
|
|
265
477
|
//# sourceMappingURL=runtime.test.js.map
|
package/dist/runtime.test.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/runtime.test.ts"],
|
|
4
|
-
"sourcesContent": ["import { deepEqual, equal, rejects, throws } from \"node:assert/strict\";\nimport test from \"node:test\";\nimport { SQL } from \"sql-template-strings\";\nimport { assertIdentifier, quoteIdentifier } from \"./lib/identifiers.js\";\nimport { parseNodeVersion } from \"./lib/runtime.js\";\nimport { raw, sql } from \"./lib/sql-helpers.js\";\nimport { DispatchError, ServiceRegistry } from \"./service-registry.js\";\n\ntest(\"parses Node versions\", () => {\n deepEqual(parseNodeVersion(\"22.5.1\"), { major: 22, minor: 5, patch: 1 });\n deepEqual(parseNodeVersion(\"v22.5.1\"), { major: 22, minor: 5, patch: 1 });\n});\n\ntest(\"accepts only safe SQL identifiers\", () => {\n equal(quoteIdentifier(\"sfmc_players\", \"table\"), '\"sfmc_players\"');\n throws(() => assertIdentifier(\"users; DROP TABLE x\"), /Invalid SQL/);\n});\n\ntest(\"sql() embeds trusted identifiers and binds values\", () => {\n const TABLE = \"sfmc_economy_accounts\";\n const q = sql(`SELECT * FROM ${TABLE} WHERE player_id = ?`, [\"p1\"]);\n equal(q.sql, \"SELECT * FROM sfmc_economy_accounts WHERE player_id = ?\");\n deepEqual(q.values, [\"p1\"]);\n});\n\ntest(\"raw() only works via append \u2014 template interpolation still becomes ?\", () => {\n const TABLE = \"sfmc_economy_accounts\";\n const broken = SQL`SELECT * FROM ${raw(TABLE)} WHERE id = ${\"p1\"}`;\n equal(broken.sql, \"SELECT * FROM ? WHERE id = ?\");\n\n const ok = SQL`SELECT * FROM `.append(raw(TABLE)).append(SQL` WHERE id = ${\"p1\"}`);\n equal(ok.sql, \"SELECT * FROM sfmc_economy_accounts WHERE id = ?\");\n deepEqual(ok.values, [\"p1\"]);\n});\n\ntest(\"ServiceRegistry: provider self-call skips requires (LSP with tx.call path)\", async () => {\n const reg = new ServiceRegistry();\n reg.registerHandler(\"feature-economy\", \"economy.stats.monthly\", async () => ({ ok: true }));\n\n const enabled = new Map([\n [\n \"feature-economy\",\n {\n id: \"feature-economy\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [{ name: \"economy.stats.monthly\" }], requires: [] as Array<{ name: string }> },\n db: { tables: [] as unknown[] },\n config: { key: \"economy\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const out = await reg.dispatch(enabled, \"feature-economy\", \"economy.stats.monthly\", {});\n deepEqual(out, { ok: true, result: { ok: true } });\n});\n\ntest(\"ServiceRegistry: missing service \u2192 no_such_service (not not_in_requires)\", async () => {\n const reg = new ServiceRegistry();\n const enabled = new Map([\n [\n \"feature-chat\",\n {\n id: \"feature-chat\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"chat\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n await rejects(\n () => reg.dispatch(enabled, \"feature-chat\", \"economy.account.get\", {}),\n (e: unknown) => e instanceof DispatchError && e.code === \"no_such_service\"\n );\n});\n\ntest(\"jsonV2Fail: ok \u65B9\u8A00 + extra(step) \u5408\u5E76(LSP/DRY)\", async () => {\n const { jsonV2Fail } = await import(\"./routes/_shared.js\");\n const chunks: Buffer[] = [];\n let statusCode = 0;\n const res = {\n writeHead(code: number) {\n statusCode = code;\n },\n end(body?: string) {\n if (body) chunks.push(Buffer.from(body));\n },\n setHeader() {},\n } as unknown as import(\"node:http\").ServerResponse;\n\n jsonV2Fail(res, \"boom\", 400, \"bad_step\", { step: 2 });\n const payload = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n equal(statusCode, 400);\n deepEqual(payload, { ok: false, error: \"boom\", code: \"bad_step\", step: 2 });\n});\n\ntest(\"jsonV2Ok: \u4E0E Fail \u5BF9\u79F0 ok \u65B9\u8A00(LSP/DRY)\", async () => {\n const { jsonV2Ok } = await import(\"./routes/_shared.js\");\n const chunks: Buffer[] = [];\n let statusCode = 0;\n const res = {\n writeHead(code: number) {\n statusCode = code;\n },\n end(body?: string) {\n if (body) chunks.push(Buffer.from(body));\n },\n setHeader() {},\n } as unknown as import(\"node:http\").ServerResponse;\n\n jsonV2Ok(res, { rows: [1] });\n const payload = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n equal(statusCode, 200);\n deepEqual(payload, { ok: true, rows: [1] });\n});\n\ntest(\"normalizeOrderBy: SDK field \u4E0E\u9057\u7559 col / \u6570\u7EC4\u4E92\u901A(LSP)\", async () => {\n const { normalizeOrderBy } = await import(\"./lib/order-by.js\");\n deepEqual(normalizeOrderBy(undefined), []);\n deepEqual(normalizeOrderBy({ field: \"created_at\", dir: \"desc\" }), [\n { col: \"created_at\", dir: \"desc\" },\n ]);\n deepEqual(normalizeOrderBy({ col: \"id\" }), [{ col: \"id\", dir: \"asc\" }]);\n deepEqual(normalizeOrderBy([{ field: \"a\" }, { col: \"b\", dir: \"desc\" }]), [\n { col: \"a\", dir: \"asc\" },\n { col: \"b\", dir: \"desc\" },\n ]);\n throws(() => normalizeOrderBy({ dir: \"asc\" }), /field\\/col/);\n});\n\ntest(\"syncModuleRuntimeState: enable/disable \u70ED\u66F4\u65B0 token+enabledSet(DIP)\", async () => {\n const { mkdtempSync, rmSync, readFileSync } = await import(\"node:fs\");\n const { join } = await import(\"node:path\");\n const { tmpdir } = await import(\"node:os\");\n const { syncModuleRuntimeState } = await import(\"./module-runtime-sync.js\");\n const { deriveToken } = await import(\"./module-auth.js\");\n const { unregisterBuiltinPluginForModule } = await import(\"./services/builtin-handlers.js\");\n\n const root = mkdtempSync(join(tmpdir(), \"sfmc-runtime-sync-\"));\n const dbPath = join(root, \"data\", \"sfmc_data.db\");\n try {\n const enabledSet = new Set<string>([\"feature-a\"]);\n const enabledManifests = new Map();\n const moduleAuth = { tokens: { \"feature-a\": \"old\" } as Record<string, string>, secret: \"test-secret\" };\n const registry = new ServiceRegistry();\n const fakeManifest = {\n id: \"feature-b\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"b\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2;\n\n syncModuleRuntimeState({\n moduleId: \"feature-b\",\n enabled: true,\n dbPath,\n envAuthToken: \"fixed-auth\",\n enabledSet,\n enabledManifests,\n loadedManifest: { modules: { \"feature-b\": fakeManifest } },\n moduleAuth,\n serviceRegistry: registry,\n builtinDeps: { query: (() => []) as never, db: {} as never },\n });\n\n equal(enabledSet.has(\"feature-b\"), true);\n equal(enabledManifests.has(\"feature-b\"), true);\n equal(moduleAuth.tokens[\"feature-b\"], deriveToken(\"feature-b\", \"test-secret\"));\n const store = JSON.parse(readFileSync(join(root, \"data\", \"module-tokens.json\"), \"utf8\"));\n equal(store.tokens[\"feature-b\"], moduleAuth.tokens[\"feature-b\"]);\n\n syncModuleRuntimeState({\n moduleId: \"feature-b\",\n enabled: false,\n dbPath,\n envAuthToken: \"fixed-auth\",\n enabledSet,\n enabledManifests,\n loadedManifest: { modules: { \"feature-b\": fakeManifest } },\n moduleAuth,\n serviceRegistry: registry,\n builtinDeps: { query: (() => []) as never, db: {} as never },\n });\n\n equal(enabledSet.has(\"feature-b\"), false);\n equal(enabledManifests.has(\"feature-b\"), false);\n equal(moduleAuth.tokens[\"feature-b\"], undefined);\n equal(unregisterBuiltinPluginForModule(registry, \"feature-b\"), 0);\n } finally {\n rmSync(root, { recursive: true, force: true });\n }\n});\n\ntest(\"module-auth: ensureModuleToken / revoke \u590D\u7528 secret(DRY)\", async () => {\n const { deriveToken, ensureModuleToken, revokeModuleToken } = await import(\"./module-auth.js\");\n const secret = \"test-secret\";\n const auth = { secret, tokens: {} as Record<string, string> };\n equal(ensureModuleToken(auth, \"feature-afk\"), true);\n equal(auth.tokens[\"feature-afk\"], deriveToken(\"feature-afk\", secret));\n equal(ensureModuleToken(auth, \"feature-afk\"), false);\n equal(revokeModuleToken(auth, \"feature-afk\"), true);\n equal(auth.tokens[\"feature-afk\"], undefined);\n equal(revokeModuleToken(auth, \"feature-afk\"), false);\n});\n\ntest(\"builtin-handlers: \u70ED\u7981\u7528\u6309 moduleId \u5378\u8F7D(DRY/OCP)\", async () => {\n const { unregisterBuiltinPluginForModule, registerBuiltinPluginForModule } = await import(\n \"./services/builtin-handlers.js\"\n );\n\n const reg = new ServiceRegistry();\n reg.registerHandler(\"feature-economy\", \"economy.account.get\", async () => ({}));\n reg.registerHandler(\"feature-economy\", \"economy.account.debit\", async () => ({}));\n reg.registerHandler(\"other-mod\", \"other.ping\", async () => ({}));\n equal(unregisterBuiltinPluginForModule(reg, \"feature-economy\"), 2);\n equal(reg.list().length, 1);\n equal(reg.list()[0]?.moduleId, \"other-mod\");\n equal(unregisterBuiltinPluginForModule(reg, \"feature-unknown\"), 0);\n\n // \u5DF2\u6709\u540C moduleId handler \u65F6\u70ED\u542F\u7528\u8DF3\u8FC7\n equal(registerBuiltinPluginForModule(reg, { query: (() => []) as never, db: {} as never }, \"feature-economy\"), true);\n equal(registerBuiltinPluginForModule(reg, { query: (() => []) as never, db: {} as never }, \"feature-economy\"), false);\n});\n\ntest(\"TxRunner \u4EA4\u4E92\u4F1A\u8BDD: step \u4E2D\u9014\u8BFB\u56DE insert/get(PR #31 leftover)\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const query = createQuery(db);\n const schema = new SchemaRegistry(db);\n schema.define(\"feature-demo\", {\n name: \"demo_items\",\n columns: {\n id: { type: \"text\", primary: true },\n name: { type: \"text\", notNull: true },\n },\n softDelete: false,\n });\n\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [\"db:read:demo_items\", \"db:write:demo_items\"],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const runner = new TxRunner({\n db,\n query,\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n });\n\n const begin = runner.beginSession(\"feature-demo\");\n equal(begin.ok, true);\n if (!begin.ok) return;\n const { txId } = begin;\n\n const ins = await runner.stepSession(txId, \"feature-demo\", {\n op: \"insert\",\n table: \"demo_items\",\n row: { id: \"a1\", name: \"alpha\" },\n });\n equal(ins.ok, true);\n if (!ins.ok) return;\n equal((ins.result as { op: string; row: { id: string } }).row.id, \"a1\");\n\n const got = await runner.stepSession(txId, \"feature-demo\", {\n op: \"get\",\n table: \"demo_items\",\n id: \"a1\",\n });\n equal(got.ok, true);\n if (!got.ok) return;\n equal((got.result as { row: { name: string } | null }).row?.name, \"alpha\");\n\n const committed = runner.commitSession(txId, \"feature-demo\");\n equal(committed.ok, true);\n\n const rows = db.prepare(\"SELECT name FROM demo_items WHERE id = ?\").all(\"a1\") as Array<{ name: string }>;\n equal(rows[0]?.name, \"alpha\");\n db.close();\n});\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { deepEqual, equal, rejects, throws } from \"node:assert/strict\";\nimport test from \"node:test\";\nimport { SQL } from \"sql-template-strings\";\nimport { assertIdentifier, quoteIdentifier } from \"./lib/identifiers.js\";\nimport { parseNodeVersion } from \"./lib/runtime.js\";\nimport { raw, sql } from \"./lib/sql-helpers.js\";\nimport { DispatchError, ServiceRegistry } from \"./service-registry.js\";\n\ntest(\"parses Node versions\", () => {\n deepEqual(parseNodeVersion(\"22.5.1\"), { major: 22, minor: 5, patch: 1 });\n deepEqual(parseNodeVersion(\"v22.5.1\"), { major: 22, minor: 5, patch: 1 });\n});\n\ntest(\"accepts only safe SQL identifiers\", () => {\n equal(quoteIdentifier(\"sfmc_players\", \"table\"), '\"sfmc_players\"');\n throws(() => assertIdentifier(\"users; DROP TABLE x\"), /Invalid SQL/);\n});\n\ntest(\"sql() embeds trusted identifiers and binds values\", () => {\n const TABLE = \"sfmc_economy_accounts\";\n const q = sql(`SELECT * FROM ${TABLE} WHERE player_id = ?`, [\"p1\"]);\n equal(q.sql, \"SELECT * FROM sfmc_economy_accounts WHERE player_id = ?\");\n deepEqual(q.values, [\"p1\"]);\n});\n\ntest(\"raw() only works via append \u2014 template interpolation still becomes ?\", () => {\n const TABLE = \"sfmc_economy_accounts\";\n const broken = SQL`SELECT * FROM ${raw(TABLE)} WHERE id = ${\"p1\"}`;\n equal(broken.sql, \"SELECT * FROM ? WHERE id = ?\");\n\n const ok = SQL`SELECT * FROM `.append(raw(TABLE)).append(SQL` WHERE id = ${\"p1\"}`);\n equal(ok.sql, \"SELECT * FROM sfmc_economy_accounts WHERE id = ?\");\n deepEqual(ok.values, [\"p1\"]);\n});\n\ntest(\"ServiceRegistry: provider self-call skips requires (LSP with tx.call path)\", async () => {\n const reg = new ServiceRegistry();\n reg.registerHandler(\"feature-economy\", \"economy.stats.monthly\", async () => ({ ok: true }));\n\n const enabled = new Map([\n [\n \"feature-economy\",\n {\n id: \"feature-economy\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [{ name: \"economy.stats.monthly\" }], requires: [] as Array<{ name: string }> },\n db: { tables: [] as unknown[] },\n config: { key: \"economy\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const out = await reg.dispatch(enabled, \"feature-economy\", \"economy.stats.monthly\", {});\n deepEqual(out, { ok: true, result: { ok: true } });\n});\n\ntest(\"ServiceRegistry: missing service \u2192 no_such_service (not not_in_requires)\", async () => {\n const reg = new ServiceRegistry();\n const enabled = new Map([\n [\n \"feature-chat\",\n {\n id: \"feature-chat\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"chat\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n await rejects(\n () => reg.dispatch(enabled, \"feature-chat\", \"economy.account.get\", {}),\n (e: unknown) => e instanceof DispatchError && e.code === \"no_such_service\"\n );\n});\n\ntest(\"jsonV2Fail: ok \u65B9\u8A00 + extra(step) \u5408\u5E76(LSP/DRY)\", async () => {\n const { jsonV2Fail } = await import(\"./routes/_shared.js\");\n const chunks: Buffer[] = [];\n let statusCode = 0;\n const res = {\n writeHead(code: number) {\n statusCode = code;\n },\n end(body?: string) {\n if (body) chunks.push(Buffer.from(body));\n },\n setHeader() {},\n } as unknown as import(\"node:http\").ServerResponse;\n\n jsonV2Fail(res, \"boom\", 400, \"bad_step\", { step: 2 });\n const payload = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n equal(statusCode, 400);\n deepEqual(payload, { ok: false, error: \"boom\", code: \"bad_step\", step: 2 });\n});\n\ntest(\"jsonV2Ok: \u4E0E Fail \u5BF9\u79F0 ok \u65B9\u8A00(LSP/DRY)\", async () => {\n const { jsonV2Ok } = await import(\"./routes/_shared.js\");\n const chunks: Buffer[] = [];\n let statusCode = 0;\n const res = {\n writeHead(code: number) {\n statusCode = code;\n },\n end(body?: string) {\n if (body) chunks.push(Buffer.from(body));\n },\n setHeader() {},\n } as unknown as import(\"node:http\").ServerResponse;\n\n jsonV2Ok(res, { rows: [1] });\n const payload = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n equal(statusCode, 200);\n deepEqual(payload, { ok: true, rows: [1] });\n});\n\ntest(\"normalizeOrderBy: SDK field \u4E0E\u9057\u7559 col / \u6570\u7EC4\u4E92\u901A(LSP)\", async () => {\n const { normalizeOrderBy } = await import(\"./lib/order-by.js\");\n deepEqual(normalizeOrderBy(undefined), []);\n deepEqual(normalizeOrderBy({ field: \"created_at\", dir: \"desc\" }), [{ col: \"created_at\", dir: \"desc\" }]);\n deepEqual(normalizeOrderBy({ col: \"id\" }), [{ col: \"id\", dir: \"asc\" }]);\n deepEqual(normalizeOrderBy([{ field: \"a\" }, { col: \"b\", dir: \"desc\" }]), [\n { col: \"a\", dir: \"asc\" },\n { col: \"b\", dir: \"desc\" },\n ]);\n throws(() => normalizeOrderBy({ dir: \"asc\" }), /field\\/col/);\n});\n\ntest(\"syncModuleRuntimeState: enable/disable \u70ED\u66F4\u65B0 token+enabledSet(DIP)\", async () => {\n const { mkdtempSync, rmSync, readFileSync } = await import(\"node:fs\");\n const { join } = await import(\"node:path\");\n const { tmpdir } = await import(\"node:os\");\n const { syncModuleRuntimeState } = await import(\"./module-runtime-sync.js\");\n const { deriveToken } = await import(\"./module-auth.js\");\n const { unregisterBuiltinPluginForModule } = await import(\"./services/builtin-handlers.js\");\n\n const root = mkdtempSync(join(tmpdir(), \"sfmc-runtime-sync-\"));\n const dbPath = join(root, \"data\", \"sfmc_data.db\");\n try {\n const enabledSet = new Set<string>([\"feature-a\"]);\n const enabledManifests = new Map();\n const moduleAuth = { tokens: { \"feature-a\": \"old\" } as Record<string, string>, secret: \"test-secret\" };\n const registry = new ServiceRegistry();\n const fakeManifest = {\n id: \"feature-b\",\n version: \"1.0.0\",\n permissions: [] as string[],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"b\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2;\n\n syncModuleRuntimeState({\n moduleId: \"feature-b\",\n enabled: true,\n dbPath,\n envAuthToken: \"fixed-auth\",\n enabledSet,\n enabledManifests,\n loadedManifest: { modules: { \"feature-b\": fakeManifest } },\n moduleAuth,\n serviceRegistry: registry,\n builtinDeps: { query: (() => []) as never, db: {} as never },\n });\n\n equal(enabledSet.has(\"feature-b\"), true);\n equal(enabledManifests.has(\"feature-b\"), true);\n equal(moduleAuth.tokens[\"feature-b\"], deriveToken(\"feature-b\", \"test-secret\"));\n const store = JSON.parse(readFileSync(join(root, \"data\", \"module-tokens.json\"), \"utf8\"));\n equal(store.tokens[\"feature-b\"], moduleAuth.tokens[\"feature-b\"]);\n\n syncModuleRuntimeState({\n moduleId: \"feature-b\",\n enabled: false,\n dbPath,\n envAuthToken: \"fixed-auth\",\n enabledSet,\n enabledManifests,\n loadedManifest: { modules: { \"feature-b\": fakeManifest } },\n moduleAuth,\n serviceRegistry: registry,\n builtinDeps: { query: (() => []) as never, db: {} as never },\n });\n\n equal(enabledSet.has(\"feature-b\"), false);\n equal(enabledManifests.has(\"feature-b\"), false);\n equal(moduleAuth.tokens[\"feature-b\"], undefined);\n equal(unregisterBuiltinPluginForModule(registry, \"feature-b\"), 0);\n } finally {\n rmSync(root, { recursive: true, force: true });\n }\n});\n\ntest(\"module-auth: ensureModuleToken / revoke \u590D\u7528 secret(DRY)\", async () => {\n const { deriveToken, ensureModuleToken, revokeModuleToken } = await import(\"./module-auth.js\");\n const secret = \"test-secret\";\n const auth = { secret, tokens: {} as Record<string, string> };\n equal(ensureModuleToken(auth, \"feature-afk\"), true);\n equal(auth.tokens[\"feature-afk\"], deriveToken(\"feature-afk\", secret));\n equal(ensureModuleToken(auth, \"feature-afk\"), false);\n equal(revokeModuleToken(auth, \"feature-afk\"), true);\n equal(auth.tokens[\"feature-afk\"], undefined);\n equal(revokeModuleToken(auth, \"feature-afk\"), false);\n});\n\ntest(\"builtin-handlers: \u70ED\u7981\u7528\u6309 moduleId \u5378\u8F7D(DRY/OCP)\", async () => {\n const { unregisterBuiltinPluginForModule, registerBuiltinPluginForModule } =\n await import(\"./services/builtin-handlers.js\");\n\n const reg = new ServiceRegistry();\n reg.registerHandler(\"feature-economy\", \"economy.account.get\", async () => ({}));\n reg.registerHandler(\"feature-economy\", \"economy.account.debit\", async () => ({}));\n reg.registerHandler(\"other-mod\", \"other.ping\", async () => ({}));\n equal(unregisterBuiltinPluginForModule(reg, \"feature-economy\"), 2);\n equal(reg.list().length, 1);\n equal(reg.list()[0]?.moduleId, \"other-mod\");\n equal(unregisterBuiltinPluginForModule(reg, \"feature-unknown\"), 0);\n\n // \u5DF2\u6709\u540C moduleId handler \u65F6\u70ED\u542F\u7528\u8DF3\u8FC7\n equal(registerBuiltinPluginForModule(reg, { query: (() => []) as never, db: {} as never }, \"feature-economy\"), true);\n equal(registerBuiltinPluginForModule(reg, { query: (() => []) as never, db: {} as never }, \"feature-economy\"), false);\n});\n\ntest(\"TxRunner \u4EA4\u4E92\u4F1A\u8BDD: step \u4E2D\u9014\u8BFB\u56DE insert/get(PR #31 leftover)\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const query = createQuery(db);\n const schema = new SchemaRegistry(db);\n schema.define(\"feature-demo\", {\n name: \"demo_items\",\n columns: {\n id: { type: \"text\", primary: true },\n name: { type: \"text\", notNull: true },\n },\n softDelete: false,\n });\n\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [\"db:read:demo_items\", \"db:write:demo_items\"],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const runner = new TxRunner({\n db,\n query,\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n });\n\n const begin = await runner.beginSession(\"feature-demo\");\n equal(begin.ok, true);\n if (!begin.ok) return;\n const { txId } = begin;\n\n const ins = await runner.stepSession(txId, \"feature-demo\", {\n op: \"insert\",\n table: \"demo_items\",\n row: { id: \"a1\", name: \"alpha\" },\n });\n equal(ins.ok, true);\n if (!ins.ok) return;\n equal((ins.result as { op: string; row: { id: string } }).row.id, \"a1\");\n\n const got = await runner.stepSession(txId, \"feature-demo\", {\n op: \"get\",\n table: \"demo_items\",\n id: \"a1\",\n });\n equal(got.ok, true);\n if (!got.ok) return;\n equal((got.result as { row: { name: string } | null }).row?.name, \"alpha\");\n\n const committed = runner.commitSession(txId, \"feature-demo\");\n equal(committed.ok, true);\n\n const rows = db.prepare(\"SELECT name FROM demo_items WHERE id = ?\").all(\"a1\") as Array<{ name: string }>;\n equal(rows[0]?.name, \"alpha\");\n db.close();\n});\n\ntest(\"TxRunner \u4EA4\u4E92\u4F1A\u8BDD: \u5E76\u53D1 beginSession \u81EA\u52A8\u6392\u961F\u4E92\u65A5\u6267\u884C\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const query = createQuery(db);\n const schema = new SchemaRegistry(db);\n schema.define(\"feature-demo\", {\n name: \"queue_items\",\n columns: {\n id: { type: \"text\", primary: true },\n val: { type: \"text\", notNull: true },\n },\n softDelete: false,\n });\n\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [\"db:read:queue_items\", \"db:write:queue_items\"],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const runner = new TxRunner({\n db,\n query,\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n });\n\n // Session 1 \u542F\u52A8\n const s1 = await runner.beginSession(\"feature-demo\");\n equal(s1.ok, true);\n if (!s1.ok) return;\n\n // Session 2 \u5E76\u53D1\u542F\u52A8\uFF08\u4F1A\u5F02\u6B65\u6392\u961F\u7B49\u5F85 Session 1 \u5B8C\u6210\uFF09\n let s2Started = false;\n const s2Promise = runner.beginSession(\"feature-demo\").then((res) => {\n s2Started = true;\n return res;\n });\n\n // \u6B64\u65F6 Session 2 \u5E94\u5904\u4E8E\u7B49\u5F85\u4E2D\uFF0C\u672A\u8FDB\u5165\n equal(s2Started, false);\n\n // Session 1 \u63D2\u5165\u6B65\u9AA4\n const s1Step = await runner.stepSession(s1.txId, \"feature-demo\", {\n op: \"insert\",\n table: \"queue_items\",\n row: { id: \"1\", val: \"one\" },\n });\n equal(s1Step.ok, true);\n\n // Session 1 \u63D0\u4EA4\n const s1Commit = runner.commitSession(s1.txId, \"feature-demo\");\n equal(s1Commit.ok, true);\n\n // Session 1 \u63D0\u4EA4\u540E\uFF0CSession 2 \u5E94\u8BE5\u88AB\u5524\u9192\u5E76\u6210\u529F\u83B7\u5F97\u4E8B\u52A1\n const s2 = await s2Promise;\n equal(s2.ok, true);\n equal(s2Started, true);\n if (!s2.ok) return;\n\n // Session 2 \u63D2\u5165\u6B65\u9AA4\u5E76\u63D0\u4EA4\n const s2Step = await runner.stepSession(s2.txId, \"feature-demo\", {\n op: \"insert\",\n table: \"queue_items\",\n row: { id: \"2\", val: \"two\" },\n });\n equal(s2Step.ok, true);\n\n const s2Commit = runner.commitSession(s2.txId, \"feature-demo\");\n equal(s2Commit.ok, true);\n\n const rows = db.prepare(\"SELECT val FROM queue_items ORDER BY id ASC\").all() as Array<{ val: string }>;\n equal(rows.length, 2);\n equal(rows[0]?.val, \"one\");\n equal(rows[1]?.val, \"two\");\n db.close();\n});\n\ntest(\"TxRunner \u5355\u8FDE\u63A5: run \u4E0E\u4EA4\u4E92\u4F1A\u8BDD\u5171\u7528\u4E8B\u52A1\u961F\u5217\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const query = createQuery(db);\n const schema = new SchemaRegistry(db);\n schema.define(\"feature-demo\", {\n name: \"queue_items\",\n columns: {\n id: { type: \"text\", primary: true },\n val: { type: \"text\", notNull: true },\n },\n softDelete: false,\n });\n\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [\"db:read:queue_items\", \"db:write:queue_items\"],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n\n const runner = new TxRunner({\n db,\n query,\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n });\n\n const session = await runner.beginSession(\"feature-demo\");\n equal(session.ok, true);\n if (!session.ok) return;\n\n let runFinished = false;\n const queuedRun = runner\n .run({\n moduleId: \"feature-demo\",\n steps: [{ op: \"query\", table: \"queue_items\" }],\n })\n .then((result) => {\n runFinished = true;\n return result;\n });\n equal(runFinished, false);\n\n equal(runner.commitSession(session.txId, \"feature-demo\").ok, true);\n equal((await queuedRun).ok, true);\n equal(runFinished, true);\n\n const running = runner.run({\n moduleId: \"feature-demo\",\n steps: [{ op: \"query\", table: \"queue_items\" }],\n });\n let sessionStarted = false;\n const queuedSession = runner.beginSession(\"feature-demo\").then((result) => {\n sessionStarted = true;\n return result;\n });\n equal(sessionStarted, false);\n equal((await running).ok, true);\n\n const nextSession = await queuedSession;\n equal(nextSession.ok, true);\n equal(sessionStarted, true);\n if (nextSession.ok) equal(runner.rollbackSession(nextSession.txId, \"feature-demo\").ok, true);\n\n db.close();\n});\n\ntest(\"TxRunner \u4F1A\u56DE\u6536\u7A7A\u95F2\u4EA4\u4E92\u4E8B\u52A1\u5E76\u91CA\u653E\u7B49\u5F85\u8005\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const schema = new SchemaRegistry(db);\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n const runner = new TxRunner(\n {\n db,\n query: createQuery(db),\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n },\n { sessionIdleTimeoutMs: 20, slotWaitTimeoutMs: 200 }\n );\n\n const orphan = await runner.beginSession(\"feature-demo\");\n equal(orphan.ok, true);\n const recovered = await runner.beginSession(\"feature-demo\");\n equal(recovered.ok, true);\n if (recovered.ok) equal(runner.rollbackSession(recovered.txId, \"feature-demo\").ok, true);\n db.close();\n});\n\ntest(\"TxRunner \u4E8B\u52A1\u69FD\u7B49\u5F85\u8D85\u65F6\u8FD4\u56DE transaction_busy\", async () => {\n const { DatabaseSync } = await import(\"node:sqlite\");\n const { createQuery } = await import(\"./lib/sqlite.js\");\n const { SchemaRegistry } = await import(\"./schema-registry.js\");\n const { TxRunner } = await import(\"./tx-runner.js\");\n\n const db = new DatabaseSync(\":memory:\");\n const schema = new SchemaRegistry(db);\n const enabled = new Map([\n [\n \"feature-demo\",\n {\n id: \"feature-demo\",\n version: \"1.0.0\",\n permissions: [],\n services: { provides: [], requires: [] },\n db: { tables: [] },\n config: { key: \"demo\" },\n } as unknown as import(\"./manifest-loader.js\").ModuleManifestV2,\n ],\n ]);\n const runner = new TxRunner(\n {\n db,\n query: createQuery(db),\n schema,\n serviceRegistry: new ServiceRegistry(),\n enabled,\n },\n { sessionIdleTimeoutMs: 200, slotWaitTimeoutMs: 20 }\n );\n\n const owner = await runner.beginSession(\"feature-demo\");\n equal(owner.ok, true);\n const blocked = await runner.beginSession(\"feature-demo\");\n equal(blocked.ok, false);\n if (!blocked.ok) equal(blocked.code, \"transaction_busy\");\n if (owner.ok) equal(runner.rollbackSession(owner.txId, \"feature-demo\").ok, true);\n db.close();\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|