@usethink/cf-core 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +88 -0
  2. package/dist/features/anti-abuse/index.d.ts +62 -0
  3. package/dist/features/anti-abuse/index.d.ts.map +1 -0
  4. package/dist/features/anti-abuse/index.js +139 -0
  5. package/dist/features/anti-abuse/index.js.map +1 -0
  6. package/dist/features/email/index.d.ts +68 -0
  7. package/dist/features/email/index.d.ts.map +1 -0
  8. package/dist/features/email/index.js +120 -0
  9. package/dist/features/email/index.js.map +1 -0
  10. package/dist/features/payment/fetch-utils.d.ts +17 -0
  11. package/dist/features/payment/fetch-utils.d.ts.map +1 -0
  12. package/dist/features/payment/fetch-utils.js +56 -0
  13. package/dist/features/payment/fetch-utils.js.map +1 -0
  14. package/dist/features/payment/index.d.ts +20 -0
  15. package/dist/features/payment/index.d.ts.map +1 -0
  16. package/dist/features/payment/index.js +21 -0
  17. package/dist/features/payment/index.js.map +1 -0
  18. package/dist/features/payment/providers/alipay.d.ts +30 -0
  19. package/dist/features/payment/providers/alipay.d.ts.map +1 -0
  20. package/dist/features/payment/providers/alipay.js +149 -0
  21. package/dist/features/payment/providers/alipay.js.map +1 -0
  22. package/dist/features/payment/providers/stripe.d.ts +34 -0
  23. package/dist/features/payment/providers/stripe.d.ts.map +1 -0
  24. package/dist/features/payment/providers/stripe.js +168 -0
  25. package/dist/features/payment/providers/stripe.js.map +1 -0
  26. package/dist/features/payment/providers/trc20.d.ts +24 -0
  27. package/dist/features/payment/providers/trc20.d.ts.map +1 -0
  28. package/dist/features/payment/providers/trc20.js +96 -0
  29. package/dist/features/payment/providers/trc20.js.map +1 -0
  30. package/dist/features/payment/registry.d.ts +43 -0
  31. package/dist/features/payment/registry.d.ts.map +1 -0
  32. package/dist/features/payment/registry.js +65 -0
  33. package/dist/features/payment/registry.js.map +1 -0
  34. package/dist/features/payment/types.d.ts +72 -0
  35. package/dist/features/payment/types.d.ts.map +1 -0
  36. package/dist/features/payment/types.js +8 -0
  37. package/dist/features/payment/types.js.map +1 -0
  38. package/dist/features/thompson-router/index.d.ts +101 -0
  39. package/dist/features/thompson-router/index.d.ts.map +1 -0
  40. package/dist/features/thompson-router/index.js +186 -0
  41. package/dist/features/thompson-router/index.js.map +1 -0
  42. package/dist/features/webhook/index.d.ts +76 -0
  43. package/dist/features/webhook/index.d.ts.map +1 -0
  44. package/dist/features/webhook/index.js +127 -0
  45. package/dist/features/webhook/index.js.map +1 -0
  46. package/dist/src/audit.d.ts +45 -0
  47. package/dist/src/audit.d.ts.map +1 -0
  48. package/dist/src/audit.js +40 -0
  49. package/dist/src/audit.js.map +1 -0
  50. package/dist/src/auth/jwt.d.ts +33 -0
  51. package/dist/src/auth/jwt.d.ts.map +1 -0
  52. package/dist/src/auth/jwt.js +87 -0
  53. package/dist/src/auth/jwt.js.map +1 -0
  54. package/dist/src/auth/password.d.ts +26 -0
  55. package/dist/src/auth/password.d.ts.map +1 -0
  56. package/dist/src/auth/password.js +52 -0
  57. package/dist/src/auth/password.js.map +1 -0
  58. package/dist/src/bootstrap.d.ts +74 -0
  59. package/dist/src/bootstrap.d.ts.map +1 -0
  60. package/dist/src/bootstrap.js +231 -0
  61. package/dist/src/bootstrap.js.map +1 -0
  62. package/dist/src/cache.d.ts +52 -0
  63. package/dist/src/cache.d.ts.map +1 -0
  64. package/dist/src/cache.js +76 -0
  65. package/dist/src/cache.js.map +1 -0
  66. package/dist/src/config.d.ts +83 -0
  67. package/dist/src/config.d.ts.map +1 -0
  68. package/dist/src/config.js +96 -0
  69. package/dist/src/config.js.map +1 -0
  70. package/dist/src/crypto.d.ts +33 -0
  71. package/dist/src/crypto.d.ts.map +1 -0
  72. package/dist/src/crypto.js +87 -0
  73. package/dist/src/crypto.js.map +1 -0
  74. package/dist/src/db/connection.d.ts +53 -0
  75. package/dist/src/db/connection.d.ts.map +1 -0
  76. package/dist/src/db/connection.js +104 -0
  77. package/dist/src/db/connection.js.map +1 -0
  78. package/dist/src/db/index.d.ts +6 -0
  79. package/dist/src/db/index.d.ts.map +1 -0
  80. package/dist/src/db/index.js +6 -0
  81. package/dist/src/db/index.js.map +1 -0
  82. package/dist/src/db/schema.d.ts +649 -0
  83. package/dist/src/db/schema.d.ts.map +1 -0
  84. package/dist/src/db/schema.js +76 -0
  85. package/dist/src/db/schema.js.map +1 -0
  86. package/dist/src/error.d.ts +47 -0
  87. package/dist/src/error.d.ts.map +1 -0
  88. package/dist/src/error.js +94 -0
  89. package/dist/src/error.js.map +1 -0
  90. package/dist/src/http.d.ts +83 -0
  91. package/dist/src/http.d.ts.map +1 -0
  92. package/dist/src/http.js +116 -0
  93. package/dist/src/http.js.map +1 -0
  94. package/dist/src/idempotency.d.ts +78 -0
  95. package/dist/src/idempotency.d.ts.map +1 -0
  96. package/dist/src/idempotency.js +84 -0
  97. package/dist/src/idempotency.js.map +1 -0
  98. package/dist/src/index.d.ts +31 -0
  99. package/dist/src/index.d.ts.map +1 -0
  100. package/dist/src/index.js +45 -0
  101. package/dist/src/index.js.map +1 -0
  102. package/dist/src/logger.d.ts +31 -0
  103. package/dist/src/logger.d.ts.map +1 -0
  104. package/dist/src/logger.js +45 -0
  105. package/dist/src/logger.js.map +1 -0
  106. package/dist/src/middleware/admin-auth.d.ts +38 -0
  107. package/dist/src/middleware/admin-auth.d.ts.map +1 -0
  108. package/dist/src/middleware/admin-auth.js +55 -0
  109. package/dist/src/middleware/admin-auth.js.map +1 -0
  110. package/dist/src/middleware/api-key-auth.d.ts +42 -0
  111. package/dist/src/middleware/api-key-auth.d.ts.map +1 -0
  112. package/dist/src/middleware/api-key-auth.js +104 -0
  113. package/dist/src/middleware/api-key-auth.js.map +1 -0
  114. package/dist/src/middleware/index.d.ts +3 -0
  115. package/dist/src/middleware/index.d.ts.map +1 -0
  116. package/dist/src/middleware/index.js +3 -0
  117. package/dist/src/middleware/index.js.map +1 -0
  118. package/dist/src/rate-limit.d.ts +54 -0
  119. package/dist/src/rate-limit.d.ts.map +1 -0
  120. package/dist/src/rate-limit.js +134 -0
  121. package/dist/src/rate-limit.js.map +1 -0
  122. package/dist/src/security.d.ts +78 -0
  123. package/dist/src/security.d.ts.map +1 -0
  124. package/dist/src/security.js +175 -0
  125. package/dist/src/security.js.map +1 -0
  126. package/dist/src/types.d.ts +64 -0
  127. package/dist/src/types.d.ts.map +1 -0
  128. package/dist/src/types.js +8 -0
  129. package/dist/src/types.js.map +1 -0
  130. package/features/anti-abuse/index.ts +180 -0
  131. package/features/anti-abuse/tests/index.test.ts +50 -0
  132. package/features/email/index.ts +172 -0
  133. package/features/email/tests/index.test.ts +44 -0
  134. package/features/payment/fetch-utils.ts +65 -0
  135. package/features/payment/index.ts +39 -0
  136. package/features/payment/providers/alipay.ts +171 -0
  137. package/features/payment/providers/stripe.ts +192 -0
  138. package/features/payment/providers/trc20.ts +115 -0
  139. package/features/payment/registry.ts +87 -0
  140. package/features/payment/tests/index.test.ts +506 -0
  141. package/features/payment/types.ts +93 -0
  142. package/features/telegram-miniapp/index.ts +109 -0
  143. package/features/telegram-miniapp/tests/index.test.ts +11 -0
  144. package/features/thompson-router/index.ts +243 -0
  145. package/features/thompson-router/tests/index.test.ts +93 -0
  146. package/features/webhook/index.ts +183 -0
  147. package/features/webhook/tests/index.test.ts +21 -0
  148. package/package.json +202 -0
  149. package/src/audit.ts +70 -0
  150. package/src/auth/jwt.ts +114 -0
  151. package/src/auth/password.ts +75 -0
  152. package/src/bootstrap.ts +322 -0
  153. package/src/cache.ts +78 -0
  154. package/src/config.ts +134 -0
  155. package/src/crypto.ts +106 -0
  156. package/src/db/connection.ts +127 -0
  157. package/src/db/index.ts +6 -0
  158. package/src/db/schema.ts +90 -0
  159. package/src/error.ts +125 -0
  160. package/src/http.ts +150 -0
  161. package/src/idempotency.ts +127 -0
  162. package/src/index.ts +85 -0
  163. package/src/logger.ts +63 -0
  164. package/src/middleware/admin-auth.ts +71 -0
  165. package/src/middleware/api-key-auth.ts +164 -0
  166. package/src/middleware/index.ts +2 -0
  167. package/src/rate-limit.ts +167 -0
  168. package/src/security.ts +219 -0
  169. package/src/types.ts +70 -0
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Workers Cache API 封装
3
+ *
4
+ * Cloudflare Free 套餐的 Cache API 完全免费,不计入 10 万次/天的请求限制。
5
+ * 这是 Free 套餐下唯一能免费扩容的手段,必须充分利用。
6
+ *
7
+ * 使用场景:
8
+ * - GET /products(商品列表,TTL 5 分钟)
9
+ * - GET /system-config(系统配置,TTL 30 分钟)
10
+ * - 任何读多写少的 API 响应
11
+ *
12
+ * 来源:eshop src/lib/cache.ts
13
+ */
14
+ /**
15
+ * 创建命名空间化的 Cache 实例。
16
+ *
17
+ * @param namespace - 缓存命名空间(通常为项目名,如 "eshop-v1")
18
+ */
19
+ export declare function createCache(namespace: string): {
20
+ /**
21
+ * 从 Cache API 读取
22
+ */
23
+ get(cacheKey: string): Promise<Response | null>;
24
+ /**
25
+ * 写入 Cache API
26
+ */
27
+ put(cacheKey: string, response: Response, ttlSeconds: number): Promise<void>;
28
+ /**
29
+ * 删除缓存
30
+ */
31
+ delete(cacheKey: string): Promise<boolean>;
32
+ /** 缓存键生成器(暴露供外部使用) */
33
+ key: (path: string, query?: string) => string;
34
+ };
35
+ /** 默认实例(无命名空间,适用于单项目场景) */
36
+ export declare const cache: {
37
+ /**
38
+ * 从 Cache API 读取
39
+ */
40
+ get(cacheKey: string): Promise<Response | null>;
41
+ /**
42
+ * 写入 Cache API
43
+ */
44
+ put(cacheKey: string, response: Response, ttlSeconds: number): Promise<void>;
45
+ /**
46
+ * 删除缓存
47
+ */
48
+ delete(cacheKey: string): Promise<boolean>;
49
+ /** 缓存键生成器(暴露供外部使用) */
50
+ key: (path: string, query?: string) => string;
51
+ };
52
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM;IAOzC;;OAEG;kBACiB,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAUrD;;OAEG;kBACiB,MAAM,YAAY,QAAQ,cAAc,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBlF;;OAEG;qBACoB,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAShD,sBAAsB;gBAnDL,MAAM,UAAU,MAAM,KAAG,MAAM;EAsDnD;AAED,2BAA2B;AAC3B,eAAO,MAAM,KAAK;IAnDd;;OAEG;kBACiB,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAUrD;;OAEG;kBACiB,MAAM,YAAY,QAAQ,cAAc,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBlF;;OAEG;qBACoB,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAShD,sBAAsB;gBAnDL,MAAM,UAAU,MAAM,KAAG,MAAM;CAyDT,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Workers Cache API 封装
3
+ *
4
+ * Cloudflare Free 套餐的 Cache API 完全免费,不计入 10 万次/天的请求限制。
5
+ * 这是 Free 套餐下唯一能免费扩容的手段,必须充分利用。
6
+ *
7
+ * 使用场景:
8
+ * - GET /products(商品列表,TTL 5 分钟)
9
+ * - GET /system-config(系统配置,TTL 30 分钟)
10
+ * - 任何读多写少的 API 响应
11
+ *
12
+ * 来源:eshop src/lib/cache.ts
13
+ */
14
+ /**
15
+ * 创建命名空间化的 Cache 实例。
16
+ *
17
+ * @param namespace - 缓存命名空间(通常为项目名,如 "eshop-v1")
18
+ */
19
+ export function createCache(namespace) {
20
+ function key(path, query) {
21
+ const base = `https://cache.local/${namespace}${path}`;
22
+ return query ? `${base}?${query}` : base;
23
+ }
24
+ return {
25
+ /**
26
+ * 从 Cache API 读取
27
+ */
28
+ async get(cacheKey) {
29
+ try {
30
+ const match = await caches.default.match(cacheKey);
31
+ return match || null;
32
+ }
33
+ catch (err) {
34
+ console.warn("[cache] get failed:", err);
35
+ return null;
36
+ }
37
+ },
38
+ /**
39
+ * 写入 Cache API
40
+ */
41
+ async put(cacheKey, response, ttlSeconds) {
42
+ try {
43
+ const clone = response.clone();
44
+ const headers = new Headers(clone.headers);
45
+ headers.set("Cache-Control", `public, max-age=${ttlSeconds}`);
46
+ headers.set("CF-Cache-Status", "HIT");
47
+ const cachedResponse = new Response(clone.body, {
48
+ status: clone.status,
49
+ statusText: clone.statusText,
50
+ headers,
51
+ });
52
+ await caches.default.put(cacheKey, cachedResponse);
53
+ }
54
+ catch (err) {
55
+ console.warn("[cache] put failed:", err);
56
+ }
57
+ },
58
+ /**
59
+ * 删除缓存
60
+ */
61
+ async delete(cacheKey) {
62
+ try {
63
+ return await caches.default.delete(cacheKey);
64
+ }
65
+ catch (err) {
66
+ console.warn("[cache] delete failed:", err);
67
+ return false;
68
+ }
69
+ },
70
+ /** 缓存键生成器(暴露供外部使用) */
71
+ key,
72
+ };
73
+ }
74
+ /** 默认实例(无命名空间,适用于单项目场景) */
75
+ export const cache = createCache("default");
76
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,SAAS,GAAG,CAAC,IAAY,EAAE,KAAc;QACvC,MAAM,IAAI,GAAG,uBAAuB,SAAS,GAAG,IAAI,EAAE,CAAC;QACvD,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3C,CAAC;IAED,OAAO;QACL;;WAEG;QACH,KAAK,CAAC,GAAG,CAAC,QAAgB;YACxB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACnD,OAAO,KAAK,IAAI,IAAI,CAAC;YACvB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;gBACzC,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAED;;WAEG;QACH,KAAK,CAAC,GAAG,CAAC,QAAgB,EAAE,QAAkB,EAAE,UAAkB;YAChE,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAC/B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC3C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,mBAAmB,UAAU,EAAE,CAAC,CAAC;gBAC9D,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;gBACtC,MAAM,cAAc,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE;oBAC9C,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,OAAO;iBACR,CAAC,CAAC;gBACH,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;YACrD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;YAC3C,CAAC;QACH,CAAC;QAED;;WAEG;QACH,KAAK,CAAC,MAAM,CAAC,QAAgB;YAC3B,IAAI,CAAC;gBACH,OAAO,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC/C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;gBAC5C,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,sBAAsB;QACtB,GAAG;KACJ,CAAC;AACJ,CAAC;AAED,2BAA2B;AAC3B,MAAM,CAAC,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * 运行时系统配置模块(热生效 KV 存储)
3
+ *
4
+ * 使用 system_config 表存储运行时配置,支持热生效(无需重启)。
5
+ * 可选启用 Cache API 缓存以减少数据库查询。
6
+ *
7
+ * 三项目均有 system_config 表且结构完全一致。
8
+ *
9
+ * 来源:eshop/xtools/vcode system_config 表 + eshop cache.ts 合并
10
+ */
11
+ import { systemConfig } from "./db/schema";
12
+ interface ConfigDbLike {
13
+ select: (cols: {
14
+ value: typeof systemConfig.value;
15
+ }) => {
16
+ from: (table: typeof systemConfig) => {
17
+ where: (cond: unknown) => {
18
+ limit: (n: number) => Promise<{
19
+ value: string;
20
+ }[]>;
21
+ };
22
+ };
23
+ };
24
+ insert: (table: typeof systemConfig) => {
25
+ values: (data: {
26
+ key: string;
27
+ value: string;
28
+ updatedAt: string;
29
+ }) => {
30
+ onConflictDoUpdate: (opts: {
31
+ target: typeof systemConfig.key;
32
+ set: {
33
+ value: string;
34
+ updatedAt: string;
35
+ };
36
+ }) => Promise<unknown>;
37
+ };
38
+ };
39
+ delete: (table: typeof systemConfig) => {
40
+ where: (cond: unknown) => Promise<unknown>;
41
+ };
42
+ }
43
+ export interface SystemConfigOptions {
44
+ /** 内存缓存 TTL(毫秒),0 表示不缓存,默认 5 分钟 */
45
+ cacheTtlMs?: number;
46
+ }
47
+ /**
48
+ * 系统配置管理器
49
+ */
50
+ export declare class SystemConfig {
51
+ private db;
52
+ private cache;
53
+ private cacheTtlMs;
54
+ constructor(db: ConfigDbLike, options?: SystemConfigOptions);
55
+ /**
56
+ * 读取配置值
57
+ *
58
+ * 优先从内存缓存读取,过期后从数据库重新加载。
59
+ */
60
+ get(key: string, defaultValue?: string): Promise<string>;
61
+ /**
62
+ * 读取配置值并解析为数字
63
+ */
64
+ getNumber(key: string, defaultValue: number): Promise<number>;
65
+ /**
66
+ * 读取配置值并解析为布尔
67
+ */
68
+ getBoolean(key: string, defaultValue?: boolean): Promise<boolean>;
69
+ /**
70
+ * 写入配置值(UPSERT)
71
+ */
72
+ set(key: string, value: string): Promise<void>;
73
+ /**
74
+ * 删除配置
75
+ */
76
+ delete(key: string): Promise<void>;
77
+ /**
78
+ * 清除所有内存缓存(用于强制刷新)
79
+ */
80
+ clearCache(): void;
81
+ }
82
+ export {};
83
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,UAAU,YAAY;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,OAAO,YAAY,CAAC,KAAK,CAAA;KAAE,KAAK;QACtD,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,YAAY,KAAK;YACpC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK;gBACxB,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;oBAAE,KAAK,EAAE,MAAM,CAAA;iBAAE,EAAE,CAAC,CAAC;aACpD,CAAC;SACH,CAAC;KACH,CAAC;IACF,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,YAAY,KAAK;QACtC,MAAM,EAAE,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,KAAK;YACnE,kBAAkB,EAAE,CAAC,IAAI,EAAE;gBACzB,MAAM,EAAE,OAAO,YAAY,CAAC,GAAG,CAAC;gBAChC,GAAG,EAAE;oBAAE,KAAK,EAAE,MAAM,CAAC;oBAAC,SAAS,EAAE,MAAM,CAAA;iBAAE,CAAC;aAC3C,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;SACxB,CAAC;KACH,CAAC;IACF,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,YAAY,KAAK;QACtC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KAC5C,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAe;IACzB,OAAO,CAAC,KAAK,CAA2D;IACxE,OAAO,CAAC,UAAU,CAAS;gBAEf,EAAE,EAAE,YAAY,EAAE,OAAO,GAAE,mBAAwB;IAK/D;;;;OAIG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC;IA4B1D;;OAEG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMnE;;OAEG;IACG,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrE;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAapD;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC;;OAEG;IACH,UAAU,IAAI,IAAI;CAGnB"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * 运行时系统配置模块(热生效 KV 存储)
3
+ *
4
+ * 使用 system_config 表存储运行时配置,支持热生效(无需重启)。
5
+ * 可选启用 Cache API 缓存以减少数据库查询。
6
+ *
7
+ * 三项目均有 system_config 表且结构完全一致。
8
+ *
9
+ * 来源:eshop/xtools/vcode system_config 表 + eshop cache.ts 合并
10
+ */
11
+ import { eq } from "drizzle-orm";
12
+ import { systemConfig } from "./db/schema";
13
+ /**
14
+ * 系统配置管理器
15
+ */
16
+ export class SystemConfig {
17
+ db;
18
+ cache = new Map();
19
+ cacheTtlMs;
20
+ constructor(db, options = {}) {
21
+ this.db = db;
22
+ this.cacheTtlMs = options.cacheTtlMs ?? 5 * 60 * 1000; // 5 分钟
23
+ }
24
+ /**
25
+ * 读取配置值
26
+ *
27
+ * 优先从内存缓存读取,过期后从数据库重新加载。
28
+ */
29
+ async get(key, defaultValue = "") {
30
+ // 内存缓存
31
+ if (this.cacheTtlMs > 0) {
32
+ const cached = this.cache.get(key);
33
+ if (cached && Date.now() < cached.expiresAt) {
34
+ return cached.value;
35
+ }
36
+ }
37
+ try {
38
+ const [row] = await this.db
39
+ .select({ value: systemConfig.value })
40
+ .from(systemConfig)
41
+ .where(eq(systemConfig.key, key))
42
+ .limit(1);
43
+ const value = row?.value ?? defaultValue;
44
+ if (this.cacheTtlMs > 0) {
45
+ this.cache.set(key, { value, expiresAt: Date.now() + this.cacheTtlMs });
46
+ }
47
+ return value;
48
+ }
49
+ catch {
50
+ return defaultValue;
51
+ }
52
+ }
53
+ /**
54
+ * 读取配置值并解析为数字
55
+ */
56
+ async getNumber(key, defaultValue) {
57
+ const raw = await this.get(key, String(defaultValue));
58
+ const num = Number(raw);
59
+ return Number.isFinite(num) ? num : defaultValue;
60
+ }
61
+ /**
62
+ * 读取配置值并解析为布尔
63
+ */
64
+ async getBoolean(key, defaultValue = false) {
65
+ const raw = await this.get(key, String(defaultValue));
66
+ return raw === "true" || raw === "1";
67
+ }
68
+ /**
69
+ * 写入配置值(UPSERT)
70
+ */
71
+ async set(key, value) {
72
+ await this.db
73
+ .insert(systemConfig)
74
+ .values({ key, value, updatedAt: new Date().toISOString() })
75
+ .onConflictDoUpdate({
76
+ target: systemConfig.key,
77
+ set: { value, updatedAt: new Date().toISOString() },
78
+ });
79
+ // 清除内存缓存
80
+ this.cache.delete(key);
81
+ }
82
+ /**
83
+ * 删除配置
84
+ */
85
+ async delete(key) {
86
+ await this.db.delete(systemConfig).where(eq(systemConfig.key, key));
87
+ this.cache.delete(key);
88
+ }
89
+ /**
90
+ * 清除所有内存缓存(用于强制刷新)
91
+ */
92
+ clearCache() {
93
+ this.cache.clear();
94
+ }
95
+ }
96
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * AES-256-GCM 加解密模块
3
+ *
4
+ * 使用 Web Crypto API(Workers 原生),零外部依赖。
5
+ * 凭据在写入数据库前加密,读取后解密,确保数据库中不存明文。
6
+ *
7
+ * 密钥来源:环境变量(32 字节 hex = 64 字符 = 256 bit)
8
+ * 格式:iv(12B) + ciphertext + authTag(16B),整体 base64 编码存储
9
+ *
10
+ * 来源:xtools src/lib/crypto.ts
11
+ */
12
+ /**
13
+ * 加密 JSON 对象 → base64 字符串
14
+ */
15
+ export declare function encrypt(data: Record<string, unknown>, encryptionKeyHex: string): Promise<string>;
16
+ /**
17
+ * 解密 base64 字符串 → JSON 对象
18
+ */
19
+ export declare function decrypt(encryptedBase64: string, encryptionKeyHex: string): Promise<Record<string, unknown>>;
20
+ /**
21
+ * 检查加密密钥是否已配置
22
+ */
23
+ export declare function isEncryptionAvailable(env: {
24
+ CREDENTIALS_ENCRYPTION_KEY?: string;
25
+ }): boolean;
26
+ /**
27
+ * 生成 UUID v4(兼容 Cloudflare Workers)
28
+ *
29
+ * 使用 crypto.getRandomValues()(100% Workers 支持),
30
+ * 不依赖 crypto.randomUUID()(部分旧版本不支持)。
31
+ */
32
+ export declare function generateUUID(): string;
33
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAmCH;;GAEG;AACH,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED;;GAEG;AACH,wBAAsB,OAAO,CAC3B,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CASlC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE;IAAE,0BAA0B,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAE3F;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAUrC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * AES-256-GCM 加解密模块
3
+ *
4
+ * 使用 Web Crypto API(Workers 原生),零外部依赖。
5
+ * 凭据在写入数据库前加密,读取后解密,确保数据库中不存明文。
6
+ *
7
+ * 密钥来源:环境变量(32 字节 hex = 64 字符 = 256 bit)
8
+ * 格式:iv(12B) + ciphertext + authTag(16B),整体 base64 编码存储
9
+ *
10
+ * 来源:xtools src/lib/crypto.ts
11
+ */
12
+ const ALGO = "AES-GCM";
13
+ const IV_LENGTH = 12;
14
+ /** 安全地将 Uint8Array 编码为 base64(分块避免大数组展开栈溢出) */
15
+ function arrayToBase64(bytes) {
16
+ const chunks = [];
17
+ const chunkSize = 8192;
18
+ for (let i = 0; i < bytes.length; i += chunkSize) {
19
+ const chunk = bytes.subarray(i, Math.min(i + chunkSize, bytes.length));
20
+ chunks.push(String.fromCharCode(...chunk));
21
+ }
22
+ return btoa(chunks.join(""));
23
+ }
24
+ /** 安全地将 base64 解码为 Uint8Array */
25
+ function base64ToArray(base64) {
26
+ const binary = atob(base64);
27
+ const bytes = new Uint8Array(binary.length);
28
+ for (let i = 0; i < binary.length; i++) {
29
+ bytes[i] = binary.charCodeAt(i);
30
+ }
31
+ return bytes;
32
+ }
33
+ /** 从 hex 字符串导入 AES-256-GCM 密钥 */
34
+ async function importKey(rawHex) {
35
+ if (!rawHex || rawHex.length !== 64) {
36
+ throw new Error("加密密钥必须为 64 字符 hex(32 字节 / 256 bit)");
37
+ }
38
+ const keyBytes = new Uint8Array(rawHex.match(/.{2}/g).map((b) => parseInt(b, 16)));
39
+ return crypto.subtle.importKey("raw", keyBytes, { name: ALGO }, false, ["encrypt", "decrypt"]);
40
+ }
41
+ /**
42
+ * 加密 JSON 对象 → base64 字符串
43
+ */
44
+ export async function encrypt(data, encryptionKeyHex) {
45
+ const key = await importKey(encryptionKeyHex);
46
+ const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
47
+ const plaintext = new TextEncoder().encode(JSON.stringify(data));
48
+ const cipherBuffer = await crypto.subtle.encrypt({ name: ALGO, iv }, key, plaintext);
49
+ const combined = new Uint8Array(iv.length + cipherBuffer.byteLength);
50
+ combined.set(iv, 0);
51
+ combined.set(new Uint8Array(cipherBuffer), iv.length);
52
+ return arrayToBase64(combined);
53
+ }
54
+ /**
55
+ * 解密 base64 字符串 → JSON 对象
56
+ */
57
+ export async function decrypt(encryptedBase64, encryptionKeyHex) {
58
+ const key = await importKey(encryptionKeyHex);
59
+ const combined = base64ToArray(encryptedBase64);
60
+ const iv = combined.slice(0, IV_LENGTH);
61
+ const ciphertext = combined.slice(IV_LENGTH);
62
+ const plainBuffer = await crypto.subtle.decrypt({ name: ALGO, iv }, key, ciphertext);
63
+ return JSON.parse(new TextDecoder().decode(plainBuffer));
64
+ }
65
+ /**
66
+ * 检查加密密钥是否已配置
67
+ */
68
+ export function isEncryptionAvailable(env) {
69
+ return !!(env.CREDENTIALS_ENCRYPTION_KEY && env.CREDENTIALS_ENCRYPTION_KEY.length === 64);
70
+ }
71
+ /**
72
+ * 生成 UUID v4(兼容 Cloudflare Workers)
73
+ *
74
+ * 使用 crypto.getRandomValues()(100% Workers 支持),
75
+ * 不依赖 crypto.randomUUID()(部分旧版本不支持)。
76
+ */
77
+ export function generateUUID() {
78
+ const bytes = new Uint8Array(16);
79
+ crypto.getRandomValues(bytes);
80
+ bytes[6] = (bytes[6] & 0x0f) | 0x40; // v4
81
+ bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant
82
+ const hex = Array.from(bytes)
83
+ .map((b) => b.toString(16).padStart(2, "0"))
84
+ .join("");
85
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
86
+ }
87
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * libSQL 数据库连接工厂
3
+ *
4
+ * 核心设计:Isolate 级连接复用
5
+ * Cloudflare Workers 的 isolate 在多个请求间复用。
6
+ * 缓存 client 实例避免每次请求重新 createClient(节省 ~1ms CPU)。
7
+ *
8
+ * 合并自:
9
+ * - eshop: src/db/database.ts + src/db/client.ts
10
+ * - xtools: src/db/database.ts + src/db/client.ts
11
+ * - vcode: src/db/index.ts(含自动迁移)
12
+ */
13
+ import { type Client } from "@libsql/client";
14
+ import { drizzle } from "drizzle-orm/libsql";
15
+ /** Drizzle ORM 实例类型(通用) */
16
+ export type DrizzleInstance<TSchema extends Record<string, unknown> = Record<string, never>> = ReturnType<typeof drizzle<TSchema>>;
17
+ /**
18
+ * 创建或复用 libSQL Client(Isolate 级缓存)
19
+ *
20
+ * 同一 isolate + 同一 URL 下复用 client 实例。
21
+ * URL 变化时(极少见)重建实例。
22
+ */
23
+ export declare function getOrCreateClient(url: string, authToken?: string): Client;
24
+ /**
25
+ * 创建 Drizzle ORM 实例
26
+ *
27
+ * @param client - libSQL Client
28
+ * @param schema - Drizzle schema 对象(可选,传入后支持关系查询)
29
+ */
30
+ export declare function createDrizzle<TSchema extends Record<string, unknown>>(client: Client, schema?: TSchema): DrizzleInstance<TSchema>;
31
+ /**
32
+ * 一步到位:初始化数据库(client + drizzle),带 Isolate 级缓存 + 连接验证。
33
+ *
34
+ * 首次连接时执行 `SELECT 1` 验证连通性,失败时自动重试(最多 2 次,指数退避)。
35
+ * 后续请求复用 Isolate 级缓存的 client,不再探活(节省 CPU 时间)。
36
+ *
37
+ * @param url - Turso URL(libsql://xxx.turso.io)
38
+ * @param authToken - Turso 认证 Token(可选)
39
+ * @param schema - Drizzle schema(可选)
40
+ */
41
+ export declare function initDatabase<TSchema extends Record<string, unknown>>(url?: string, authToken?: string, schema?: TSchema): DrizzleInstance<TSchema>;
42
+ /**
43
+ * 带连接验证的数据库初始化(用于 bootstrap 中间件)。
44
+ *
45
+ * 首次连接时执行 `SELECT 1` 验证连通性,失败时重试。
46
+ * 后续请求直接返回缓存的 drizzle 实例(零额外开销)。
47
+ */
48
+ export declare function initDatabaseWithHealthCheck<TSchema extends Record<string, unknown>>(url?: string, authToken?: string, schema?: TSchema): Promise<DrizzleInstance<TSchema>>;
49
+ /**
50
+ * 重置 Isolate 级缓存(仅用于测试)
51
+ */
52
+ export declare function _resetCache(): void;
53
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../src/db/connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,2BAA2B;AAC3B,MAAM,MAAM,eAAe,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IACzF,UAAU,CAAC,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AAStC;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CASzE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,OAAO,GACf,eAAe,CAAC,OAAO,CAAC,CAE1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,GAAG,CAAC,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,OAAO,GACf,eAAe,CAAC,OAAO,CAAC,CAI1B;AAED;;;;;GAKG;AACH,wBAAsB,2BAA2B,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvF,GAAG,CAAC,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CA+BnC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAGlC"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * libSQL 数据库连接工厂
3
+ *
4
+ * 核心设计:Isolate 级连接复用
5
+ * Cloudflare Workers 的 isolate 在多个请求间复用。
6
+ * 缓存 client 实例避免每次请求重新 createClient(节省 ~1ms CPU)。
7
+ *
8
+ * 合并自:
9
+ * - eshop: src/db/database.ts + src/db/client.ts
10
+ * - xtools: src/db/database.ts + src/db/client.ts
11
+ * - vcode: src/db/index.ts(含自动迁移)
12
+ */
13
+ import { createClient } from "@libsql/client";
14
+ import { drizzle } from "drizzle-orm/libsql";
15
+ // ═══════════════════════════════════════════════════════════════════════════════
16
+ // Isolate 级缓存(模块级别变量在 Workers isolate 生命周期内持久存在)
17
+ // ═══════════════════════════════════════════════════════════════════════════════
18
+ let _cachedUrl;
19
+ let _cachedClient;
20
+ /**
21
+ * 创建或复用 libSQL Client(Isolate 级缓存)
22
+ *
23
+ * 同一 isolate + 同一 URL 下复用 client 实例。
24
+ * URL 变化时(极少见)重建实例。
25
+ */
26
+ export function getOrCreateClient(url, authToken) {
27
+ if (_cachedClient && _cachedUrl === url) {
28
+ return _cachedClient;
29
+ }
30
+ const client = createClient({ url, authToken });
31
+ _cachedUrl = url;
32
+ _cachedClient = client;
33
+ return client;
34
+ }
35
+ /**
36
+ * 创建 Drizzle ORM 实例
37
+ *
38
+ * @param client - libSQL Client
39
+ * @param schema - Drizzle schema 对象(可选,传入后支持关系查询)
40
+ */
41
+ export function createDrizzle(client, schema) {
42
+ return (schema ? drizzle(client, { schema }) : drizzle(client));
43
+ }
44
+ /**
45
+ * 一步到位:初始化数据库(client + drizzle),带 Isolate 级缓存 + 连接验证。
46
+ *
47
+ * 首次连接时执行 `SELECT 1` 验证连通性,失败时自动重试(最多 2 次,指数退避)。
48
+ * 后续请求复用 Isolate 级缓存的 client,不再探活(节省 CPU 时间)。
49
+ *
50
+ * @param url - Turso URL(libsql://xxx.turso.io)
51
+ * @param authToken - Turso 认证 Token(可选)
52
+ * @param schema - Drizzle schema(可选)
53
+ */
54
+ export function initDatabase(url, authToken, schema) {
55
+ if (!url)
56
+ throw new Error("TURSO_URL is required");
57
+ const client = getOrCreateClient(url, authToken);
58
+ return createDrizzle(client, schema);
59
+ }
60
+ /**
61
+ * 带连接验证的数据库初始化(用于 bootstrap 中间件)。
62
+ *
63
+ * 首次连接时执行 `SELECT 1` 验证连通性,失败时重试。
64
+ * 后续请求直接返回缓存的 drizzle 实例(零额外开销)。
65
+ */
66
+ export async function initDatabaseWithHealthCheck(url, authToken, schema) {
67
+ if (!url)
68
+ throw new Error("TURSO_URL is required");
69
+ // 如果已有缓存的连接,直接返回(Isolate 复用场景)
70
+ if (_cachedClient && _cachedUrl === url) {
71
+ return createDrizzle(_cachedClient, schema);
72
+ }
73
+ // 首次连接:创建 client 并验证连通性
74
+ const client = getOrCreateClient(url, authToken);
75
+ const db = createDrizzle(client, schema);
76
+ // 最多重试 2 次(500ms → 1s),避免 Turso 短暂不可达导致部署失败
77
+ for (let attempt = 0; attempt <= 2; attempt++) {
78
+ try {
79
+ await client.execute("SELECT 1");
80
+ return db;
81
+ }
82
+ catch (err) {
83
+ if (attempt >= 2)
84
+ throw err;
85
+ const delay = 500 * Math.pow(2, attempt);
86
+ console.warn(`[db:health-check] 连接失败,${delay}ms 后重试 (${attempt + 1}/2)`);
87
+ await new Promise((r) => setTimeout(r, delay));
88
+ // 重建 client(可能连接状态已损坏)
89
+ _cachedClient = undefined;
90
+ _cachedUrl = undefined;
91
+ const newClient = getOrCreateClient(url, authToken);
92
+ Object.assign(db, createDrizzle(newClient, schema));
93
+ }
94
+ }
95
+ return db;
96
+ }
97
+ /**
98
+ * 重置 Isolate 级缓存(仅用于测试)
99
+ */
100
+ export function _resetCache() {
101
+ _cachedUrl = undefined;
102
+ _cachedClient = undefined;
103
+ }
104
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../src/db/connection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @usethink/cf-core/db — 数据库层统一导出
3
+ */
4
+ export { getOrCreateClient, createDrizzle, initDatabase, initDatabaseWithHealthCheck, type DrizzleInstance } from "./connection";
5
+ export * from "./schema";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/db/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,EAAE,2BAA2B,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACjI,cAAc,UAAU,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @usethink/cf-core/db — 数据库层统一导出
3
+ */
4
+ export { getOrCreateClient, createDrizzle, initDatabase, initDatabaseWithHealthCheck } from "./connection";
5
+ export * from "./schema";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/db/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,EAAE,2BAA2B,EAAwB,MAAM,cAAc,CAAC;AACjI,cAAc,UAAU,CAAC"}