@ubean/server 0.1.12 → 0.2.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 (56) hide show
  1. package/dist/analytics-entry.d.ts +2 -0
  2. package/dist/analytics-entry.js +2 -0
  3. package/dist/cache-C84ix1Vq.js +173 -0
  4. package/dist/cache-b-MZlyv0.d.ts +48 -0
  5. package/dist/cache-directive-C1Nekkza.js +304 -0
  6. package/dist/cache-directive-CAxJAQyE.d.ts +175 -0
  7. package/dist/cache-directive.d.ts +2 -0
  8. package/dist/cache-directive.js +2 -0
  9. package/dist/cache-entry.d.ts +3 -0
  10. package/dist/cache-entry.js +3 -0
  11. package/dist/cron-entry.d.ts +2 -0
  12. package/dist/cron-entry.js +2 -0
  13. package/dist/cron-scheduler-BF33PPn4.d.ts +77 -0
  14. package/dist/cron-scheduler-BVuXv7nn.js +258 -0
  15. package/dist/database-CfpFznl-.d.ts +67 -0
  16. package/dist/database-DNrY44SQ.js +352 -0
  17. package/dist/database.d.ts +2 -0
  18. package/dist/database.js +2 -0
  19. package/dist/email-BjfRiR9b.js +354 -0
  20. package/dist/email-BvpEuNn_.d.ts +226 -0
  21. package/dist/email.d.ts +2 -0
  22. package/dist/email.js +2 -0
  23. package/dist/feature-flags-CdLwsMD2.js +657 -0
  24. package/dist/feature-flags-DWkS6p0D.d.ts +386 -0
  25. package/dist/fetch-memo-rbkxxnW4.js +338 -0
  26. package/dist/index.d.ts +183 -488
  27. package/dist/index.js +352 -2023
  28. package/dist/middleware.d.ts +2 -0
  29. package/dist/middleware.js +3 -0
  30. package/dist/observability-Cio6Qq1H.js +339 -0
  31. package/dist/observability-DUNUEjj3.d.ts +70 -0
  32. package/dist/observability.d.ts +2 -0
  33. package/dist/observability.js +2 -0
  34. package/dist/queue-Bwzi3mhK.js +210 -0
  35. package/dist/queue-GOfTAWlz.d.ts +55 -0
  36. package/dist/queue.d.ts +2 -0
  37. package/dist/queue.js +2 -0
  38. package/dist/realtime.d.ts +2 -0
  39. package/dist/realtime.js +2 -0
  40. package/dist/security.d.ts +2 -0
  41. package/dist/security.js +2 -0
  42. package/dist/sessions-BLqFFQTL.d.ts +217 -0
  43. package/dist/sessions-BsBsyFAG.js +450 -0
  44. package/dist/single-flight-BJyhDLdU.d.ts +422 -0
  45. package/dist/single-flight-mJ4ZKbx1.js +715 -0
  46. package/dist/sse-Ct72zhic.d.ts +95 -0
  47. package/dist/sse-a6Ky9Vcl.js +310 -0
  48. package/dist/static-DPHaovQe.js +90 -0
  49. package/dist/static-K2dRvjpS.d.ts +11 -0
  50. package/dist/static.d.ts +2 -0
  51. package/dist/static.js +2 -0
  52. package/dist/storage-BZLMaqHr.js +162 -0
  53. package/dist/storage-QdlPtPtR.d.ts +48 -0
  54. package/dist/storage.d.ts +2 -0
  55. package/dist/storage.js +2 -0
  56. package/package.json +68 -6
