@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.
Files changed (163) hide show
  1. package/LICENSE +663 -0
  2. package/dist/domain/bridge.d.ts +57 -23
  3. package/dist/domain/bridge.d.ts.map +1 -1
  4. package/dist/domain/bridge.js +77 -6
  5. package/dist/domain/bridge.js.map +2 -2
  6. package/dist/domain/bridge.test.d.ts +2 -0
  7. package/dist/domain/bridge.test.d.ts.map +1 -0
  8. package/dist/domain/bridge.test.js +87 -0
  9. package/dist/domain/bridge.test.js.map +7 -0
  10. package/dist/domain/economy.d.ts +38 -7
  11. package/dist/domain/economy.d.ts.map +1 -1
  12. package/dist/domain/economy.js.map +2 -2
  13. package/dist/domain/messages-bridge-filter.test.d.ts +5 -0
  14. package/dist/domain/messages-bridge-filter.test.d.ts.map +1 -0
  15. package/dist/domain/messages-bridge-filter.test.js +20 -0
  16. package/dist/domain/messages-bridge-filter.test.js.map +7 -0
  17. package/dist/domain/player-session.d.ts +8 -0
  18. package/dist/domain/player-session.d.ts.map +1 -0
  19. package/dist/domain/player-session.js +16 -0
  20. package/dist/domain/player-session.js.map +7 -0
  21. package/dist/domain/player-session.test.d.ts +2 -0
  22. package/dist/domain/player-session.test.d.ts.map +1 -0
  23. package/dist/domain/player-session.test.js +33 -0
  24. package/dist/domain/player-session.test.js.map +7 -0
  25. package/dist/domain/qq-bind-status.test.d.ts +2 -0
  26. package/dist/domain/qq-bind-status.test.d.ts.map +1 -0
  27. package/dist/domain/qq-bind-status.test.js +211 -0
  28. package/dist/domain/qq-bind-status.test.js.map +7 -0
  29. package/dist/domain/qq-events.d.ts +84 -0
  30. package/dist/domain/qq-events.d.ts.map +1 -0
  31. package/dist/domain/qq-events.js +193 -0
  32. package/dist/domain/qq-events.js.map +7 -0
  33. package/dist/domain/qq-events.test.d.ts +5 -0
  34. package/dist/domain/qq-events.test.d.ts.map +1 -0
  35. package/dist/domain/qq-events.test.js +170 -0
  36. package/dist/domain/qq-events.test.js.map +7 -0
  37. package/dist/domain/qq-join.test.d.ts +2 -0
  38. package/dist/domain/qq-join.test.d.ts.map +1 -0
  39. package/dist/domain/qq-join.test.js +353 -0
  40. package/dist/domain/qq-join.test.js.map +7 -0
  41. package/dist/domain/schema.d.ts +15 -18
  42. package/dist/domain/schema.d.ts.map +1 -1
  43. package/dist/domain/schema.js +104 -453
  44. package/dist/domain/schema.js.map +2 -2
  45. package/dist/domain/system-status.d.ts +75 -0
  46. package/dist/domain/system-status.d.ts.map +1 -0
  47. package/dist/domain/system-status.js +181 -0
  48. package/dist/domain/system-status.js.map +7 -0
  49. package/dist/domain/system-status.test.d.ts +5 -0
  50. package/dist/domain/system-status.test.d.ts.map +1 -0
  51. package/dist/domain/system-status.test.js +19 -0
  52. package/dist/domain/system-status.test.js.map +7 -0
  53. package/dist/domain/transaction.d.ts +5 -4
  54. package/dist/domain/transaction.d.ts.map +1 -1
  55. package/dist/domain/transaction.js.map +2 -2
  56. package/dist/env.d.ts +7 -0
  57. package/dist/env.d.ts.map +1 -1
  58. package/dist/env.js +57 -9
  59. package/dist/env.js.map +2 -2
  60. package/dist/index.d.ts +1 -2
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +128 -38
  63. package/dist/index.js.map +2 -2
  64. package/dist/lib/idempotency-store.d.ts +17 -11
  65. package/dist/lib/idempotency-store.d.ts.map +1 -1
  66. package/dist/lib/idempotency-store.js.map +2 -2
  67. package/dist/lib/idempotency.d.ts +3 -4
  68. package/dist/lib/idempotency.d.ts.map +1 -1
  69. package/dist/lib/idempotency.js.map +2 -2
  70. package/dist/lib/log.d.ts.map +1 -1
  71. package/dist/lib/log.js +3 -6
  72. package/dist/lib/log.js.map +2 -2
  73. package/dist/lib/module-state.d.ts +38 -8
  74. package/dist/lib/module-state.d.ts.map +1 -1
  75. package/dist/lib/module-state.js.map +2 -2
  76. package/dist/lib/order-by.d.ts +8 -6
  77. package/dist/lib/order-by.d.ts.map +1 -1
  78. package/dist/lib/order-by.js.map +2 -2
  79. package/dist/lib/sql-helpers.d.ts +25 -13
  80. package/dist/lib/sql-helpers.d.ts.map +1 -1
  81. package/dist/lib/sql-helpers.js.map +2 -2
  82. package/dist/lib/sqlite.d.ts +14 -5
  83. package/dist/lib/sqlite.d.ts.map +1 -1
  84. package/dist/lib/sqlite.js.map +2 -2
  85. package/dist/manifest-loader.d.ts +5 -2
  86. package/dist/manifest-loader.d.ts.map +1 -1
  87. package/dist/manifest-loader.js +16 -6
  88. package/dist/manifest-loader.js.map +2 -2
  89. package/dist/manifest.d.ts +1 -2
  90. package/dist/manifest.d.ts.map +1 -1
  91. package/dist/manifest.js +13 -8
  92. package/dist/manifest.js.map +2 -2
  93. package/dist/module-auth.d.ts.map +1 -1
  94. package/dist/module-auth.js +2 -6
  95. package/dist/module-auth.js.map +2 -2
  96. package/dist/permission-gate.d.ts.map +1 -1
  97. package/dist/permission-gate.js +8 -2
  98. package/dist/permission-gate.js.map +2 -2
  99. package/dist/project-root.d.ts +6 -0
  100. package/dist/project-root.d.ts.map +1 -0
  101. package/dist/project-root.js +9 -0
  102. package/dist/project-root.js.map +7 -0
  103. package/dist/routes/_shared.d.ts +14 -5
  104. package/dist/routes/_shared.d.ts.map +1 -1
  105. package/dist/routes/_shared.js.map +2 -2
  106. package/dist/routes/config.d.ts +8 -11
  107. package/dist/routes/config.d.ts.map +1 -1
  108. package/dist/routes/config.js +17 -23
  109. package/dist/routes/config.js.map +2 -2
  110. package/dist/routes/db-routes.d.ts +8 -2
  111. package/dist/routes/db-routes.d.ts.map +1 -1
  112. package/dist/routes/db-routes.js +5 -12
  113. package/dist/routes/db-routes.js.map +2 -2
  114. package/dist/routes/messages.d.ts +6 -2
  115. package/dist/routes/messages.d.ts.map +1 -1
  116. package/dist/routes/messages.js +17 -3
  117. package/dist/routes/messages.js.map +2 -2
  118. package/dist/routes/module-config-routes.d.ts +13 -9
  119. package/dist/routes/module-config-routes.d.ts.map +1 -1
  120. package/dist/routes/module-config-routes.js.map +2 -2
  121. package/dist/routes/modules.d.ts +6 -0
  122. package/dist/routes/modules.d.ts.map +1 -1
  123. package/dist/routes/modules.js.map +2 -2
  124. package/dist/routes/qq-bind.d.ts +24 -0
  125. package/dist/routes/qq-bind.d.ts.map +1 -0
  126. package/dist/routes/qq-bind.js +179 -0
  127. package/dist/routes/qq-bind.js.map +7 -0
  128. package/dist/routes/qq-events.d.ts +20 -0
  129. package/dist/routes/qq-events.d.ts.map +1 -0
  130. package/dist/routes/qq-events.js +39 -0
  131. package/dist/routes/qq-events.js.map +7 -0
  132. package/dist/routes/qq-join.d.ts +46 -0
  133. package/dist/routes/qq-join.d.ts.map +1 -0
  134. package/dist/routes/qq-join.js +277 -0
  135. package/dist/routes/qq-join.js.map +7 -0
  136. package/dist/routes/service-routes.d.ts +6 -0
  137. package/dist/routes/service-routes.d.ts.map +1 -1
  138. package/dist/routes/service-routes.js.map +2 -2
  139. package/dist/routes/status.d.ts +19 -0
  140. package/dist/routes/status.d.ts.map +1 -0
  141. package/dist/routes/status.js +74 -0
  142. package/dist/routes/status.js.map +7 -0
  143. package/dist/runtime.test.js +216 -4
  144. package/dist/runtime.test.js.map +2 -2
  145. package/dist/schema-registry.d.ts +17 -16
  146. package/dist/schema-registry.d.ts.map +1 -1
  147. package/dist/schema-registry.js +76 -6
  148. package/dist/schema-registry.js.map +2 -2
  149. package/dist/server.d.ts +19 -0
  150. package/dist/server.d.ts.map +1 -1
  151. package/dist/server.js.map +2 -2
  152. package/dist/service-registry.d.ts +39 -9
  153. package/dist/service-registry.d.ts.map +1 -1
  154. package/dist/service-registry.js +30 -2
  155. package/dist/service-registry.js.map +2 -2
  156. package/dist/services/builtin-handlers.d.ts +27 -7
  157. package/dist/services/builtin-handlers.d.ts.map +1 -1
  158. package/dist/services/builtin-handlers.js.map +2 -2
  159. package/dist/tx-runner.d.ts +48 -9
  160. package/dist/tx-runner.d.ts.map +1 -1
  161. package/dist/tx-runner.js +99 -27
  162. package/dist/tx-runner.js.map +2 -2
  163. package/package.json +26 -24
@@ -1,28 +1,34 @@
1
1
  /**
2
- * lib/idempotency.ts — db.idempotent() 后端
2
+ * lib/idempotency-store.ts — 幂等操作底层持久化存储
3
3
  *
4
- * 表:sfmc__idempotent (db-tables.ts 创建)
5
- * key: (module_id, action, key) → cached value?
4
+ * 底层表:`sfmc__idempotent`
5
+ * 主键复合维度:`module_id` + `action` + `key`
6
6
  *
7
- * probe(module_id, action, key):
8
- * - 命中 → {replayed:true, cached}
9
- * - 不命中 → {replayed:false}
7
+ * 探针机制:
8
+ * - `probe(moduleId, action, key)`:探测是否已执行过;命中则返回 `{ replayed: true, cached }`,未命中返回 `{ replayed: false }`
9
+ * - `commit(moduleId, action, key, value?)`:写入成功执行记录与缓存结果
10
10
  *
11
- * commit(module_id, action, key, value?):
12
- * - INSERT … (replace) — 即使没 probe 也允许直接 commit
13
- *
14
- * 事务内调用一律走 tx-runner 不行(commit 必须在事务外完成,跨事务持久化);
15
- * 这里单独端点 POST /api/sfmc/db/idempotent/{probe,commit},不走 tx。
11
+ * 事务与持久化边界:
12
+ * 幂等提交独立于单次业务事务,确保跨事务持久化去重。
16
13
  */
