@simplysm/core-common 13.0.97 → 13.0.98

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.
@@ -0,0 +1,143 @@
1
+ # Features
2
+
3
+ Feature classes for async control flow and event handling.
4
+
5
+ Source: `src/features/*.ts`
6
+
7
+ ---
8
+
9
+ ## `EventEmitter`
10
+
11
+ Type-safe event emitter that can be used in both browsers and Node.js. Internally implemented using `EventTarget`. Supports the `using` statement (`Symbol.dispose`).
12
+
13
+ ```typescript
14
+ export class EventEmitter<
15
+ TEvents extends { [K in keyof TEvents]: unknown } = Record<string, unknown>,
16
+ > {
17
+ /**
18
+ * Register an event listener
19
+ * @note Duplicate registration of the same listener to the same event is ignored
20
+ */
21
+ on<TEventName extends keyof TEvents & string>(
22
+ type: TEventName,
23
+ listener: (data: TEvents[TEventName]) => void,
24
+ ): void;
25
+
26
+ /** Remove an event listener */
27
+ off<TEventName extends keyof TEvents & string>(
28
+ type: TEventName,
29
+ listener: (data: TEvents[TEventName]) => void,
30
+ ): void;
31
+
32
+ /**
33
+ * Emit an event
34
+ * @param args Event data (omitted if void type)
35
+ */
36
+ emit<TEventName extends keyof TEvents & string>(
37
+ type: TEventName,
38
+ ...args: TEvents[TEventName] extends void ? [] : [data: TEvents[TEventName]]
39
+ ): void;
40
+
41
+ /** Return the number of listeners for a specific event */
42
+ listenerCount<TEventName extends keyof TEvents & string>(type: TEventName): number;
43
+
44
+ /** Remove all event listeners */
45
+ dispose(): void;
46
+
47
+ [Symbol.dispose](): void;
48
+ }
49
+ ```
50
+
51
+ **Example:**
52
+
53
+ ```typescript
54
+ interface MyEvents {
55
+ data: string;
56
+ error: Error;
57
+ done: void;
58
+ }
59
+
60
+ class MyEmitter extends EventEmitter<MyEvents> {}
61
+
62
+ const emitter = new MyEmitter();
63
+ emitter.on("data", (data) => console.log(data)); // data: string
64
+ emitter.emit("data", "hello");
65
+ emitter.emit("done"); // void type is called without arguments
66
+ ```
67
+
68
+ ---
69
+
70
+ ## `DebounceQueue`
71
+
72
+ Asynchronous debounce queue. When called multiple times within a short time, only the last request is executed and previous requests are ignored. Extends `EventEmitter<{ error: SdError }>`. Supports the `using` statement.
73
+
74
+ Requests added during execution are processed immediately after the current execution completes without debounce delay.
75
+
76
+ ```typescript
77
+ export class DebounceQueue extends EventEmitter<{ error: SdError }> {
78
+ /**
79
+ * @param _delay Debounce delay time (milliseconds). If omitted, executes immediately (next event loop)
80
+ */
81
+ constructor(delay?: number);
82
+
83
+ /** Clean up pending tasks and timers */
84
+ dispose(): void;
85
+
86
+ [Symbol.dispose](): void;
87
+
88
+ /**
89
+ * Add a function to the queue
90
+ * If there was a previously added function, it will be replaced
91
+ */
92
+ run(fn: () => void | Promise<void>): void;
93
+ }
94
+ ```
95
+
96
+ **Error handling:** If there are `"error"` event listeners, errors are emitted as events. Otherwise, errors are logged via `consola`.
97
+
98
+ **Example:**
99
+
100
+ ```typescript
101
+ const queue = new DebounceQueue(300); // 300ms delay
102
+ queue.run(() => console.log("1")); // ignored
103
+ queue.run(() => console.log("2")); // ignored
104
+ queue.run(() => console.log("3")); // executed after 300ms
105
+
106
+ queue.on("error", (err) => console.error(err));
107
+ ```
108
+
109
+ ---
110
+
111
+ ## `SerialQueue`
112
+
113
+ Asynchronous serial queue. Functions added to the queue are executed sequentially. The next task starts only after one task completes. Subsequent tasks continue to execute even if an error occurs. Extends `EventEmitter<{ error: SdError }>`. Supports the `using` statement.
114
+
115
+ ```typescript
116
+ export class SerialQueue extends EventEmitter<{ error: SdError }> {
117
+ /**
118
+ * @param gap Gap between each task (ms). Default: 0
119
+ */
120
+ constructor(gap?: number);
121
+
122
+ /** Clear pending queue (currently executing task will complete) */
123
+ dispose(): void;
124
+
125
+ [Symbol.dispose](): void;
126
+
127
+ /** Add a function to the queue and execute it */
128
+ run(fn: () => void | Promise<void>): void;
129
+ }
130
+ ```
131
+
132
+ **Error handling:** Same as `DebounceQueue` — emitted as event if listeners exist, otherwise logged.
133
+
134
+ **Example:**
135
+
136
+ ```typescript
137
+ const queue = new SerialQueue();
138
+ queue.run(async () => { await fetch("/api/1"); });
139
+ queue.run(async () => { await fetch("/api/2"); }); // executed after 1 completes
140
+ queue.run(async () => { await fetch("/api/3"); }); // executed after 2 completes
141
+
142
+ queue.on("error", (err) => console.error(err));
143
+ ```
package/docs/types.md ADDED
@@ -0,0 +1,287 @@
1
+ # Types
2
+
3
+ Data types and common type definitions.
4
+
5
+ Source: `src/types/*.ts`, `src/common.types.ts`, `src/env.ts`
6
+
7
+ ---
8
+
9
+ ## `Uuid`
10
+
11
+ UUID v4 class. Generates cryptographically secure UUIDs based on `crypto.getRandomValues` (Chrome 79+, Node.js compatible).
12
+
13
+ ```typescript
14
+ export class Uuid {
15
+ /** Create new UUID v4 instance */
16
+ static generate(): Uuid;
17
+
18
+ /**
19
+ * Create UUID from 16-byte Uint8Array
20
+ * @throws {ArgumentError} If byte size is not 16
21
+ */
22
+ static fromBytes(bytes: Bytes): Uuid;
23
+
24
+ /**
25
+ * @param uuid UUID string (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
26
+ * @throws {ArgumentError} If format is invalid
27
+ */
28
+ constructor(uuid: string);
29
+
30
+ /** Convert UUID to string */
31
+ toString(): string;
32
+
33
+ /** Convert UUID to 16-byte Uint8Array */
34
+ toBytes(): Bytes;
35
+ }
36
+ ```
37
+
38
+ ---
39
+
40
+ ## `DateTime`
41
+
42
+ Immutable date+time class wrapping JavaScript `Date`. Supports millisecond precision and operates based on local timezone.
43
+
44
+ ```typescript
45
+ export class DateTime {
46
+ readonly date: Date;
47
+
48
+ /** Create with current time */
49
+ constructor();
50
+ /** Create with year, month, day, hour, minute, second, millisecond */
51
+ constructor(year: number, month: number, day: number, hour?: number, minute?: number, second?: number, millisecond?: number);
52
+ /** Create from tick (millisecond) */
53
+ constructor(tick: number);
54
+ /** Create from Date object */
55
+ constructor(date: Date);
56
+
57
+ /**
58
+ * Parse a string to create DateTime instance
59
+ * Supported formats: 'yyyy-MM-dd HH:mm:ss', 'yyyy-MM-dd HH:mm:ss.fff', 'yyyyMMddHHmmss',
60
+ * 'yyyy-MM-dd AM/PM HH:mm:ss', ISO 8601
61
+ * @throws ArgumentError If unsupported format
62
+ */
63
+ static parse(str: string): DateTime;
64
+ }
65
+ ```
66
+
67
+ **Getters** (read-only): `year`, `month`, `day`, `hour`, `minute`, `second`, `millisecond`, `tick`, `dayOfWeek` (0=Sunday), `timezoneOffsetMinutes`, `isValid`
68
+
69
+ **Immutable setters** (return new instance): `setYear(year)`, `setMonth(month)`, `setDay(day)`, `setHour(hour)`, `setMinute(minute)`, `setSecond(second)`, `setMillisecond(millisecond)`
70
+
71
+ **Arithmetic** (return new instance): `addYears(n)`, `addMonths(n)`, `addDays(n)`, `addHours(n)`, `addMinutes(n)`, `addSeconds(n)`, `addMilliseconds(n)`
72
+
73
+ **Formatting**: `toFormatString(formatStr)`, `toString()` (default: `"yyyy-MM-ddTHH:mm:ss.fffzzz"`)
74
+
75
+ ---
76
+
77
+ ## `DateOnly`
78
+
79
+ Immutable date class (without time: `yyyy-MM-dd`). Operates based on local timezone.
80
+
81
+ ```typescript
82
+ export class DateOnly {
83
+ readonly date: Date;
84
+
85
+ /** Current date */
86
+ constructor();
87
+ /** Initialize with year, month, day */
88
+ constructor(year: number, month: number, day: number);
89
+ /** Create from tick (millisecond) */
90
+ constructor(tick: number);
91
+ /** Create from Date type */
92
+ constructor(date: Date);
93
+
94
+ /**
95
+ * Parse a string into DateOnly
96
+ * Supported formats: 'yyyy-MM-dd', 'yyyyMMdd', ISO 8601
97
+ */
98
+ static parse(str: string): DateOnly;
99
+
100
+ /**
101
+ * Get the start date of a week based on week information
102
+ */
103
+ static getDateByYearWeekSeq(
104
+ arg: { year: number; month?: number; weekSeq: number },
105
+ weekStartDay?: number,
106
+ minDaysInFirstWeek?: number,
107
+ ): DateOnly;
108
+ }
109
+ ```
110
+
111
+ **Getters** (read-only): `year`, `month`, `day`, `tick`, `dayOfWeek`, `isValid`
112
+
113
+ **Immutable setters**: `setYear(year)`, `setMonth(month)`, `setDay(day)`
114
+
115
+ **Arithmetic**: `addYears(n)`, `addMonths(n)`, `addDays(n)`
116
+
117
+ **Week calculation methods**:
118
+
119
+ ```typescript
120
+ getBaseYearMonthSeqForWeekSeq(weekStartDay?: number, minDaysInFirstWeek?: number): { year: number; monthSeq: number };
121
+ getWeekSeqStartDate(weekStartDay?: number, minDaysInFirstWeek?: number): DateOnly;
122
+ getWeekSeqOfYear(weekStartDay?: number, minDaysInFirstWeek?: number): { year: number; weekSeq: number };
123
+ getWeekSeqOfMonth(weekStartDay?: number, minDaysInFirstWeek?: number): { year: number; monthSeq: number; weekSeq: number };
124
+ ```
125
+
126
+ Defaults: `weekStartDay = 1` (Monday), `minDaysInFirstWeek = 4` (ISO 8601 standard).
127
+
128
+ **Formatting**: `toFormatString(formatStr)`, `toString()` (default: `"yyyy-MM-dd"`)
129
+
130
+ ---
131
+
132
+ ## `Time`
133
+
134
+ Immutable time class (without date: `HH:mm:ss.fff`). Values exceeding 24 hours or negative values are automatically normalized (24-hour wraparound).
135
+
136
+ ```typescript
137
+ export class Time {
138
+ /** Create with current time */
139
+ constructor();
140
+ /** Create with hour, minute, second, millisecond */
141
+ constructor(hour: number, minute: number, second?: number, millisecond?: number);
142
+ /** Create from tick (millisecond) */
143
+ constructor(tick: number);
144
+ /** Create by extracting time part only from Date object */
145
+ constructor(date: Date);
146
+
147
+ /**
148
+ * Parse a string to create Time instance
149
+ * Supported formats: 'HH:mm:ss', 'HH:mm:ss.fff', 'AM/PM HH:mm:ss', ISO 8601 (extract time part)
150
+ * @throws ArgumentError If unsupported format
151
+ */
152
+ static parse(str: string): Time;
153
+ }
154
+ ```
155
+
156
+ **Getters**: `hour`, `minute`, `second`, `millisecond`, `tick`, `isValid`
157
+
158
+ **Immutable setters**: `setHour(hour)`, `setMinute(minute)`, `setSecond(second)`, `setMillisecond(millisecond)`
159
+
160
+ **Arithmetic** (24-hour wraparound): `addHours(n)`, `addMinutes(n)`, `addSeconds(n)`, `addMilliseconds(n)`
161
+
162
+ **Formatting**: `toFormatString(formatStr)`, `toString()` (default: `"HH:mm:ss.fff"`)
163
+
164
+ ---
165
+
166
+ ## `LazyGcMap`
167
+
168
+ Map with automatic expiration feature. Updates access time in LRU manner; auto-deletes if not accessed for specified time. Supports the `using` statement (`Symbol.dispose`).
169
+
170
+ ```typescript
171
+ export class LazyGcMap<TKey, TValue> {
172
+ /**
173
+ * @param options.gcInterval GC interval in ms. Default: 1/10 of expireTime (minimum 1000ms)
174
+ * @param options.expireTime Expiration time in ms since last access
175
+ * @param options.onExpire Callback called on expiration (can be async)
176
+ */
177
+ constructor(options: {
178
+ gcInterval?: number;
179
+ expireTime: number;
180
+ onExpire?: (key: TKey, value: TValue) => void | Promise<void>;
181
+ });
182
+
183
+ get size(): number;
184
+ has(key: TKey): boolean;
185
+ get(key: TKey): TValue | undefined;
186
+ set(key: TKey, value: TValue): void;
187
+ delete(key: TKey): boolean;
188
+ clear(): void;
189
+ getOrCreate(key: TKey, factory: () => TValue): TValue;
190
+ values(): IterableIterator<TValue>;
191
+ keys(): IterableIterator<TKey>;
192
+ entries(): IterableIterator<[TKey, TValue]>;
193
+ dispose(): void;
194
+ [Symbol.dispose](): void;
195
+ }
196
+ ```
197
+
198
+ **Example:**
199
+
200
+ ```typescript
201
+ using map = new LazyGcMap({ gcInterval: 10000, expireTime: 60000 });
202
+ map.set("session", data);
203
+ const val = map.getOrCreate("key", () => computeExpensive());
204
+ ```
205
+
206
+ ---
207
+
208
+ ## `Bytes`
209
+
210
+ Binary type alias used instead of `Buffer`.
211
+
212
+ ```typescript
213
+ export type Bytes = Uint8Array;
214
+ ```
215
+
216
+ ---
217
+
218
+ ## `PrimitiveTypeMap`
219
+
220
+ Primitive type mapping shared with orm-common.
221
+
222
+ ```typescript
223
+ export type PrimitiveTypeMap = {
224
+ string: string;
225
+ number: number;
226
+ boolean: boolean;
227
+ DateTime: DateTime;
228
+ DateOnly: DateOnly;
229
+ Time: Time;
230
+ Uuid: Uuid;
231
+ Bytes: Bytes;
232
+ };
233
+ ```
234
+
235
+ ---
236
+
237
+ ## `PrimitiveTypeStr`
238
+
239
+ ```typescript
240
+ export type PrimitiveTypeStr = keyof PrimitiveTypeMap;
241
+ ```
242
+
243
+ ---
244
+
245
+ ## `PrimitiveType`
246
+
247
+ ```typescript
248
+ export type PrimitiveType = PrimitiveTypeMap[PrimitiveTypeStr] | undefined;
249
+ ```
250
+
251
+ ---
252
+
253
+ ## `DeepPartial<T>`
254
+
255
+ Recursively makes all properties of an object optional. Primitive types are kept as-is.
256
+
257
+ ```typescript
258
+ export type DeepPartial<TObject> = Partial<{
259
+ [K in keyof TObject]: TObject[K] extends PrimitiveType ? TObject[K] : DeepPartial<TObject[K]>;
260
+ }>;
261
+ ```
262
+
263
+ ---
264
+
265
+ ## `Type<T>`
266
+
267
+ Constructor type used for dependency injection, factory patterns, and `instanceof` checks.
268
+
269
+ ```typescript
270
+ export interface Type<TInstance> extends Function {
271
+ new (...args: unknown[]): TInstance;
272
+ }
273
+ ```
274
+
275
+ ---
276
+
277
+ ## `env`
278
+
279
+ Unified environment variable accessor. Merges `import.meta.env` and `process.env`. `DEV` is parsed as boolean, `VER` as optional string.
280
+
281
+ ```typescript
282
+ export const env: {
283
+ DEV: boolean;
284
+ VER?: string;
285
+ [key: string]: unknown;
286
+ };
287
+ ```