@@ -0,0 +1,175 @@
1
+ //#region src/cache-directive.d.ts
2
+ /**
3
+ * 组件级缓存 —— 运行时原语
4
+ *
5
+ * 通过 `defineCachedFunction()` 显式包装异步函数,缓存其返回值。
6
+ *
7
+ * 设计要点:
8
+ * - **值缓存**(非 HTTP 响应缓存):缓存异步函数的返回值(JSON 可序列化),
9
+ * 与 `cache.ts`(HTTP 响应缓存)解耦。
10
+ * - **AsyncLocalStorage 作用域**:`cacheLife()` / `cacheTag()` 通过 ALS 写入
11
+ * 当前缓存的执行作用域,无需手动传递 scope 对象。
12
+ * - **标签失效**:`revalidateTag(tag)` 失效所有带该标签的缓存条目;
13
+ * `revalidatePath(path)` 失效匹配路径的缓存条目。
14
+ * - **零外部依赖**:使用内置 `AsyncLocalStorage` + `Map` 内存存储。
15
+ *
16
+ * 用户通过 `defineCachedFunction(fn, options)` 显式声明缓存函数,无需 Vite 插件
17
+ * 参与,运行时通过此模块的 API 执行缓存逻辑。
18
+ */
19
+ /**
20
+ * 组件级缓存条目。存储序列化后的值 + 标签 + 过期时间。
21
+ */
22
+ interface ComponentCacheEntry {
23
+ /** JSON 序列化后的返回值。 */
24
+ value: string;
25
+ /** 关联的标签列表(用于 `revalidateTag`)。 */
26
+ tags: string[];
27
+ /** 创建时间戳(ms)。 */
28
+ createdAt: number;
29
+ /** 过期时间戳(ms)。 */
30
+ expiresAt: number;
31
+ }
32
+ /**
33
+ * 组件级缓存存储接口。
34
+ *
35
+ * 与 `CacheStore`(HTTP 响应缓存)不同,此接口存储任意 JSON 可序列化值。
36
+ */
37
+ interface ComponentCacheStore {
38
+ get(key: string): Promise<ComponentCacheEntry | undefined>;
39
+ set(key: string, entry: Omit<ComponentCacheEntry, 'createdAt' | 'expiresAt'>, ttl: number): Promise<void>;
40
+ delete(key: string): Promise<boolean>;
41
+ clear(): Promise<void>;
42
+ /** 返回所有键(用于标签/路径失效扫描)。 */
43
+ keys?: () => Promise<string[]>;
44
+ }
45
+ /**
46
+ * 默认内存组件缓存存储。带标签反向索引以加速 `revalidateTag`。
47
+ */
48
+ declare function createComponentMemoryStore(maxEntries?: number): ComponentCacheStore;
49
+ declare function useComponentCacheStore(store?: ComponentCacheStore): ComponentCacheStore;
50
+ declare function clearComponentCacheStore(): void;
51
+ /**
52
+ * 设置当前缓存作用域的 TTL(秒)。
53
+ *
54
+ * 必须在 `defineCachedFunction()` 包装的函数体内调用,否则为空操作(允许在
55
+ * 非缓存上下文中调用以简化条件逻辑)。
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const getUser = defineCachedFunction(
60
+ * async (id: string) => {
61
+ * cacheLife(3600); // 缓存 1 小时
62
+ * return await db.query.user.findById(id);
63
+ * },
64
+ * { name: 'getUser' }
65
+ * );
66
+ * ```
67
+ */
68
+ declare function cacheLife(seconds: number): void;
69
+ /**
70
+ * 为当前缓存作用域添加标签。
71
+ *
72
+ * 标签可用于通过 `revalidateTag(tag)` 精确失效缓存。
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * const getUser = defineCachedFunction(
77
+ * async (id: string) => {
78
+ * cacheTag('users', `user:${id}`);
79
+ * return await db.query.user.findById(id);
80
+ * },
81
+ * { name: 'getUser' }
82
+ * );
83
+ *
84
+ * // 失效所有带 'users' 标签的缓存
85
+ * await revalidateTag('users');
86
+ * ```
87
+ */
88
+ declare function cacheTag(...tags: string[]): void;
89
+ /**
90
+ * 缓存函数选项。
91
+ */
92
+ interface CachedFunctionOptions {
93
+ /** 缓存键前缀(通常是函数名或文件路径 + 函数名)。 */
94
+ name: string;
95
+ /** 默认 TTL(秒)。未调用 `cacheLife()` 时使用。默认 60。 */
96
+ defaultTtl?: number;
97
+ /**
98
+ * 自定义缓存键生成函数。默认使用 `name + JSON.stringify(args)`。
99
+ * 用于精细化控制缓存键(例如排除不稳定的参数)。
100
+ */
101
+ getKey?: (...args: unknown[]) => string;
102
+ }
103
+ /**
104
+ * @deprecated 请使用 `CachedFunctionOptions`。保留旧名称仅为向后兼容。
105
+ */
106
+ type CacheWrapOptions = CachedFunctionOptions;
107
+ /**
108
+ * 将异步函数包装为带缓存的函数。
109
+ *
110
+ * 用户在源码中显式调用此函数声明缓存函数(无需 Vite 插件参与)。
111
+ *
112
+ * 工作流程:
113
+ * 1. 根据参数生成缓存键
114
+ * 2. 查询缓存 → 命中则返回反序列化的值
115
+ * 3. 未命中 → 在 `AsyncLocalStorage` 作用域中执行原函数
116
+ * - 函数体内的 `cacheLife()` / `cacheTag()` 写入作用域
117
+ * 4. 将结果序列化存入缓存(带标签 + TTL)
118
+ * 5. 返回结果
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * import { defineCachedFunction, cacheLife, cacheTag } from '@ubean/server';
123
+ *
124
+ * const getUser = defineCachedFunction(
125
+ * async (id: string) => {
126
+ * cacheLife(3600);
127
+ * cacheTag('users', `user:${id}`);
128
+ * return await db.query.user.findById(id);
129
+ * },
130
+ * { name: 'getUser' }
131
+ * );
132
+ * ```
133
+ *
134
+ * @param fn 原始异步函数
135
+ * @param options 缓存选项
136
+ */
137
+ declare function defineCachedFunction<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, options: CachedFunctionOptions): (...args: TArgs) => Promise<TResult>;
138
+ /**
139
+ * @deprecated 请使用 `defineCachedFunction()`。保留旧名称仅为向后兼容。
140
+ */
141
+ declare const wrapWithCache: typeof defineCachedFunction;
142
+ /**
143
+ * 失效所有带指定标签的缓存条目。
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * // 用户数据更新后,失效所有 'users' 标签的缓存
148
+ * await revalidateTag('users');
149
+ * ```
150
+ */
151
+ declare function revalidateTag(tag: string): Promise<number>;
152
+ /**
153
+ * 失效多个标签的缓存条目。
154
+ */
155
+ declare function revalidateTags(...tags: string[]): Promise<number>;
156
+ /**
157
+ * 失效缓存键匹配指定模式的条目。
158
+ *
159
+ * 支持 `*`(单段通配)和 `**`(多段通配)。
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * // 失效所有 getUser 开头的缓存
164
+ * await revalidatePath('getUser:*');
165
+ * // 失效特定用户缓存
166
+ * await revalidatePath('getUser:["user-123"]');
167
+ * ```
168
+ */
169
+ declare function revalidatePath(pattern: string | RegExp): Promise<number>;
170
+ /**
171
+ * 清空所有组件级缓存。
172
+ */
173
+ declare function clearComponentCache(): Promise<void>;
174
+ //#endregion
175
+ export { cacheLife as a, clearComponentCacheStore as c, revalidatePath as d, revalidateTag as f, wrapWithCache as h, ComponentCacheStore as i, createComponentMemoryStore as l, useComponentCacheStore as m, CachedFunctionOptions as n, cacheTag as o, revalidateTags as p, ComponentCacheEntry as r, clearComponentCache as s, CacheWrapOptions as t, defineCachedFunction as u };
@@ -0,0 +1,2 @@
1
+ import { a as cacheLife, c as clearComponentCacheStore, d as revalidatePath, f as revalidateTag, h as wrapWithCache, i as ComponentCacheStore, l as createComponentMemoryStore, m as useComponentCacheStore, n as CachedFunctionOptions, o as cacheTag, p as revalidateTags, r as ComponentCacheEntry, s as clearComponentCache, t as CacheWrapOptions, u as defineCachedFunction } from "./cache-directive-CAxJAQyE.js";
2
+ export { CacheWrapOptions, CachedFunctionOptions, ComponentCacheEntry, ComponentCacheStore, cacheLife, cacheTag, clearComponentCache, clearComponentCacheStore, createComponentMemoryStore, defineCachedFunction, revalidatePath, revalidateTag, revalidateTags, useComponentCacheStore, wrapWithCache };
@@ -0,0 +1,2 @@
1
+ import { a as createComponentMemoryStore, c as revalidateTag, d as wrapWithCache, i as clearComponentCacheStore, l as revalidateTags, n as cacheTag, o as defineCachedFunction, r as clearComponentCache, s as revalidatePath, t as cacheLife, u as useComponentCacheStore } from "./cache-directive-C1Nekkza.js";
2
+ export { cacheLife, cacheTag, clearComponentCache, clearComponentCacheStore, createComponentMemoryStore, defineCachedFunction, revalidatePath, revalidateTag, revalidateTags, useComponentCacheStore, wrapWithCache };
@@ -0,0 +1,3 @@
1
+ import { a as cacheLife, c as clearComponentCacheStore, d as revalidatePath, f as revalidateTag, h as wrapWithCache, i as ComponentCacheStore, l as createComponentMemoryStore, m as useComponentCacheStore, n as CachedFunctionOptions, o as cacheTag, p as revalidateTags, r as ComponentCacheEntry, s as clearComponentCache, t as CacheWrapOptions, u as defineCachedFunction } from "./cache-directive-CAxJAQyE.js";
2
+ import { a as clearCacheStore, c as invalidateRouteCache, i as cachedEventHandler, l as resolveRouteCacheRules, n as CacheRule, o as createCacheMiddleware, r as CacheStore, s as createMemoryStore, t as CacheEntry, u as useCacheStore } from "./cache-b-MZlyv0.js";
3
+ export { CacheEntry, CacheRule, CacheStore, CacheWrapOptions, CachedFunctionOptions, ComponentCacheEntry, ComponentCacheStore, cacheLife, cacheTag, cachedEventHandler, clearCacheStore, clearComponentCache, clearComponentCacheStore, createCacheMiddleware, createComponentMemoryStore, createMemoryStore, defineCachedFunction, invalidateRouteCache, resolveRouteCacheRules, revalidatePath, revalidateTag, revalidateTags, useCacheStore, useComponentCacheStore, wrapWithCache };
@@ -0,0 +1,3 @@
1
+ import { a as invalidateRouteCache, i as createMemoryStore, n as clearCacheStore, o as resolveRouteCacheRules, r as createCacheMiddleware, s as useCacheStore, t as cachedEventHandler } from "./cache-C84ix1Vq.js";
2
+ import { a as createComponentMemoryStore, c as revalidateTag, d as wrapWithCache, i as clearComponentCacheStore, l as revalidateTags, n as cacheTag, o as defineCachedFunction, r as clearComponentCache, s as revalidatePath, t as cacheLife, u as useComponentCacheStore } from "./cache-directive-C1Nekkza.js";
3
+ export { cacheLife, cacheTag, cachedEventHandler, clearCacheStore, clearComponentCache, clearComponentCacheStore, createCacheMiddleware, createComponentMemoryStore, createMemoryStore, defineCachedFunction, invalidateRouteCache, resolveRouteCacheRules, revalidatePath, revalidateTag, revalidateTags, useCacheStore, useComponentCacheStore, wrapWithCache };
@@ -0,0 +1,2 @@
1
+ import { _ as runScheduledTask, a as resetCronRunCounts, c as CronContext, d as CronTaskMeta, f as ScheduledTask, g as getScheduledTasks, h as defineScheduled, i as parseCron, l as CronSchedule, m as createCronContext, n as SchedulerOptions, o as startCronScheduler, p as clearScheduledTasks, r as createMemoryCronScheduler, s as validateCron, t as CronScheduler, u as CronTaskDefinition } from "./cron-scheduler-BF33PPn4.js";
2
+ export { CronContext, CronSchedule, CronScheduler, CronTaskDefinition, CronTaskMeta, ScheduledTask, SchedulerOptions, clearScheduledTasks, createCronContext, createMemoryCronScheduler, defineScheduled, getScheduledTasks, parseCron, resetCronRunCounts, runScheduledTask, startCronScheduler, validateCron };
@@ -0,0 +1,2 @@
1
+ import { a as validateCron, c as defineScheduled, i as startCronScheduler, l as getScheduledTasks, n as parseCron, o as clearScheduledTasks, r as resetCronRunCounts, s as createCronContext, t as createMemoryCronScheduler, u as runScheduledTask } from "./cron-scheduler-BVuXv7nn.js";
2
+ export { clearScheduledTasks, createCronContext, createMemoryCronScheduler, defineScheduled, getScheduledTasks, parseCron, resetCronRunCounts, runScheduledTask, startCronScheduler, validateCron };
@@ -0,0 +1,77 @@
1
+ //#region src/cron.d.ts
2
+ type CronSchedule = string;
3
+ interface CronTaskMeta {
4
+ name: string;
5
+ schedule: CronSchedule;
6
+ description?: string;
7
+ timezone?: string;
8
+ timeout?: number;
9
+ runOnStart?: boolean;
10
+ }
11
+ interface CronTaskDefinition extends CronTaskMeta {
12
+ handler: (ctx: CronContext) => void | Promise<void>;
13
+ }
14
+ interface CronContext {
15
+ name: string;
16
+ schedule: CronSchedule;
17
+ timestamp: Date;
18
+ runCount: number;
19
+ }
20
+ interface ScheduledTask {
21
+ name: string;
22
+ schedule: CronSchedule;
23
+ handler: (ctx: CronContext) => void | Promise<void>;
24
+ meta: Omit<CronTaskMeta, 'name' | 'schedule'>;
25
+ }
26
+ declare function defineScheduled(metaOrName: CronTaskMeta | string, handler?: (ctx: CronContext) => void | Promise<void>): ScheduledTask;
27
+ declare function getScheduledTasks(): ScheduledTask[];
28
+ declare function clearScheduledTasks(): void;
29
+ declare function runScheduledTask(name: string): Promise<{
30
+ ok: boolean;
31
+ duration: number;
32
+ error?: Error;
33
+ }>;
34
+ declare function createCronContext(name: string, schedule: string): CronContext;
35
+ //#endregion
36
+ //#region src/cron-scheduler.d.ts
37
+ interface CronScheduler {
38
+ start(): Promise<void>;
39
+ stop(): Promise<void>;
40
+ runTask(name: string): Promise<{
41
+ ok: boolean;
42
+ duration: number;
43
+ error?: Error;
44
+ }>;
45
+ isRunning(): boolean;
46
+ getTasks(): ScheduledTask[];
47
+ getNextRuns(): Array<{
48
+ name: string;
49
+ nextRun: Date | null;
50
+ }>;
51
+ }
52
+ interface SchedulerOptions {
53
+ timezone?: string;
54
+ defaultTimeout?: number;
55
+ onTaskStart?: (task: ScheduledTask) => void;
56
+ onTaskComplete?: (task: ScheduledTask, result: {
57
+ ok: boolean;
58
+ duration: number;
59
+ error?: Error;
60
+ }) => void;
61
+ onTaskError?: (task: ScheduledTask, error: Error) => void;
62
+ }
63
+ type CronField = number[];
64
+ interface ParsedCron {
65
+ minute: CronField;
66
+ hour: CronField;
67
+ dom: CronField;
68
+ month: CronField;
69
+ dow: CronField;
70
+ }
71
+ declare function parseCron(schedule: string): ParsedCron | null;
72
+ declare function createMemoryCronScheduler(options?: SchedulerOptions): CronScheduler;
73
+ declare function startCronScheduler(options?: SchedulerOptions): CronScheduler;
74
+ declare function resetCronRunCounts(): void;
75
+ declare function validateCron(schedule: string): boolean;
76
+ //#endregion
77
+ export { runScheduledTask as _, resetCronRunCounts as a, CronContext as c, CronTaskMeta as d, ScheduledTask as f, getScheduledTasks as g, defineScheduled as h, parseCron as i, CronSchedule as l, createCronContext as m, SchedulerOptions as n, startCronScheduler as o, clearScheduledTasks as p, createMemoryCronScheduler as r, validateCron as s, CronScheduler as t, CronTaskDefinition as u };
@@ -0,0 +1,258 @@
1
+ //#region src/cron.ts
2
+ const SCHEDULED_TASKS_KEY = "__ubean_scheduled_tasks__";
3
+ const TASK_RUN_COUNT_KEY = "__ubean_task_run_count__";
4
+ function getTaskMap() {
5
+ if (!globalThis[SCHEDULED_TASKS_KEY]) globalThis[SCHEDULED_TASKS_KEY] = /* @__PURE__ */ new Map();
6
+ return globalThis[SCHEDULED_TASKS_KEY];
7
+ }
8
+ function getTaskRunCount() {
9
+ if (!globalThis[TASK_RUN_COUNT_KEY]) globalThis[TASK_RUN_COUNT_KEY] = { value: 0 };
10
+ return globalThis[TASK_RUN_COUNT_KEY];
11
+ }
12
+ function defineScheduled(metaOrName, handler) {
13
+ let meta;
14
+ let fn;
15
+ if (typeof metaOrName === "string") {
16
+ meta = {
17
+ name: metaOrName,
18
+ schedule: "* * * * *"
19
+ };
20
+ fn = handler || (() => {});
21
+ } else {
22
+ meta = metaOrName;
23
+ fn = handler || (() => {});
24
+ }
25
+ if (!meta.name) throw new Error("[ubean] Cron task must have a name");
26
+ if (!meta.schedule) throw new Error(`[ubean] Cron task "${meta.name}" must have a schedule (cron expression)`);
27
+ const task = {
28
+ name: meta.name,
29
+ schedule: meta.schedule,
30
+ handler: fn,
31
+ meta: {
32
+ description: meta.description,
33
+ timezone: meta.timezone,
34
+ timeout: meta.timeout,
35
+ runOnStart: meta.runOnStart
36
+ }
37
+ };
38
+ getTaskMap().set(meta.name, task);
39
+ return task;
40
+ }
41
+ function getScheduledTasks() {
42
+ return Array.from(getTaskMap().values());
43
+ }
44
+ function clearScheduledTasks() {
45
+ getTaskMap().clear();
46
+ }
47
+ async function runScheduledTask(name) {
48
+ const task = getTaskMap().get(name);
49
+ if (!task) throw new Error(`[ubean] Cron task "${name}" not found`);
50
+ const runCount = ++getTaskRunCount().value;
51
+ const start = Date.now();
52
+ try {
53
+ const ctx = {
54
+ name: task.name,
55
+ schedule: task.schedule,
56
+ timestamp: /* @__PURE__ */ new Date(),
57
+ runCount
58
+ };
59
+ await task.handler(ctx);
60
+ return {
61
+ ok: true,
62
+ duration: Date.now() - start
63
+ };
64
+ } catch (err) {
65
+ return {
66
+ ok: false,
67
+ duration: Date.now() - start,
68
+ error: err instanceof Error ? err : new Error(String(err))
69
+ };
70
+ }
71
+ }
72
+ function createCronContext(name, schedule) {
73
+ const runCount = ++getTaskRunCount().value;
74
+ return {
75
+ name,
76
+ schedule,
77
+ timestamp: /* @__PURE__ */ new Date(),
78
+ runCount
79
+ };
80
+ }
81
+ //#endregion
82
+ //#region src/cron-scheduler.ts
83
+ function parseField(field, min, max) {
84
+ const result = /* @__PURE__ */ new Set();
85
+ for (const part of field.split(",")) {
86
+ const trimmed = part.trim();
87
+ if (trimmed === "*") {
88
+ for (let i = min; i <= max; i++) result.add(i);
89
+ continue;
90
+ }
91
+ const stepMatch = trimmed.match(/^(.+)\/(\d+)$/);
92
+ let base = trimmed;
93
+ let step = 1;
94
+ if (stepMatch) {
95
+ base = stepMatch[1];
96
+ step = parseInt(stepMatch[2], 10);
97
+ }
98
+ if (base === "*") {
99
+ for (let i = min; i <= max; i += step) result.add(i);
100
+ continue;
101
+ }
102
+ const rangeMatch = base.match(/^(\d+)-(\d+)$/);
103
+ if (rangeMatch) {
104
+ const start = parseInt(rangeMatch[1], 10);
105
+ const end = parseInt(rangeMatch[2], 10);
106
+ for (let i = Math.max(min, start); i <= Math.min(max, end); i += step) result.add(i);
107
+ continue;
108
+ }
109
+ const value = parseInt(base, 10);
110
+ if (!isNaN(value) && value >= min && value <= max) result.add(value);
111
+ }
112
+ return Array.from(result).sort((a, b) => a - b);
113
+ }
114
+ function parseCron(schedule) {
115
+ const parts = schedule.trim().split(/\s+/);
116
+ if (parts.length < 5) return null;
117
+ const parsed = {
118
+ minute: parseField(parts[0], 0, 59),
119
+ hour: parseField(parts[1], 0, 23),
120
+ dom: parseField(parts[2], 1, 31),
121
+ month: parseField(parts[3], 1, 12),
122
+ dow: parseField(parts[4], 0, 6)
123
+ };
124
+ if (parsed.minute.length === 0 || parsed.hour.length === 0 || parsed.dom.length === 0 || parsed.month.length === 0 || parsed.dow.length === 0) return null;
125
+ return parsed;
126
+ }
127
+ function matches(date, parsed) {
128
+ return parsed.minute.includes(date.getMinutes()) && parsed.hour.includes(date.getHours()) && parsed.dom.includes(date.getDate()) && parsed.month.includes(date.getMonth() + 1) && parsed.dow.includes(date.getDay());
129
+ }
130
+ function nextMatch(from, parsed) {
131
+ const d = new Date(from);
132
+ d.setSeconds(0, 0);
133
+ d.setMinutes(d.getMinutes() + 1);
134
+ for (let i = 0; i < 527040; i++) {
135
+ if (matches(d, parsed)) return new Date(d);
136
+ d.setMinutes(d.getMinutes() + 1);
137
+ }
138
+ return null;
139
+ }
140
+ const taskRunCounts = /* @__PURE__ */ new Map();
141
+ function getRunCount(name) {
142
+ const c = taskRunCounts.get(name) || 0;
143
+ taskRunCounts.set(name, c + 1);
144
+ return c + 1;
145
+ }
146
+ function createMemoryCronScheduler(options = {}) {
147
+ let timer = null;
148
+ let running = false;
149
+ const taskTimers = /* @__PURE__ */ new Map();
150
+ const runOnStartExecuted = /* @__PURE__ */ new Set();
151
+ async function executeTask(task) {
152
+ const ctx = {
153
+ name: task.name,
154
+ schedule: task.schedule,
155
+ timestamp: /* @__PURE__ */ new Date(),
156
+ runCount: getRunCount(task.name)
157
+ };
158
+ const start = Date.now();
159
+ options.onTaskStart?.(task);
160
+ try {
161
+ const timeoutMs = task.meta.timeout || options.defaultTimeout || 3e4;
162
+ await Promise.race([task.handler(ctx), new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`Task "${task.name}" timed out after ${timeoutMs}ms`)), timeoutMs))]);
163
+ const result = {
164
+ ok: true,
165
+ duration: Date.now() - start
166
+ };
167
+ options.onTaskComplete?.(task, result);
168
+ return result;
169
+ } catch (err) {
170
+ const error = err instanceof Error ? err : new Error(String(err));
171
+ const result = {
172
+ ok: false,
173
+ duration: Date.now() - start,
174
+ error
175
+ };
176
+ options.onTaskError?.(task, error);
177
+ options.onTaskComplete?.(task, result);
178
+ return result;
179
+ }
180
+ }
181
+ function checkAndRun() {
182
+ const tasks = getScheduledTasks();
183
+ const now = /* @__PURE__ */ new Date();
184
+ for (const task of tasks) {
185
+ const parsed = parseCron(task.schedule);
186
+ if (!parsed) continue;
187
+ if (matches(now, parsed)) {
188
+ const timerKey = `${task.name}_${now.getFullYear()}-${now.getMonth()}-${now.getDate()}-${now.getHours()}-${now.getMinutes()}`;
189
+ if (taskTimers.has(timerKey)) continue;
190
+ if (runOnStartExecuted.has(task.name)) {
191
+ runOnStartExecuted.delete(task.name);
192
+ taskTimers.set(timerKey, setTimeout(() => {
193
+ taskTimers.delete(timerKey);
194
+ }, 6e4));
195
+ continue;
196
+ }
197
+ taskTimers.set(timerKey, setTimeout(() => {
198
+ taskTimers.delete(timerKey);
199
+ }, 6e4));
200
+ executeTask(task);
201
+ }
202
+ }
203
+ }
204
+ return {
205
+ async start() {
206
+ if (running) return;
207
+ running = true;
208
+ const tasks = getScheduledTasks();
209
+ for (const task of tasks) if (task.meta.runOnStart) {
210
+ runOnStartExecuted.add(task.name);
211
+ await executeTask(task);
212
+ }
213
+ timer = setInterval(checkAndRun, 3e4);
214
+ },
215
+ async stop() {
216
+ running = false;
217
+ if (timer) {
218
+ clearInterval(timer);
219
+ timer = null;
220
+ }
221
+ for (const t of taskTimers.values()) clearTimeout(t);
222
+ taskTimers.clear();
223
+ },
224
+ async runTask(name) {
225
+ const task = getScheduledTasks().find((t) => t.name === name);
226
+ if (!task) throw new Error(`[ubean] Cron task "${name}" not found`);
227
+ return executeTask(task);
228
+ },
229
+ isRunning() {
230
+ return running;
231
+ },
232
+ getTasks() {
233
+ return getScheduledTasks();
234
+ },
235
+ getNextRuns() {
236
+ return getScheduledTasks().map((task) => {
237
+ const parsed = parseCron(task.schedule);
238
+ return {
239
+ name: task.name,
240
+ nextRun: parsed ? nextMatch(/* @__PURE__ */ new Date(), parsed) : null
241
+ };
242
+ });
243
+ }
244
+ };
245
+ }
246
+ function startCronScheduler(options = {}) {
247
+ const scheduler = createMemoryCronScheduler(options);
248
+ scheduler.start();
249
+ return scheduler;
250
+ }
251
+ function resetCronRunCounts() {
252
+ taskRunCounts.clear();
253
+ }
254
+ function validateCron(schedule) {
255
+ return parseCron(schedule) !== null;
256
+ }
257
+ //#endregion
258
+ export { validateCron as a, defineScheduled as c, startCronScheduler as i, getScheduledTasks as l, parseCron as n, clearScheduledTasks as o, resetCronRunCounts as r, createCronContext as s, createMemoryCronScheduler as t, runScheduledTask as u };
@@ -0,0 +1,67 @@
1
+ //#region src/database.d.ts
2
+ interface DatabaseHooks {
3
+ 'db:connect': (db: Database) => void | Promise<void>;
4
+ 'db:disconnect': (db: Database) => void | Promise<void>;
5
+ 'db:query': (query: string, params?: unknown[]) => void | Promise<void>;
6
+ 'db:error': (error: Error, query?: string) => void | Promise<void>;
7
+ }
8
+ interface Database {
9
+ sql: <T = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]) => Promise<{
10
+ rows: T[];
11
+ }>;
12
+ exec: (query: string) => Promise<void>;
13
+ close: () => Promise<void>;
14
+ }
15
+ interface DatabaseConnector {
16
+ (options?: Record<string, unknown>): DatabaseConnectorInstance;
17
+ }
18
+ interface DatabaseConnectorInstance {
19
+ dialect?: string;
20
+ [key: string]: unknown;
21
+ }
22
+ interface DrizzleConfig {
23
+ schema?: Record<string, unknown>;
24
+ logger?: boolean | {
25
+ logQuery?: (query: string, params: unknown[]) => void;
26
+ };
27
+ casing?: 'camelCase' | 'snake_case';
28
+ }
29
+ interface DatabaseOptions {
30
+ connector?: DatabaseConnectorInstance;
31
+ connectors?: Record<string, DatabaseConnectorInstance>;
32
+ default?: string;
33
+ }
34
+ interface Migration {
35
+ name: string;
36
+ up: string;
37
+ down?: string;
38
+ }
39
+ declare const RAW_SQL: unique symbol;
40
+ interface RawSqlValue {
41
+ [RAW_SQL]: true;
42
+ value: string;
43
+ }
44
+ declare function rawSql(str: string): RawSqlValue;
45
+ type RawSqlFn = <T = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]) => Promise<{
46
+ rows: T[];
47
+ }>;
48
+ type RawExecFn = (query: string) => Promise<void>;
49
+ type RawCloseFn = () => Promise<void>;
50
+ declare function defineDatabase(options?: DatabaseOptions): Database;
51
+ declare function useDatabase(name?: string): Database;
52
+ declare function closeDatabases(): Promise<void>;
53
+ declare function getDatabaseHooks(): import("hookable").Hookable<DatabaseHooks, import("hookable").HookKeys<DatabaseHooks>>;
54
+ declare function registerDb0Create(fn: (connector: DatabaseConnectorInstance) => {
55
+ sql: RawSqlFn;
56
+ exec: RawExecFn;
57
+ close: RawCloseFn;
58
+ }): void;
59
+ declare function migrateDatabase(db: Database, migrations: string[]): Promise<void>;
60
+ declare function runMigrations(db: Database, migrations: Migration[], options?: {
61
+ table?: string;
62
+ log?: boolean;
63
+ }): Promise<{
64
+ applied: string[];
65
+ }>;
66
+ //#endregion
67
+ export { DatabaseOptions as a, closeDatabases as c, migrateDatabase as d, rawSql as f, useDatabase as h, DatabaseHooks as i, defineDatabase as l, runMigrations as m, DatabaseConnector as n, DrizzleConfig as o, registerDb0Create as p, DatabaseConnectorInstance as r, Migration as s, Database as t, getDatabaseHooks as u };