17
14
  import type { DatabaseSync } from "node:sqlite";
15
+ /** 幂等存储器接口。 */
18
16
  export interface IdempotencyStore {
17
+ /** 探测指定的动作与幂等键是否已经执行过。 */
19
18
  probe(moduleId: string, action: string, key: string): Promise<{
20
19
  replayed: boolean;
21
20
  cached?: unknown;
22
21
  }>;
22
+ /** 提交并持久化已执行的幂等动作与结果数据。 */
23
23
  commit(moduleId: string, action: string, key: string, value?: unknown): Promise<{
24
24
  ok: boolean;
25
25
  }>;
26
26
  }
27
+ /**
28
+ * 创建基于 SQLite 的幂等存储操作实例。
29
+ *
30
+ * @param db SQLite 数据库连接。
31
+ * @returns 幂等存储器对象。
32
+ */
27
33
  export declare function createIdempotencyStore(db: DatabaseSync): IdempotencyStore;
28
34
  //# sourceMappingURL=idempotency-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"idempotency-store.d.ts","sourceRoot":"","sources":["../../src/lib/idempotency-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACvG,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAClG;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,gBAAgB,CAgCzE"}
1
+ {"version":3,"file":"idempotency-store.d.ts","sourceRoot":"","sources":["../../src/lib/idempotency-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,eAAe;AACf,MAAM,WAAW,gBAAgB;IAC/B,0BAA0B;IAC1B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACvG,2BAA2B;IAC3B,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAClG;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,gBAAgB,CAiCzE"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/idempotency-store.ts"],
4
- "sourcesContent": ["/**\n * lib/idempotency.ts \u2014 db.idempotent() \u540E\u7AEF\n *\n * \u8868:sfmc__idempotent (db-tables.ts \u521B\u5EFA)\n * key: (module_id, action, key) \u2192 cached value?\n *\n * probe(module_id, action, key):\n * - \u547D\u4E2D \u2192 {replayed:true, cached}\n * - \u4E0D\u547D\u4E2D \u2192 {replayed:false}\n *\n * commit(module_id, action, key, value?):\n * - INSERT \u2026 (replace) \u2014 \u5373\u4F7F\u6CA1 probe \u4E5F\u5141\u8BB8\u76F4\u63A5 commit\n *\n * \u4E8B\u52A1\u5185\u8C03\u7528\u4E00\u5F8B\u8D70 tx-runner \u4E0D\u884C(commit \u5FC5\u987B\u5728\u4E8B\u52A1\u5916\u5B8C\u6210,\u8DE8\u4E8B\u52A1\u6301\u4E45\u5316);\n * \u8FD9\u91CC\u5355\u72EC\u7AEF\u70B9 POST /api/sfmc/db/idempotent/{probe,commit},\u4E0D\u8D70 tx\u3002\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport { isValidIdempotencyKey } from \"./idempotency.js\";\n\nexport interface IdempotencyStore {\n probe(moduleId: string, action: string, key: string): Promise<{ replayed: boolean; cached?: unknown }>;\n commit(moduleId: string, action: string, key: string, value?: unknown): Promise<{ ok: boolean }>;\n}\n\nexport function createIdempotencyStore(db: DatabaseSync): IdempotencyStore {\n const probeStmt = db.prepare(\n \"SELECT value FROM sfmc__idempotent WHERE module_id=? AND action=? AND key=?\"\n );\n const insertStmt = db.prepare(\n `INSERT OR REPLACE INTO sfmc__idempotent (module_id, action, key, value, created_at)\n VALUES (?, ?, ?, ?, ?)`\n );\n\n return {\n async probe(moduleId, action, key) {\n if (!isValidIdempotencyKey(key)) throw new Error(\"invalid idempotency key\");\n const row = probeStmt.get(moduleId, action, key) as { value: string | null } | undefined;\n if (!row) return { replayed: false };\n let cached: unknown = null;\n if (row.value) {\n try {\n cached = JSON.parse(row.value);\n } catch {\n cached = row.value;\n }\n }\n return { replayed: true, cached };\n },\n\n async commit(moduleId, action, key, value?) {\n if (!isValidIdempotencyKey(key)) throw new Error(\"invalid idempotency key\");\n const json = value === undefined ? null : JSON.stringify(value);\n insertStmt.run(moduleId, action, key, json, new Date().toISOString());\n return { ok: true };\n },\n };\n}\n"],
5
- "mappings": "AAkBA,SAAS,6BAA6B;AAO/B,SAAS,uBAAuB,IAAoC;AACzE,QAAM,YAAY,GAAG;AAAA,IACnB;AAAA,EACF;AACA,QAAM,aAAa,GAAG;AAAA,IACpB;AAAA;AAAA,EAEF;AAEA,SAAO;AAAA,IACL,MAAM,MAAM,UAAU,QAAQ,KAAK;AACjC,UAAI,CAAC,sBAAsB,GAAG,EAAG,OAAM,IAAI,MAAM,yBAAyB;AAC1E,YAAM,MAAM,UAAU,IAAI,UAAU,QAAQ,GAAG;AAC/C,UAAI,CAAC,IAAK,QAAO,EAAE,UAAU,MAAM;AACnC,UAAI,SAAkB;AACtB,UAAI,IAAI,OAAO;AACb,YAAI;AACF,mBAAS,KAAK,MAAM,IAAI,KAAK;AAAA,QAC/B,QAAQ;AACN,mBAAS,IAAI;AAAA,QACf;AAAA,MACF;AACA,aAAO,EAAE,UAAU,MAAM,OAAO;AAAA,IAClC;AAAA,IAEA,MAAM,OAAO,UAAU,QAAQ,KAAK,OAAQ;AAC1C,UAAI,CAAC,sBAAsB,GAAG,EAAG,OAAM,IAAI,MAAM,yBAAyB;AAC1E,YAAM,OAAO,UAAU,SAAY,OAAO,KAAK,UAAU,KAAK;AAC9D,iBAAW,IAAI,UAAU,QAAQ,KAAK,OAAM,oBAAI,KAAK,GAAE,YAAY,CAAC;AACpE,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["/**\n * lib/idempotency-store.ts \u2014 \u5E42\u7B49\u64CD\u4F5C\u5E95\u5C42\u6301\u4E45\u5316\u5B58\u50A8\n *\n * \u5E95\u5C42\u8868\uFF1A`sfmc__idempotent`\n * \u4E3B\u952E\u590D\u5408\u7EF4\u5EA6\uFF1A`module_id` + `action` + `key`\n *\n * \u63A2\u9488\u673A\u5236\uFF1A\n * - `probe(moduleId, action, key)`\uFF1A\u63A2\u6D4B\u662F\u5426\u5DF2\u6267\u884C\u8FC7\uFF1B\u547D\u4E2D\u5219\u8FD4\u56DE `{ replayed: true, cached }`\uFF0C\u672A\u547D\u4E2D\u8FD4\u56DE `{ replayed: false }`\n * - `commit(moduleId, action, key, value?)`\uFF1A\u5199\u5165\u6210\u529F\u6267\u884C\u8BB0\u5F55\u4E0E\u7F13\u5B58\u7ED3\u679C\n *\n * \u4E8B\u52A1\u4E0E\u6301\u4E45\u5316\u8FB9\u754C\uFF1A\n * \u5E42\u7B49\u63D0\u4EA4\u72EC\u7ACB\u4E8E\u5355\u6B21\u4E1A\u52A1\u4E8B\u52A1\uFF0C\u786E\u4FDD\u8DE8\u4E8B\u52A1\u6301\u4E45\u5316\u53BB\u91CD\u3002\n */\n\nimport type { DatabaseSync } from \"node:sqlite\";\nimport { isValidIdempotencyKey } from \"./idempotency.js\";\n\n/** \u5E42\u7B49\u5B58\u50A8\u5668\u63A5\u53E3\u3002 */\nexport interface IdempotencyStore {\n /** \u63A2\u6D4B\u6307\u5B9A\u7684\u52A8\u4F5C\u4E0E\u5E42\u7B49\u952E\u662F\u5426\u5DF2\u7ECF\u6267\u884C\u8FC7\u3002 */\n probe(moduleId: string, action: string, key: string): Promise<{ replayed: boolean; cached?: unknown }>;\n /** \u63D0\u4EA4\u5E76\u6301\u4E45\u5316\u5DF2\u6267\u884C\u7684\u5E42\u7B49\u52A8\u4F5C\u4E0E\u7ED3\u679C\u6570\u636E\u3002 */\n commit(moduleId: string, action: string, key: string, value?: unknown): Promise<{ ok: boolean }>;\n}\n\n/**\n * \u521B\u5EFA\u57FA\u4E8E SQLite \u7684\u5E42\u7B49\u5B58\u50A8\u64CD\u4F5C\u5B9E\u4F8B\u3002\n *\n * @param db SQLite \u6570\u636E\u5E93\u8FDE\u63A5\u3002\n * @returns \u5E42\u7B49\u5B58\u50A8\u5668\u5BF9\u8C61\u3002\n */\nexport function createIdempotencyStore(db: DatabaseSync): IdempotencyStore {\n\n const probeStmt = db.prepare(\n \"SELECT value FROM sfmc__idempotent WHERE module_id=? AND action=? AND key=?\"\n );\n const insertStmt = db.prepare(\n `INSERT OR REPLACE INTO sfmc__idempotent (module_id, action, key, value, created_at)\n VALUES (?, ?, ?, ?, ?)`\n );\n\n return {\n async probe(moduleId, action, key) {\n if (!isValidIdempotencyKey(key)) throw new Error(\"invalid idempotency key\");\n const row = probeStmt.get(moduleId, action, key) as { value: string | null } | undefined;\n if (!row) return { replayed: false };\n let cached: unknown = null;\n if (row.value) {\n try {\n cached = JSON.parse(row.value);\n } catch {\n cached = row.value;\n }\n }\n return { replayed: true, cached };\n },\n\n async commit(moduleId, action, key, value?) {\n if (!isValidIdempotencyKey(key)) throw new Error(\"invalid idempotency key\");\n const json = value === undefined ? null : JSON.stringify(value);\n insertStmt.run(moduleId, action, key, json, new Date().toISOString());\n return { ok: true };\n },\n };\n}\n"],
5
+ "mappings": "AAeA,SAAS,6BAA6B;AAgB/B,SAAS,uBAAuB,IAAoC;AAEzE,QAAM,YAAY,GAAG;AAAA,IACnB;AAAA,EACF;AACA,QAAM,aAAa,GAAG;AAAA,IACpB;AAAA;AAAA,EAEF;AAEA,SAAO;AAAA,IACL,MAAM,MAAM,UAAU,QAAQ,KAAK;AACjC,UAAI,CAAC,sBAAsB,GAAG,EAAG,OAAM,IAAI,MAAM,yBAAyB;AAC1E,YAAM,MAAM,UAAU,IAAI,UAAU,QAAQ,GAAG;AAC/C,UAAI,CAAC,IAAK,QAAO,EAAE,UAAU,MAAM;AACnC,UAAI,SAAkB;AACtB,UAAI,IAAI,OAAO;AACb,YAAI;AACF,mBAAS,KAAK,MAAM,IAAI,KAAK;AAAA,QAC/B,QAAQ;AACN,mBAAS,IAAI;AAAA,QACf;AAAA,MACF;AACA,aAAO,EAAE,UAAU,MAAM,OAAO;AAAA,IAClC;AAAA,IAEA,MAAM,OAAO,UAAU,QAAQ,KAAK,OAAQ;AAC1C,UAAI,CAAC,sBAAsB,GAAG,EAAG,OAAM,IAAI,MAAM,yBAAyB;AAC1E,YAAM,OAAO,UAAU,SAAY,OAAO,KAAK,UAAU,KAAK;AAC9D,iBAAW,IAAI,UAAU,QAAQ,KAAK,OAAM,oBAAI,KAAK,GAAE,YAAY,CAAC;AACpE,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,9 +1,8 @@
1
1
  /**
2
- * 验证幂等键格式
2
+ * 校验幂等键格式是否合法(仅允许 1~128 位的字母、数字及 `_.:-` 字符)。
3
3
  *
4
- * @export
5
- * @param {string} key
6
- * @return {*} {(boolean | string)}
4
+ * @param key 待校验的幂等键。
5
+ * @returns 格式合法返回 `true`,否则返回 `false`。
7
6
  */
8
7
  export declare function isValidIdempotencyKey(key: string): boolean;
9
8
  //# sourceMappingURL=idempotency.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../src/lib/idempotency.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAI1D"}
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../src/lib/idempotency.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAK1D"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/idempotency.ts"],
4
- "sourcesContent": ["/**\n * \u9A8C\u8BC1\u5E42\u7B49\u952E\u683C\u5F0F\n *\n * @export\n * @param {string} key\n * @return {*} {(boolean | string)}\n */\nexport function isValidIdempotencyKey(key: string): boolean {\n if (!key || typeof key !== \"string\") return false;\n const trimmed = key.trim();\n return trimmed.length > 0 && /^[A-Za-z0-9_.:-]{1,128}$/.test(trimmed);\n}\n"],
5
- "mappings": "AAOO,SAAS,sBAAsB,KAAsB;AAC1D,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,UAAU,IAAI,KAAK;AACzB,SAAO,QAAQ,SAAS,KAAK,2BAA2B,KAAK,OAAO;AACtE;",
4
+ "sourcesContent": ["/**\n * \u6821\u9A8C\u5E42\u7B49\u952E\u683C\u5F0F\u662F\u5426\u5408\u6CD5\uFF08\u4EC5\u5141\u8BB8 1~128 \u4F4D\u7684\u5B57\u6BCD\u3001\u6570\u5B57\u53CA `_.:-` \u5B57\u7B26\uFF09\u3002\n *\n * @param key \u5F85\u6821\u9A8C\u7684\u5E42\u7B49\u952E\u3002\n * @returns \u683C\u5F0F\u5408\u6CD5\u8FD4\u56DE `true`\uFF0C\u5426\u5219\u8FD4\u56DE `false`\u3002\n */\nexport function isValidIdempotencyKey(key: string): boolean {\n\n if (!key || typeof key !== \"string\") return false;\n const trimmed = key.trim();\n return trimmed.length > 0 && /^[A-Za-z0-9_.:-]{1,128}$/.test(trimmed);\n}\n"],
5
+ "mappings": "AAMO,SAAS,sBAAsB,KAAsB;AAE1D,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,UAAU,IAAI,KAAK;AACzB,SAAO,QAAQ,SAAS,KAAK,2BAA2B,KAAK,OAAO;AACtE;",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/lib/log.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,eAAO,MAAM,GAAG,gDAGd,CAAC"}
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/lib/log.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,eAAO,MAAM,GAAG,gDAGd,CAAC"}
package/dist/lib/log.js CHANGED
@@ -1,13 +1,10 @@
1
1
  import { createNodeServiceLogger } from "@sfmc-bds/sdk/logs";
2
- import { logFile, resolveRuntimeRoot } from "@sfmc-bds/sdk/node/config";
3
- import { dirname, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- const ROOT = resolveRuntimeRoot(resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", ".."));
2
+ import { logFile } from "@sfmc-bds/sdk/node/config";
3
+ import { PROJECT_ROOT } from "../project-root.js";
6
4
  const log = createNodeServiceLogger({
7
5
  source: "db",
8
- logPath: logFile(ROOT, "db")
6
+ logPath: logFile(PROJECT_ROOT, "db")
9
7
  });
10
- process.on("exit", () => log.close());
11
8
  export {
12
9
  log
13
10
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/log.ts"],
4
- "sourcesContent": ["/**\n * log.ts \u2014 db-server \u7EDF\u4E00\u65E5\u5FD7\u5B9E\u4F8B\n *\n * stdout bare + \u843D\u76D8 `<SFMC_ROOT>/.sfmc/logs/db.log`\u3002\n * bare: \u53EA\u8F93\u51FA\u7EAF text,\u7531 sfmc \u6355\u83B7\u540E\u7EDF\u4E00\u52A0\u524D\u7F00;error \u8D70 stderr\u3002\n */\n\nimport { createNodeServiceLogger } from \"@sfmc-bds/sdk/logs\";\nimport { logFile, resolveRuntimeRoot } from \"@sfmc-bds/sdk/node/config\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** src/lib \u2192 \u4ED3\u6839(\u4E0E env.ts \u7684 src \u4E0A\u6EAF\u5DEE\u4E00\u5C42) */\nconst ROOT = resolveRuntimeRoot(resolve(dirname(fileURLToPath(import.meta.url)), \"..\", \"..\", \"..\"));\n\nexport const log = createNodeServiceLogger({\n source: \"db\",\n logPath: logFile(ROOT, \"db\"),\n});\n\nprocess.on(\"exit\", () => log.close());\n"],
5
- "mappings": "AAOA,SAAS,+BAA+B;AACxC,SAAS,SAAS,0BAA0B;AAC5C,SAAS,SAAS,eAAe;AACjC,SAAS,qBAAqB;AAG9B,MAAM,OAAO,mBAAmB,QAAQ,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,MAAM,MAAM,IAAI,CAAC;AAE3F,MAAM,MAAM,wBAAwB;AAAA,EACzC,QAAQ;AAAA,EACR,SAAS,QAAQ,MAAM,IAAI;AAC7B,CAAC;AAED,QAAQ,GAAG,QAAQ,MAAM,IAAI,MAAM,CAAC;",
4
+ "sourcesContent": ["/**\n * log.ts \u2014 db-server \u7EDF\u4E00\u65E5\u5FD7\u5B9E\u4F8B\n *\n * stdout bare + \u843D\u76D8 `<SFMC_ROOT>/.sfmc/logs/db.log`\u3002\n * bare: \u53EA\u8F93\u51FA\u7EAF text,\u7531 sfmc \u6355\u83B7\u540E\u7EDF\u4E00\u52A0\u524D\u7F00;error \u8D70 stderr\u3002\n */\n\nimport { createNodeServiceLogger } from \"@sfmc-bds/sdk/logs\";\nimport { logFile } from \"@sfmc-bds/sdk/node/config\";\nimport { PROJECT_ROOT } from \"../project-root.js\";\n\nexport const log = createNodeServiceLogger({\n source: \"db\",\n logPath: logFile(PROJECT_ROOT, \"db\"),\n});\n"],
5
+ "mappings": "AAOA,SAAS,+BAA+B;AACxC,SAAS,eAAe;AACxB,SAAS,oBAAoB;AAEtB,MAAM,MAAM,wBAAwB;AAAA,EACzC,QAAQ;AAAA,EACR,SAAS,QAAQ,cAAc,IAAI;AACrC,CAAC;",
6
6
  "names": []
7
7
  }
@@ -1,15 +1,45 @@
1
- interface ModuleLock {
2
- version: number;
3
- modules: Record<string, {
4
- enabled?: boolean;
5
- updatedAt?: number;
6
- }>;
7
- }
8
- type ModuleState = ModuleLock["modules"][string];
1
+ import type { ModuleLock, ModuleRuntimeState } from "@sfmc-bds/sdk/contracts";
2
+ type ModuleState = ModuleRuntimeState;
3
+ /**
4
+ * 读取 `module-lock.json` 模块启停锁文件;若文件不存在则初始化空骨架文件并自动落盘。
5
+ *
6
+ * @param filePath 锁文件绝对路径。
7
+ * @returns 模块锁数据结构。
8
+ */
9
9
  export declare function loadModuleLock(filePath: string): ModuleLock;
10
+ /**
11
+ * 持久化写入 `module-lock.json` 模块锁文件。
12
+ *
13
+ * @param filePath 目标文件绝对路径。
14
+ * @param lock 待持久化的模块锁数据。
15
+ */
10
16
  export declare function saveModuleLock(filePath: string, lock: ModuleLock): void;
17
+ /**
18
+ * 获取指定模块的启停状态信息。
19
+ *
20
+ * @param lock 模块锁数据。
21
+ * @param id 模块唯一标识符。
22
+ * @param defaults 回退默认状态。
23
+ * @returns 模块状态对象。
24
+ */
11
25
  export declare function getModuleState(lock: ModuleLock, id: string, defaults?: ModuleState): ModuleState;
26
+ /**
27
+ * 判断指定模块当前是否处于启用状态。
28
+ *
29
+ * @param lock 模块锁数据。
30
+ * @param id 模块唯一标识符。
31
+ * @param defaultValue 缺省默认启用值(默认为 `false`)。
32
+ * @returns 是否启用。
33
+ */
12
34
  export declare function isEnabled(lock: ModuleLock, id: string, defaultValue?: boolean): boolean;
35
+ /**
36
+ * 更新指定模块的状态信息并刷新修改时间戳(`updatedAt`)。
37
+ *
38
+ * @param lock 模块锁数据对象。
39
+ * @param id 模块唯一标识符。
40
+ * @param patch 状态增量补丁。
41
+ * @returns 更新后的模块状态对象。
42
+ */
13
43
  export declare function updateModuleState(lock: ModuleLock, id: string, patch: Partial<Omit<ModuleState, "updatedAt">>): ModuleState;
14
44
  export {};
15
45
  //# sourceMappingURL=module-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module-state.d.ts","sourceRoot":"","sources":["../../src/lib/module-state.ts"],"names":[],"mappings":"AAEA,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE;AAED,KAAK,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAS3D;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAEvE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,WAAgB,GAAG,WAAW,CAGpG;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAE9F;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,EAChB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,GAC7C,WAAW,CASb"}
1
+ {"version":3,"file":"module-state.d.ts","sourceRoot":"","sources":["../../src/lib/module-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,KAAK,WAAW,GAAG,kBAAkB,CAAC;AAEtC;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAS3D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAEvE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,WAAgB,GAAG,WAAW,CAGpG;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAE9F;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,EAChB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,GAC7C,WAAW,CASb"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/module-state.ts"],
4
- "sourcesContent": ["import { readJson, writeJson } from \"@sfmc-bds/sdk/node/config\";\n\ninterface ModuleLock {\n version: number;\n modules: Record<string, { enabled?: boolean; updatedAt?: number }>;\n}\n\ntype ModuleState = ModuleLock[\"modules\"][string];\n\nexport function loadModuleLock(filePath: string): ModuleLock {\n const data = readJson<ModuleLock>(filePath);\n const modules = data?.modules && typeof data.modules === \"object\" ? data.modules : {};\n const lock: ModuleLock = { version: 1, modules };\n // \u7F3A\u6587\u4EF6\u65F6\u843D\u76D8\u7A7A\u9AA8\u67B6\uFF0C\u4FBF\u4E8E\u540E\u7EED enable/disable\uFF08\u4E0E configs \u540C\u5C5E\u672C\u5730\u72B6\u6001\uFF09\n if (!data) {\n saveModuleLock(filePath, lock);\n }\n return lock;\n}\n\nexport function saveModuleLock(filePath: string, lock: ModuleLock): void {\n writeJson(filePath, lock);\n}\n\nexport function getModuleState(lock: ModuleLock, id: string, defaults: ModuleState = {}): ModuleState {\n const state = lock.modules[id];\n return state && typeof state === \"object\" ? state : defaults;\n}\n\nexport function isEnabled(lock: ModuleLock, id: string, defaultValue: boolean = false): boolean {\n return getModuleState(lock, id).enabled ?? defaultValue;\n}\n\nexport function updateModuleState(\n lock: ModuleLock,\n id: string,\n patch: Partial<Omit<ModuleState, \"updatedAt\">>\n): ModuleState {\n const now = Date.now();\n const previous = getModuleState(lock, id);\n lock.modules[id] = {\n ...previous,\n ...patch,\n updatedAt: now,\n };\n return lock.modules[id];\n}"],
5
- "mappings": "AAAA,SAAS,UAAU,iBAAiB;AAS7B,SAAS,eAAe,UAA8B;AAC3D,QAAM,OAAO,SAAqB,QAAQ;AAC1C,QAAM,UAAU,MAAM,WAAW,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU,CAAC;AACpF,QAAM,OAAmB,EAAE,SAAS,GAAG,QAAQ;AAE/C,MAAI,CAAC,MAAM;AACT,mBAAe,UAAU,IAAI;AAAA,EAC/B;AACA,SAAO;AACT;AAEO,SAAS,eAAe,UAAkB,MAAwB;AACvE,YAAU,UAAU,IAAI;AAC1B;AAEO,SAAS,eAAe,MAAkB,IAAY,WAAwB,CAAC,GAAgB;AACpG,QAAM,QAAQ,KAAK,QAAQ,EAAE;AAC7B,SAAO,SAAS,OAAO,UAAU,WAAW,QAAQ;AACtD;AAEO,SAAS,UAAU,MAAkB,IAAY,eAAwB,OAAgB;AAC9F,SAAO,eAAe,MAAM,EAAE,EAAE,WAAW;AAC7C;AAEO,SAAS,kBACd,MACA,IACA,OACa;AACb,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,WAAW,eAAe,MAAM,EAAE;AACxC,OAAK,QAAQ,EAAE,IAAI;AAAA,IACjB,GAAG;AAAA,IACH,GAAG;AAAA,IACH,WAAW;AAAA,EACb;AACA,SAAO,KAAK,QAAQ,EAAE;AACxB;",
4
+ "sourcesContent": ["import { readJson, writeJson } from \"@sfmc-bds/sdk/node/config\";\nimport type { ModuleLock, ModuleRuntimeState } from \"@sfmc-bds/sdk/contracts\";\n\ntype ModuleState = ModuleRuntimeState;\n\n/**\n * \u8BFB\u53D6 `module-lock.json` \u6A21\u5757\u542F\u505C\u9501\u6587\u4EF6\uFF1B\u82E5\u6587\u4EF6\u4E0D\u5B58\u5728\u5219\u521D\u59CB\u5316\u7A7A\u9AA8\u67B6\u6587\u4EF6\u5E76\u81EA\u52A8\u843D\u76D8\u3002\n *\n * @param filePath \u9501\u6587\u4EF6\u7EDD\u5BF9\u8DEF\u5F84\u3002\n * @returns \u6A21\u5757\u9501\u6570\u636E\u7ED3\u6784\u3002\n */\nexport function loadModuleLock(filePath: string): ModuleLock {\n const data = readJson<ModuleLock>(filePath);\n const modules = data?.modules && typeof data.modules === \"object\" ? data.modules : {};\n const lock: ModuleLock = { version: 1, modules };\n // \u7F3A\u6587\u4EF6\u65F6\u843D\u76D8\u7A7A\u9AA8\u67B6\uFF0C\u4FBF\u4E8E\u540E\u7EED enable/disable\uFF08\u4E0E configs \u540C\u5C5E\u672C\u5730\u72B6\u6001\uFF09\n if (!data) {\n saveModuleLock(filePath, lock);\n }\n return lock;\n}\n\n/**\n * \u6301\u4E45\u5316\u5199\u5165 `module-lock.json` \u6A21\u5757\u9501\u6587\u4EF6\u3002\n *\n * @param filePath \u76EE\u6807\u6587\u4EF6\u7EDD\u5BF9\u8DEF\u5F84\u3002\n * @param lock \u5F85\u6301\u4E45\u5316\u7684\u6A21\u5757\u9501\u6570\u636E\u3002\n */\nexport function saveModuleLock(filePath: string, lock: ModuleLock): void {\n writeJson(filePath, lock);\n}\n\n/**\n * \u83B7\u53D6\u6307\u5B9A\u6A21\u5757\u7684\u542F\u505C\u72B6\u6001\u4FE1\u606F\u3002\n *\n * @param lock \u6A21\u5757\u9501\u6570\u636E\u3002\n * @param id \u6A21\u5757\u552F\u4E00\u6807\u8BC6\u7B26\u3002\n * @param defaults \u56DE\u9000\u9ED8\u8BA4\u72B6\u6001\u3002\n * @returns \u6A21\u5757\u72B6\u6001\u5BF9\u8C61\u3002\n */\nexport function getModuleState(lock: ModuleLock, id: string, defaults: ModuleState = {}): ModuleState {\n const state = lock.modules[id];\n return state && typeof state === \"object\" ? state : defaults;\n}\n\n/**\n * \u5224\u65AD\u6307\u5B9A\u6A21\u5757\u5F53\u524D\u662F\u5426\u5904\u4E8E\u542F\u7528\u72B6\u6001\u3002\n *\n * @param lock \u6A21\u5757\u9501\u6570\u636E\u3002\n * @param id \u6A21\u5757\u552F\u4E00\u6807\u8BC6\u7B26\u3002\n * @param defaultValue \u7F3A\u7701\u9ED8\u8BA4\u542F\u7528\u503C\uFF08\u9ED8\u8BA4\u4E3A `false`\uFF09\u3002\n * @returns \u662F\u5426\u542F\u7528\u3002\n */\nexport function isEnabled(lock: ModuleLock, id: string, defaultValue: boolean = false): boolean {\n return getModuleState(lock, id).enabled ?? defaultValue;\n}\n\n/**\n * \u66F4\u65B0\u6307\u5B9A\u6A21\u5757\u7684\u72B6\u6001\u4FE1\u606F\u5E76\u5237\u65B0\u4FEE\u6539\u65F6\u95F4\u6233\uFF08`updatedAt`\uFF09\u3002\n *\n * @param lock \u6A21\u5757\u9501\u6570\u636E\u5BF9\u8C61\u3002\n * @param id \u6A21\u5757\u552F\u4E00\u6807\u8BC6\u7B26\u3002\n * @param patch \u72B6\u6001\u589E\u91CF\u8865\u4E01\u3002\n * @returns \u66F4\u65B0\u540E\u7684\u6A21\u5757\u72B6\u6001\u5BF9\u8C61\u3002\n */\nexport function updateModuleState(\n lock: ModuleLock,\n id: string,\n patch: Partial<Omit<ModuleState, \"updatedAt\">>\n): ModuleState {\n const now = Date.now();\n const previous = getModuleState(lock, id);\n lock.modules[id] = {\n ...previous,\n ...patch,\n updatedAt: now,\n };\n return lock.modules[id];\n}\n"],
5
+ "mappings": "AAAA,SAAS,UAAU,iBAAiB;AAW7B,SAAS,eAAe,UAA8B;AAC3D,QAAM,OAAO,SAAqB,QAAQ;AAC1C,QAAM,UAAU,MAAM,WAAW,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU,CAAC;AACpF,QAAM,OAAmB,EAAE,SAAS,GAAG,QAAQ;AAE/C,MAAI,CAAC,MAAM;AACT,mBAAe,UAAU,IAAI;AAAA,EAC/B;AACA,SAAO;AACT;AAQO,SAAS,eAAe,UAAkB,MAAwB;AACvE,YAAU,UAAU,IAAI;AAC1B;AAUO,SAAS,eAAe,MAAkB,IAAY,WAAwB,CAAC,GAAgB;AACpG,QAAM,QAAQ,KAAK,QAAQ,EAAE;AAC7B,SAAO,SAAS,OAAO,UAAU,WAAW,QAAQ;AACtD;AAUO,SAAS,UAAU,MAAkB,IAAY,eAAwB,OAAgB;AAC9F,SAAO,eAAe,MAAM,EAAE,EAAE,WAAW;AAC7C;AAUO,SAAS,kBACd,MACA,IACA,OACa;AACb,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,WAAW,eAAe,MAAM,EAAE;AACxC,OAAK,QAAQ,EAAE,IAAI;AAAA,IACjB,GAAG;AAAA,IACH,GAAG;AAAA,IACH,WAAW;AAAA,EACb;AACA,SAAO,KAAK,QAAQ,EAAE;AACxB;",
6
6
  "names": []
7
7
  }
@@ -1,17 +1,19 @@
1
1
  /**
2
- * order-by.ts — 规范化 QueryOptions.orderBy(DRY + LSP)
2
+ * order-by.ts — 数据库查询排序项(QueryOptions.orderBy)规范化工具
3
3
  *
4
- * SDK 权威形态是 `{ field, dir? }`(见 @sfmc-bds/sdk/db OrderBy);
5
- * tx-runner 早期用过 `{ col, dir? }`。两端必须互通,否则
6
- * `ORDER BY "undefined"` / `orderBy bad column` 静默炸。
4
+ * 兼容处理 SDK 的标准形态 `{ field, dir? }` 与服务端的 `{ col, dir? }` 格式,
5
+ * 统一转化为标准化的 `{ col, dir }[]` 结构,防止由于字段名不匹配导致 SQL 拼出 `ORDER BY "undefined"` 错误。
7
6
  */
8
7
  export type NormalizedOrder = {
9
8
  col: string;
10
9
  dir: "asc" | "desc";
11
10
  };
12
11
  /**
13
- * 把 SDK `field` / 遗留 `col`、单值 / 数组 统一成 `{col,dir}[]`。
14
- * @throws 缺列名或类型非法时
12
+ * 将多形态的 orderBy 配置(单项或数组、`field` 或 `col`)统一规范化为 `{ col, dir }[]`。
13
+ *
14
+ * @param orderBy 原始排序配置项。
15
+ * @returns 规范化的排序描述数组。
16
+ * @throws 当排序项类型非法或缺少列名时抛出异常。
15
17
  */
16
18
  export declare function normalizeOrderBy(orderBy: unknown): NormalizedOrder[];
17
19
  //# sourceMappingURL=order-by.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"order-by.d.ts","sourceRoot":"","sources":["../../src/lib/order-by.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,eAAe,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,KAAK,GAAG,MAAM,CAAA;CAAE,CAAC;AAEnE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,EAAE,CAmBpE"}
1
+ {"version":3,"file":"order-by.d.ts","sourceRoot":"","sources":["../../src/lib/order-by.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,eAAe,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,KAAK,GAAG,MAAM,CAAA;CAAE,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,EAAE,CAoBpE"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/order-by.ts"],
4
- "sourcesContent": ["/**\n * order-by.ts \u2014 \u89C4\u8303\u5316 QueryOptions.orderBy(DRY + LSP)\n *\n * SDK \u6743\u5A01\u5F62\u6001\u662F `{ field, dir? }`(\u89C1 @sfmc-bds/sdk/db OrderBy);\n * tx-runner \u65E9\u671F\u7528\u8FC7 `{ col, dir? }`\u3002\u4E24\u7AEF\u5FC5\u987B\u4E92\u901A,\u5426\u5219\n * `ORDER BY \"undefined\"` / `orderBy bad column` \u9759\u9ED8\u70B8\u3002\n */\n\nexport type NormalizedOrder = { col: string; dir: \"asc\" | \"desc\" };\n\n/**\n * \u628A SDK `field` / \u9057\u7559 `col`\u3001\u5355\u503C / \u6570\u7EC4 \u7EDF\u4E00\u6210 `{col,dir}[]`\u3002\n * @throws \u7F3A\u5217\u540D\u6216\u7C7B\u578B\u975E\u6CD5\u65F6\n */\nexport function normalizeOrderBy(orderBy: unknown): NormalizedOrder[] {\n if (orderBy == null) return [];\n const list = Array.isArray(orderBy) ? orderBy : [orderBy];\n const out: NormalizedOrder[] = [];\n for (const item of list) {\n if (item == null || typeof item !== \"object\") {\n throw new Error(\"[tx] orderBy \u9879\u5FC5\u987B\u662F\u5BF9\u8C61\");\n }\n const rec = item as Record<string, unknown>;\n const col = rec.col ?? rec.field;\n if (typeof col !== \"string\" || !col) {\n throw new Error(\"[tx] orderBy \u7F3A\u5C11 field/col\");\n }\n out.push({\n col,\n dir: rec.dir === \"desc\" ? \"desc\" : \"asc\",\n });\n }\n return out;\n}\n"],
5
- "mappings": "AAcO,SAAS,iBAAiB,SAAqC;AACpE,MAAI,WAAW,KAAM,QAAO,CAAC;AAC7B,QAAM,OAAO,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO;AACxD,QAAM,MAAyB,CAAC;AAChC,aAAW,QAAQ,MAAM;AACvB,QAAI,QAAQ,QAAQ,OAAO,SAAS,UAAU;AAC5C,YAAM,IAAI,MAAM,mDAAqB;AAAA,IACvC;AACA,UAAM,MAAM;AACZ,UAAM,MAAM,IAAI,OAAO,IAAI;AAC3B,QAAI,OAAO,QAAQ,YAAY,CAAC,KAAK;AACnC,YAAM,IAAI,MAAM,qCAA2B;AAAA,IAC7C;AACA,QAAI,KAAK;AAAA,MACP;AAAA,MACA,KAAK,IAAI,QAAQ,SAAS,SAAS;AAAA,IACrC,CAAC;AAAA,EACH;AACA,SAAO;AACT;",
4
+ "sourcesContent": ["/**\n * order-by.ts \u2014 \u6570\u636E\u5E93\u67E5\u8BE2\u6392\u5E8F\u9879\uFF08QueryOptions.orderBy\uFF09\u89C4\u8303\u5316\u5DE5\u5177\n *\n * \u517C\u5BB9\u5904\u7406 SDK \u7684\u6807\u51C6\u5F62\u6001 `{ field, dir? }` \u4E0E\u670D\u52A1\u7AEF\u7684 `{ col, dir? }` \u683C\u5F0F\uFF0C\n * \u7EDF\u4E00\u8F6C\u5316\u4E3A\u6807\u51C6\u5316\u7684 `{ col, dir }[]` \u7ED3\u6784\uFF0C\u9632\u6B62\u7531\u4E8E\u5B57\u6BB5\u540D\u4E0D\u5339\u914D\u5BFC\u81F4 SQL \u62FC\u51FA `ORDER BY \"undefined\"` \u9519\u8BEF\u3002\n */\n\nexport type NormalizedOrder = { col: string; dir: \"asc\" | \"desc\" };\n\n/**\n * \u5C06\u591A\u5F62\u6001\u7684 orderBy \u914D\u7F6E\uFF08\u5355\u9879\u6216\u6570\u7EC4\u3001`field` \u6216 `col`\uFF09\u7EDF\u4E00\u89C4\u8303\u5316\u4E3A `{ col, dir }[]`\u3002\n *\n * @param orderBy \u539F\u59CB\u6392\u5E8F\u914D\u7F6E\u9879\u3002\n * @returns \u89C4\u8303\u5316\u7684\u6392\u5E8F\u63CF\u8FF0\u6570\u7EC4\u3002\n * @throws \u5F53\u6392\u5E8F\u9879\u7C7B\u578B\u975E\u6CD5\u6216\u7F3A\u5C11\u5217\u540D\u65F6\u629B\u51FA\u5F02\u5E38\u3002\n */\nexport function normalizeOrderBy(orderBy: unknown): NormalizedOrder[] {\n\n if (orderBy == null) return [];\n const list = Array.isArray(orderBy) ? orderBy : [orderBy];\n const out: NormalizedOrder[] = [];\n for (const item of list) {\n if (item == null || typeof item !== \"object\") {\n throw new Error(\"[tx] orderBy \u9879\u5FC5\u987B\u662F\u5BF9\u8C61\");\n }\n const rec = item as Record<string, unknown>;\n const col = rec.col ?? rec.field;\n if (typeof col !== \"string\" || !col) {\n throw new Error(\"[tx] orderBy \u7F3A\u5C11 field/col\");\n }\n out.push({\n col,\n dir: rec.dir === \"desc\" ? \"desc\" : \"asc\",\n });\n }\n return out;\n}\n"],
5
+ "mappings": "AAgBO,SAAS,iBAAiB,SAAqC;AAEpE,MAAI,WAAW,KAAM,QAAO,CAAC;AAC7B,QAAM,OAAO,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO;AACxD,QAAM,MAAyB,CAAC;AAChC,aAAW,QAAQ,MAAM;AACvB,QAAI,QAAQ,QAAQ,OAAO,SAAS,UAAU;AAC5C,YAAM,IAAI,MAAM,mDAAqB;AAAA,IACvC;AACA,UAAM,MAAM;AACZ,UAAM,MAAM,IAAI,OAAO,IAAI;AAC3B,QAAI,OAAO,QAAQ,YAAY,CAAC,KAAK;AACnC,YAAM,IAAI,MAAM,qCAA2B;AAAA,IAC7C;AACA,QAAI,KAAK;AAAA,MACP;AAAA,MACA,KAAK,IAAI,QAAQ,SAAS,SAAS;AAAA,IACrC,CAAC;AAAA,EACH;AACA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,31 +1,43 @@
1
1
  /**
2
- * lib/sql-helpers.ts — SQL 构造辅助
2
+ * lib/sql-helpers.ts — SQL 语句构造辅助工具
3
3
  *
4
- * sql-template-strings 会把每一次 ${} 插值都变成 `?` 绑定值。
5
- * 表名/列名等信任标识符必须嵌入 SQL 文本,不能走绑定 —— 否则会出现
6
- * `FROM ?` → `near "?": syntax error`(见 economy 域修复)。
4
+ * `sql-template-strings` 会将所有 `${}` 模板插值自动转为 `?` 参数化绑定值。
5
+ * 数据表名、列名等受信任的标识符不能作为参数绑定,必须直接嵌入 SQL 文本中,
6
+ * 否则会导致 `FROM ?` 语法解析错误(`near "?": syntax error`)。
7
7
  */
8
8
  import { type SQLStatement } from "sql-template-strings";
9
- /** 参数化查询信封,与 createQuery / AnyQuery 的格式 2 对齐 */
9
+ /** 参数化查询对象,与 `createQuery` / `AnyQuery` 的查询输入格式完全对齐。 */
10
10
  export type BoundSql = {
11
11
  sql: string;
12
12
  values: unknown[];
13
13
  };
14
14
  /**
15
- * 构造「标识符已嵌入、值走 ?」的查询。
16
- * 表名等信任常量用模板字符串嵌入;用户输入只放进 values。
15
+ * 构造安全拼接后的参数化查询结构(标识符安全嵌入文本,动态变量作为参数绑定)。
16
+ * 表名等受信任的系统常量可直接通过模板字符串拼接,用户输入必须存入 `values` 数组中。
17
17
  *
18
18
  * @example
19
- * sql(`SELECT * FROM ${TABLE} WHERE id = ?`, [id])
19
+ * ```ts
20
+ * sql(`SELECT * FROM ${TABLE} WHERE id = ?`, [id])
21
+ * ```
22
+ *
23
+ * @param text 包含 `?` 占位符的 SQL 文本。
24
+ * @param values 对应的参数绑定值数组。
25
+ * @returns 构造的参数化查询结构。
20
26
  */
21
27
  export declare function sql(text: string, values?: unknown[]): BoundSql;
22
28
  /**
23
- * 把一段静态 SQL 文本包装成 SQLStatement,仅可与 `.append(...)` 合用。
29
+ * 将一段静态 SQL 片段包装为 `SQLStatement` 对象,专用于配合 `.append(...)` 链式拼接。
30
+ *
31
+ * 注意事项:
32
+ * 切勿写成 `SQL`... FROM ${raw(table)} ...`` —— 模板插值仍会被转为 `?` 占位符。
33
+ * 正确用法:
34
+ * ```ts
35
+ * SQL`SELECT * FROM `.append(raw(TABLE)).append(SQL` WHERE id = ${id}`)
36
+ * ```
37
+ * 推荐在日常业务中优先使用 {@link sql} 函数。
24
38
  *
25
- * 警告(LSP):切勿写成 `SQL\`... FROM ${raw(table)} ...\`` —— 模板插值仍会变成 `?`,
26
- * 与直接插值表名同病。正确用法:
27
- * SQL`SELECT * FROM `.append(raw(TABLE)).append(SQL` WHERE id = ${id}`)
28
- * 纯字符串 `.append(TABLE)` 效果相同;优先用 {@link sql}。
39
+ * @param text 静态 SQL 文本片段。
40
+ * @returns 包装后的 SQLStatement 实例。
29
41
  */
30
42
  export declare function raw(text: string): SQLStatement;
31
43
  //# sourceMappingURL=sql-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sql-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/sql-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE9D,gDAAgD;AAChD,MAAM,MAAM,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,QAAQ,CAElE;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAE9C"}
1
+ {"version":3,"file":"sql-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/sql-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE9D,wDAAwD;AACxD,MAAM,MAAM,QAAQ,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,QAAQ,CAElE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAE9C"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/sql-helpers.ts"],
4
- "sourcesContent": ["/**\n * lib/sql-helpers.ts \u2014 SQL \u6784\u9020\u8F85\u52A9\n *\n * sql-template-strings \u4F1A\u628A\u6BCF\u4E00\u6B21 ${} \u63D2\u503C\u90FD\u53D8\u6210 `?` \u7ED1\u5B9A\u503C\u3002\n * \u8868\u540D/\u5217\u540D\u7B49\u4FE1\u4EFB\u6807\u8BC6\u7B26\u5FC5\u987B\u5D4C\u5165 SQL \u6587\u672C,\u4E0D\u80FD\u8D70\u7ED1\u5B9A \u2014\u2014 \u5426\u5219\u4F1A\u51FA\u73B0\n * `FROM ?` \u2192 `near \"?\": syntax error`(\u89C1 economy \u57DF\u4FEE\u590D)\u3002\n */\n\nimport { SQL, type SQLStatement } from \"sql-template-strings\";\n\n/** \u53C2\u6570\u5316\u67E5\u8BE2\u4FE1\u5C01,\u4E0E createQuery / AnyQuery \u7684\u683C\u5F0F 2 \u5BF9\u9F50 */\nexport type BoundSql = { sql: string; values: unknown[] };\n\n/**\n * \u6784\u9020\u300C\u6807\u8BC6\u7B26\u5DF2\u5D4C\u5165\u3001\u503C\u8D70 ?\u300D\u7684\u67E5\u8BE2\u3002\n * \u8868\u540D\u7B49\u4FE1\u4EFB\u5E38\u91CF\u7528\u6A21\u677F\u5B57\u7B26\u4E32\u5D4C\u5165;\u7528\u6237\u8F93\u5165\u53EA\u653E\u8FDB values\u3002\n *\n * @example\n * sql(`SELECT * FROM ${TABLE} WHERE id = ?`, [id])\n */\nexport function sql(text: string, values: unknown[] = []): BoundSql {\n return { sql: text, values };\n}\n\n/**\n * \u628A\u4E00\u6BB5\u9759\u6001 SQL \u6587\u672C\u5305\u88C5\u6210 SQLStatement,\u4EC5\u53EF\u4E0E `.append(...)` \u5408\u7528\u3002\n *\n * \u8B66\u544A(LSP):\u5207\u52FF\u5199\u6210 `SQL\\`... FROM ${raw(table)} ...\\`` \u2014\u2014 \u6A21\u677F\u63D2\u503C\u4ECD\u4F1A\u53D8\u6210 `?`,\n * \u4E0E\u76F4\u63A5\u63D2\u503C\u8868\u540D\u540C\u75C5\u3002\u6B63\u786E\u7528\u6CD5:\n * SQL`SELECT * FROM `.append(raw(TABLE)).append(SQL` WHERE id = ${id}`)\n * \u7EAF\u5B57\u7B26\u4E32 `.append(TABLE)` \u6548\u679C\u76F8\u540C;\u4F18\u5148\u7528 {@link sql}\u3002\n */\nexport function raw(text: string): SQLStatement {\n return SQL([text]) as SQLStatement;\n}\n"],
5
- "mappings": "AAQA,SAAS,WAA8B;AAYhC,SAAS,IAAI,MAAc,SAAoB,CAAC,GAAa;AAClE,SAAO,EAAE,KAAK,MAAM,OAAO;AAC7B;AAUO,SAAS,IAAI,MAA4B;AAC9C,SAAO,IAAI,CAAC,IAAI,CAAC;AACnB;",
4
+ "sourcesContent": ["/**\n * lib/sql-helpers.ts \u2014 SQL \u8BED\u53E5\u6784\u9020\u8F85\u52A9\u5DE5\u5177\n *\n * `sql-template-strings` \u4F1A\u5C06\u6240\u6709 `${}` \u6A21\u677F\u63D2\u503C\u81EA\u52A8\u8F6C\u4E3A `?` \u53C2\u6570\u5316\u7ED1\u5B9A\u503C\u3002\n * \u6570\u636E\u8868\u540D\u3001\u5217\u540D\u7B49\u53D7\u4FE1\u4EFB\u7684\u6807\u8BC6\u7B26\u4E0D\u80FD\u4F5C\u4E3A\u53C2\u6570\u7ED1\u5B9A\uFF0C\u5FC5\u987B\u76F4\u63A5\u5D4C\u5165 SQL \u6587\u672C\u4E2D\uFF0C\n * \u5426\u5219\u4F1A\u5BFC\u81F4 `FROM ?` \u8BED\u6CD5\u89E3\u6790\u9519\u8BEF\uFF08`near \"?\": syntax error`\uFF09\u3002\n */\n\nimport { SQL, type SQLStatement } from \"sql-template-strings\";\n\n/** \u53C2\u6570\u5316\u67E5\u8BE2\u5BF9\u8C61\uFF0C\u4E0E `createQuery` / `AnyQuery` \u7684\u67E5\u8BE2\u8F93\u5165\u683C\u5F0F\u5B8C\u5168\u5BF9\u9F50\u3002 */\nexport type BoundSql = { sql: string; values: unknown[] };\n\n/**\n * \u6784\u9020\u5B89\u5168\u62FC\u63A5\u540E\u7684\u53C2\u6570\u5316\u67E5\u8BE2\u7ED3\u6784\uFF08\u6807\u8BC6\u7B26\u5B89\u5168\u5D4C\u5165\u6587\u672C\uFF0C\u52A8\u6001\u53D8\u91CF\u4F5C\u4E3A\u53C2\u6570\u7ED1\u5B9A\uFF09\u3002\n * \u8868\u540D\u7B49\u53D7\u4FE1\u4EFB\u7684\u7CFB\u7EDF\u5E38\u91CF\u53EF\u76F4\u63A5\u901A\u8FC7\u6A21\u677F\u5B57\u7B26\u4E32\u62FC\u63A5\uFF0C\u7528\u6237\u8F93\u5165\u5FC5\u987B\u5B58\u5165 `values` \u6570\u7EC4\u4E2D\u3002\n *\n * @example\n * ```ts\n * sql(`SELECT * FROM ${TABLE} WHERE id = ?`, [id])\n * ```\n *\n * @param text \u5305\u542B `?` \u5360\u4F4D\u7B26\u7684 SQL \u6587\u672C\u3002\n * @param values \u5BF9\u5E94\u7684\u53C2\u6570\u7ED1\u5B9A\u503C\u6570\u7EC4\u3002\n * @returns \u6784\u9020\u7684\u53C2\u6570\u5316\u67E5\u8BE2\u7ED3\u6784\u3002\n */\nexport function sql(text: string, values: unknown[] = []): BoundSql {\n return { sql: text, values };\n}\n\n/**\n * \u5C06\u4E00\u6BB5\u9759\u6001 SQL \u7247\u6BB5\u5305\u88C5\u4E3A `SQLStatement` \u5BF9\u8C61\uFF0C\u4E13\u7528\u4E8E\u914D\u5408 `.append(...)` \u94FE\u5F0F\u62FC\u63A5\u3002\n *\n * \u6CE8\u610F\u4E8B\u9879\uFF1A\n * \u5207\u52FF\u5199\u6210 `SQL`... FROM ${raw(table)} ...`` \u2014\u2014 \u6A21\u677F\u63D2\u503C\u4ECD\u4F1A\u88AB\u8F6C\u4E3A `?` \u5360\u4F4D\u7B26\u3002\n * \u6B63\u786E\u7528\u6CD5\uFF1A\n * ```ts\n * SQL`SELECT * FROM `.append(raw(TABLE)).append(SQL` WHERE id = ${id}`)\n * ```\n * \u63A8\u8350\u5728\u65E5\u5E38\u4E1A\u52A1\u4E2D\u4F18\u5148\u4F7F\u7528 {@link sql} \u51FD\u6570\u3002\n *\n * @param text \u9759\u6001 SQL \u6587\u672C\u7247\u6BB5\u3002\n * @returns \u5305\u88C5\u540E\u7684 SQLStatement \u5B9E\u4F8B\u3002\n */\nexport function raw(text: string): SQLStatement {\n return SQL([text]) as SQLStatement;\n}\n\n"],
5
+ "mappings": "AAQA,SAAS,WAA8B;AAkBhC,SAAS,IAAI,MAAc,SAAoB,CAAC,GAAa;AAClE,SAAO,EAAE,KAAK,MAAM,OAAO;AAC7B;AAgBO,SAAS,IAAI,MAA4B;AAC9C,SAAO,IAAI,CAAC,IAAI,CAAC;AACnB;",
6
6
  "names": []
7
7
  }
@@ -7,14 +7,23 @@ export type QueryFn = (sql: string | SQLStatement | {
7
7
  changes: number | bigint;
8
8
  };
9
9
  /**
10
- * 统一 SQL 查询接口 — 支持三种调用格式:
11
- * 1. query("SELECT * FROM t WHERE id=?", [1])
12
- * 2. query({ sql: "SELECT * FROM t WHERE id=?", values: [1] })
13
- * 3. query(SQL`SELECT * FROM t WHERE id=${1}`) ← sql-template-strings
10
+ * 创建统一的 SQL 查询接口(内置 LRU 预编译语句缓存机制)。
11
+ * 支持三种调用形态:
12
+ * 1. `query("SELECT * FROM t WHERE id = ?", [1])`
13
+ * 2. `query({ sql: "SELECT * FROM t WHERE id = ?", values: [1] })`
14
+ * 3. `query(SQL`SELECT * FROM t WHERE id = ${1}`)`(sql-template-strings)
15
+ *
16
+ * @param db SQLite 数据库连接实例。
17
+ * @param maxStatements 预编译语句缓存上限(默认为 200)。
18
+ * @returns 统一查询执行函数。
14
19
  */
15
20
  export declare function createQuery(db: DatabaseSync, maxStatements?: number): QueryFn;
16
21
  /**
17
- * 打开 SQLite 数据库(同步模式),启用外键、WAL、busy_timeout
22
+ * 打开 SQLite 数据库文件(基于 Node.js 原生 DatabaseSync 同步模块)。
23
+ * 自动递归创建所在父级目录,并初始化配置外键约束(`foreign_keys = ON`)、预写日志(`journal_mode = WAL`)与忙等待超时(`busy_timeout = 5000`)。
24
+ *
25
+ * @param filePath 数据库文件绝对路径。
26
+ * @returns SQLite DatabaseSync 连接实例。
18
27
  */
19
28
  export declare function openDatabase(filePath: string): DatabaseSync;
20
29
  //# sourceMappingURL=sqlite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/lib/sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,EAChE,MAAM,CAAC,EAAE,OAAO,EAAE,KACf,OAAO,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAE9C;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,YAAY,EAAE,aAAa,GAAE,MAAY,GAAG,OAAO,CAuClF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAO3D"}
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/lib/sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,EAChE,MAAM,CAAC,EAAE,OAAO,EAAE,KACf,OAAO,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,YAAY,EAAE,aAAa,GAAE,MAAY,GAAG,OAAO,CAwClF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAO3D"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/sqlite.ts"],
4
- "sourcesContent": ["import { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { DatabaseSync, StatementSync } from \"node:sqlite\";\nimport type { SQLStatement } from \"sql-template-strings\";\n\nexport type QueryFn = (\n sql: string | SQLStatement | { sql: string; values?: unknown[] },\n values?: unknown[]\n) => unknown[] | { changes: number | bigint };\n\n/**\n * \u7EDF\u4E00 SQL \u67E5\u8BE2\u63A5\u53E3 \u2014 \u652F\u6301\u4E09\u79CD\u8C03\u7528\u683C\u5F0F\uFF1A\n * 1. query(\"SELECT * FROM t WHERE id=?\", [1])\n * 2. query({ sql: \"SELECT * FROM t WHERE id=?\", values: [1] })\n * 3. query(SQL`SELECT * FROM t WHERE id=${1}`) \u2190 sql-template-strings\n */\nexport function createQuery(db: DatabaseSync, maxStatements: number = 200): QueryFn {\n const stmts = new Map<string, StatementSync>();\n\n function getStmt(sql: string): StatementSync {\n let s = stmts.get(sql);\n if (!s) {\n s = db.prepare(sql);\n if (stmts.size >= maxStatements) {\n const first = stmts.keys().next().value;\n if (first !== undefined) stmts.delete(first);\n }\n stmts.set(sql, s);\n }\n return s;\n }\n\n function runQuery(sql: string, values: unknown[]): any[] | { changes: number | bigint } {\n const s = getStmt(sql);\n const upper = sql.trim().toUpperCase();\n const needsRows =\n upper.startsWith(\"SELECT\") ||\n upper.startsWith(\"WITH\") ||\n upper.startsWith(\"PRAGMA\") ||\n /\\bRETURNING\\b/.test(upper);\n const bind = values as any[];\n if (needsRows) return s.all(...bind);\n const r = s.run(...bind);\n return { changes: r.changes };\n }\n\n return function query(raw: string | { sql: string; values?: unknown[] }, maybeValues?: unknown[]): any[] | { changes: number | bigint } {\n // \u683C\u5F0F 1\uFF1Astring + values\n if (typeof raw === \"string\") {\n return runQuery(raw, maybeValues ?? []);\n }\n // \u683C\u5F0F 2 / 3\uFF1A{ sql, values } \u6216 sql-template-strings \u5BF9\u8C61\n const o = raw as { sql: string; values?: unknown[] };\n return runQuery(o.sql, o.values ?? []);\n };\n}\n\n/**\n * \u6253\u5F00 SQLite \u6570\u636E\u5E93\uFF08\u540C\u6B65\u6A21\u5F0F\uFF09\uFF0C\u542F\u7528\u5916\u952E\u3001WAL\u3001busy_timeout\n */\nexport function openDatabase(filePath: string): DatabaseSync {\n mkdirSync(dirname(filePath), { recursive: true });\n const db = new DatabaseSync(filePath);\n db.exec(\"PRAGMA foreign_keys = ON\");\n db.exec(\"PRAGMA journal_mode = WAL\");\n db.exec(\"PRAGMA busy_timeout = 5000\");\n return db;\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,cAAc,qBAAqB;AAcrC,SAAS,YAAY,IAAkB,gBAAwB,KAAc;AAClF,QAAM,QAAQ,oBAAI,IAA2B;AAE7C,WAAS,QAAQ,KAA4B;AAC3C,QAAI,IAAI,MAAM,IAAI,GAAG;AACrB,QAAI,CAAC,GAAG;AACN,UAAI,GAAG,QAAQ,GAAG;AAClB,UAAI,MAAM,QAAQ,eAAe;AAC/B,cAAM,QAAQ,MAAM,KAAK,EAAE,KAAK,EAAE;AAClC,YAAI,UAAU,OAAW,OAAM,OAAO,KAAK;AAAA,MAC7C;AACA,YAAM,IAAI,KAAK,CAAC;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,KAAa,QAAyD;AACtF,UAAM,IAAI,QAAQ,GAAG;AACrB,UAAM,QAAQ,IAAI,KAAK,EAAE,YAAY;AACrC,UAAM,YACJ,MAAM,WAAW,QAAQ,KACzB,MAAM,WAAW,MAAM,KACvB,MAAM,WAAW,QAAQ,KACzB,gBAAgB,KAAK,KAAK;AAC5B,UAAM,OAAO;AACb,QAAI,UAAW,QAAO,EAAE,IAAI,GAAG,IAAI;AACnC,UAAM,IAAI,EAAE,IAAI,GAAG,IAAI;AACvB,WAAO,EAAE,SAAS,EAAE,QAAQ;AAAA,EAC9B;AAEA,SAAO,SAAS,MAAM,KAAmD,aAA+D;AAEtI,QAAI,OAAO,QAAQ,UAAU;AAC3B,aAAO,SAAS,KAAK,eAAe,CAAC,CAAC;AAAA,IACxC;AAEA,UAAM,IAAI;AACV,WAAO,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;AAAA,EACvC;AACF;AAKO,SAAS,aAAa,UAAgC;AAC3D,YAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,QAAM,KAAK,IAAI,aAAa,QAAQ;AACpC,KAAG,KAAK,0BAA0B;AAClC,KAAG,KAAK,2BAA2B;AACnC,KAAG,KAAK,4BAA4B;AACpC,SAAO;AACT;",
4
+ "sourcesContent": ["import { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { DatabaseSync, StatementSync } from \"node:sqlite\";\nimport type { SQLStatement } from \"sql-template-strings\";\n\nexport type QueryFn = (\n sql: string | SQLStatement | { sql: string; values?: unknown[] },\n values?: unknown[]\n) => unknown[] | { changes: number | bigint };\n\n/**\n * \u521B\u5EFA\u7EDF\u4E00\u7684 SQL \u67E5\u8BE2\u63A5\u53E3\uFF08\u5185\u7F6E LRU \u9884\u7F16\u8BD1\u8BED\u53E5\u7F13\u5B58\u673A\u5236\uFF09\u3002\n * \u652F\u6301\u4E09\u79CD\u8C03\u7528\u5F62\u6001\uFF1A\n * 1. `query(\"SELECT * FROM t WHERE id = ?\", [1])`\n * 2. `query({ sql: \"SELECT * FROM t WHERE id = ?\", values: [1] })`\n * 3. `query(SQL`SELECT * FROM t WHERE id = ${1}`)`\uFF08sql-template-strings\uFF09\n *\n * @param db SQLite \u6570\u636E\u5E93\u8FDE\u63A5\u5B9E\u4F8B\u3002\n * @param maxStatements \u9884\u7F16\u8BD1\u8BED\u53E5\u7F13\u5B58\u4E0A\u9650\uFF08\u9ED8\u8BA4\u4E3A 200\uFF09\u3002\n * @returns \u7EDF\u4E00\u67E5\u8BE2\u6267\u884C\u51FD\u6570\u3002\n */\nexport function createQuery(db: DatabaseSync, maxStatements: number = 200): QueryFn {\n\n const stmts = new Map<string, StatementSync>();\n\n function getStmt(sql: string): StatementSync {\n let s = stmts.get(sql);\n if (!s) {\n s = db.prepare(sql);\n if (stmts.size >= maxStatements) {\n const first = stmts.keys().next().value;\n if (first !== undefined) stmts.delete(first);\n }\n stmts.set(sql, s);\n }\n return s;\n }\n\n function runQuery(sql: string, values: unknown[]): any[] | { changes: number | bigint } {\n const s = getStmt(sql);\n const upper = sql.trim().toUpperCase();\n const needsRows =\n upper.startsWith(\"SELECT\") ||\n upper.startsWith(\"WITH\") ||\n upper.startsWith(\"PRAGMA\") ||\n /\\bRETURNING\\b/.test(upper);\n const bind = values as any[];\n if (needsRows) return s.all(...bind);\n const r = s.run(...bind);\n return { changes: r.changes };\n }\n\n return function query(raw: string | { sql: string; values?: unknown[] }, maybeValues?: unknown[]): any[] | { changes: number | bigint } {\n // \u683C\u5F0F 1\uFF1Astring + values\n if (typeof raw === \"string\") {\n return runQuery(raw, maybeValues ?? []);\n }\n // \u683C\u5F0F 2 / 3\uFF1A{ sql, values } \u6216 sql-template-strings \u5BF9\u8C61\n const o = raw as { sql: string; values?: unknown[] };\n return runQuery(o.sql, o.values ?? []);\n };\n}\n\n/**\n * \u6253\u5F00 SQLite \u6570\u636E\u5E93\u6587\u4EF6\uFF08\u57FA\u4E8E Node.js \u539F\u751F DatabaseSync \u540C\u6B65\u6A21\u5757\uFF09\u3002\n * \u81EA\u52A8\u9012\u5F52\u521B\u5EFA\u6240\u5728\u7236\u7EA7\u76EE\u5F55\uFF0C\u5E76\u521D\u59CB\u5316\u914D\u7F6E\u5916\u952E\u7EA6\u675F\uFF08`foreign_keys = ON`\uFF09\u3001\u9884\u5199\u65E5\u5FD7\uFF08`journal_mode = WAL`\uFF09\u4E0E\u5FD9\u7B49\u5F85\u8D85\u65F6\uFF08`busy_timeout = 5000`\uFF09\u3002\n *\n * @param filePath \u6570\u636E\u5E93\u6587\u4EF6\u7EDD\u5BF9\u8DEF\u5F84\u3002\n * @returns SQLite DatabaseSync \u8FDE\u63A5\u5B9E\u4F8B\u3002\n */\nexport function openDatabase(filePath: string): DatabaseSync {\n mkdirSync(dirname(filePath), { recursive: true });\n const db = new DatabaseSync(filePath);\n db.exec(\"PRAGMA foreign_keys = ON\");\n db.exec(\"PRAGMA journal_mode = WAL\");\n db.exec(\"PRAGMA busy_timeout = 5000\");\n return db;\n}\n\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,cAAc,qBAAqB;AAmBrC,SAAS,YAAY,IAAkB,gBAAwB,KAAc;AAElF,QAAM,QAAQ,oBAAI,IAA2B;AAE7C,WAAS,QAAQ,KAA4B;AAC3C,QAAI,IAAI,MAAM,IAAI,GAAG;AACrB,QAAI,CAAC,GAAG;AACN,UAAI,GAAG,QAAQ,GAAG;AAClB,UAAI,MAAM,QAAQ,eAAe;AAC/B,cAAM,QAAQ,MAAM,KAAK,EAAE,KAAK,EAAE;AAClC,YAAI,UAAU,OAAW,OAAM,OAAO,KAAK;AAAA,MAC7C;AACA,YAAM,IAAI,KAAK,CAAC;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,KAAa,QAAyD;AACtF,UAAM,IAAI,QAAQ,GAAG;AACrB,UAAM,QAAQ,IAAI,KAAK,EAAE,YAAY;AACrC,UAAM,YACJ,MAAM,WAAW,QAAQ,KACzB,MAAM,WAAW,MAAM,KACvB,MAAM,WAAW,QAAQ,KACzB,gBAAgB,KAAK,KAAK;AAC5B,UAAM,OAAO;AACb,QAAI,UAAW,QAAO,EAAE,IAAI,GAAG,IAAI;AACnC,UAAM,IAAI,EAAE,IAAI,GAAG,IAAI;AACvB,WAAO,EAAE,SAAS,EAAE,QAAQ;AAAA,EAC9B;AAEA,SAAO,SAAS,MAAM,KAAmD,aAA+D;AAEtI,QAAI,OAAO,QAAQ,UAAU;AAC3B,aAAO,SAAS,KAAK,eAAe,CAAC,CAAC;AAAA,IACxC;AAEA,UAAM,IAAI;AACV,WAAO,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;AAAA,EACvC;AACF;AASO,SAAS,aAAa,UAAgC;AAC3D,YAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,QAAM,KAAK,IAAI,aAAa,QAAQ;AACpC,KAAG,KAAK,0BAA0B;AAClC,KAAG,KAAK,2BAA2B;AACnC,KAAG,KAAK,4BAA4B;AACpC,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -6,7 +6,6 @@
6
6
  * "schemaVersion": 2,
7
7
  * "id": "feature-land",
8
8
  * "name": "领地",
9
- * "type": "feature",
10
9
  * "configKey": "land",
11
10
  * "requires": ["feature-economy"],
12
11
  * "permissions": ["db:read:lands", "service:economy.account"],
@@ -36,7 +35,6 @@ export interface ModuleManifestV2 {
36
35
  schemaVersion: 2;
37
36
  id: string;
38
37
  name: string;
39
- type: "core" | "feature";
40
38
  configKey: string;
41
39
  requires: string[];
42
40
  permissions: string[];
@@ -44,6 +42,8 @@ export interface ModuleManifestV2 {
44
42
  provides: ServiceEntry[];
45
43
  requires: ServiceEntry[];
46
44
  };
45
+ enabledByDefault?: boolean | undefined;
46
+ canDisable?: boolean | undefined;
47
47
  notes?: string | undefined;
48
48
  }
49
49
  /** v1 / 缺 schemaVersion:跳过(直到所有模块迁 v2) */
@@ -59,6 +59,9 @@ export interface LoadedManifests {
59
59
  /**
60
60
  * 启动期调。throw = 启动失败。
61
61
  * 注:不读 v1 manifest。只有 schemaVersion=2 才走。
62
+ *
63
+ * 形状校验:核心字段句式可与 `@sfmc-bds/sdk/validation` 对齐;本加载器另有
64
+ * 弃用字段拒绝、service 冲突、权限门等域规则,故暂未整段委托 validateManifest。
62
65
  */
63
66
  export declare function loadManifestV2(packagesDir?: string): LoadedManifests;
64
67
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAOH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACtC,MAAM,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACxC;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,CAAC,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE;QAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IACjE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAKD,0CAA0C;AAC1C,qBAAa,oBAAqB,SAAQ,KAAK;IAC1B,QAAQ,EAAE,MAAM;IAAS,eAAe,EAAE,OAAO;IAApE,YAAmB,QAAQ,EAAE,MAAM,EAAS,eAAe,EAAE,OAAO,EAEnE;CACF;AA8FD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC1C,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,GAAE,MAA6B,GAAG,eAAe,CA8D1F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAMlC"}
1
+ {"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACtC,MAAM,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CACxC;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,CAAC,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE;QAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;QAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;IACjE,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAID,0CAA0C;AAC1C,qBAAa,oBAAqB,SAAQ,KAAK;IAC1B,QAAQ,EAAE,MAAM;IAAS,eAAe,EAAE,OAAO;IAApE,YAAmB,QAAQ,EAAE,MAAM,EAAS,eAAe,EAAE,OAAO,EAEnE;CACF;AAkGD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC1C,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,WAAW,GAAE,MAA6B,GAAG,eAAe,CAqE1F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,eAAe,EACvB,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAMlC"}
@@ -1,8 +1,7 @@
1
- import { existsSync, readdirSync, readFileSync } from "node:fs";
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { defaultPackagesDir } from "./manifest.js";
4
4
  import { validateManifestPermissions } from "./permission-gate.js";
5
- const VALID_TYPES = /* @__PURE__ */ new Set(["core", "feature"]);
6
5
  const IDENT = /^[A-Za-z0-9_-]+$/;
7
6
  class ModuleV1SkippedError extends Error {
8
7
  constructor(moduleId, declaredVersion) {
@@ -26,8 +25,11 @@ function assertModuleObject(modId, obj) {
26
25
  if (typeof obj.name !== "string" || obj.name.length === 0) {
27
26
  throw new Error(`[manifest] ${modId}: name \u7F3A\u5931`);
28
27
  }
29
- if (typeof obj.type !== "string" || !VALID_TYPES.has(obj.type)) {
30
- throw new Error(`[manifest] ${modId}: type \u5FC5\u987B\u662F core|feature ("${String(obj.type)}")`);
28
+ if (obj.enabledByDefault !== void 0 && typeof obj.enabledByDefault !== "boolean") {
29
+ throw new Error(`[manifest] ${modId}: enabledByDefault \u5FC5\u987B\u662F boolean`);
30
+ }
31
+ if (obj.canDisable !== void 0 && typeof obj.canDisable !== "boolean") {
32
+ throw new Error(`[manifest] ${modId}: canDisable \u5FC5\u987B\u662F boolean`);
31
33
  }
32
34
  if (typeof obj.configKey !== "string" || obj.configKey.length === 0) {
33
35
  throw new Error(`[manifest] ${modId}: configKey \u7F3A\u5931`);
@@ -93,11 +95,12 @@ function parseMod(modId, raw) {
93
95
  schemaVersion: 2,
94
96
  id: raw.id,
95
97
  name: raw.name,
96
- type: raw.type,
97
98
  configKey: raw.configKey,
98
99
  requires,
99
100
  permissions,
100
101
  services: parseServices(modId, raw.services),
102
+ ...typeof raw.enabledByDefault === "boolean" ? { enabledByDefault: raw.enabledByDefault } : {},
103
+ ...typeof raw.canDisable === "boolean" ? { canDisable: raw.canDisable } : {},
101
104
  notes: typeof raw.notes === "string" ? raw.notes : void 0
102
105
  };
103
106
  }
@@ -105,7 +108,14 @@ function loadManifestV2(packagesDir = defaultPackagesDir()) {
105
108
  if (!existsSync(packagesDir)) {
106
109
  throw new Error(`[manifest] ${packagesDir} \u4E0D\u5B58\u5728;\u65E0\u6CD5\u542F\u52A8 v2 \u534F\u8BAE`);
107
110
  }
108
- const ids = readdirSync(packagesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
111
+ const ids = readdirSync(packagesDir, { withFileTypes: true }).filter((e) => {
112
+ if (e.name.startsWith(".")) return false;
113
+ try {
114
+ return statSync(resolve(packagesDir, e.name)).isDirectory();
115
+ } catch {
116
+ return false;
117
+ }
118
+ }).map((e) => e.name);
109
119
  const modules = {};
110
120
  const providesMap = /* @__PURE__ */ new Map();
111
121
  for (const id of ids) {