@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.
- package/README.md +160 -0
- package/docs/errors.md +119 -0
- package/docs/extensions.md +387 -0
- package/docs/features.md +143 -0
- package/docs/types.md +287 -0
- package/docs/utils.md +708 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# @simplysm/core-common
|
|
2
|
+
|
|
3
|
+
Core module (common) — platform-neutral core utilities for the Simplysm framework.
|
|
4
|
+
|
|
5
|
+
Provides error classes, immutable date/time types, prototype extensions, event handling, and utility namespaces that work in both browser and Node.js environments.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @simplysm/core-common
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Side-Effect Imports
|
|
14
|
+
|
|
15
|
+
Importing the package entry point (`@simplysm/core-common`) automatically patches `Array`, `Map`, and `Set` prototypes with extension methods. These are declared as side effects in `package.json`:
|
|
16
|
+
|
|
17
|
+
- `extensions/arr-ext` — Array prototype extensions
|
|
18
|
+
- `extensions/map-ext` — Map prototype extensions
|
|
19
|
+
- `extensions/set-ext` — Set prototype extensions
|
|
20
|
+
|
|
21
|
+
If you import only specific modules (e.g., `@simplysm/core-common/dist/types/date-time`), the prototype extensions are **not** applied unless you also import the entry point or the extension modules directly.
|
|
22
|
+
|
|
23
|
+
## API Overview
|
|
24
|
+
|
|
25
|
+
### Errors
|
|
26
|
+
|
|
27
|
+
| API | Type | Description |
|
|
28
|
+
|-----|------|-------------|
|
|
29
|
+
| `SdError` | class | Error with tree-structured cause chaining |
|
|
30
|
+
| `ArgumentError` | class | Invalid argument error with YAML-formatted details |
|
|
31
|
+
| `NotImplementedError` | class | Unimplemented feature error |
|
|
32
|
+
| `TimeoutError` | class | Waiting time exceeded error |
|
|
33
|
+
|
|
34
|
+
-> See [docs/errors.md](./docs/errors.md) for details.
|
|
35
|
+
|
|
36
|
+
### Types
|
|
37
|
+
|
|
38
|
+
| API | Type | Description |
|
|
39
|
+
|-----|------|-------------|
|
|
40
|
+
| `Uuid` | class | UUID v4 generation and parsing |
|
|
41
|
+
| `DateTime` | class | Immutable date+time (millisecond precision) |
|
|
42
|
+
| `DateOnly` | class | Immutable date without time |
|
|
43
|
+
| `Time` | class | Immutable time without date (24h wraparound) |
|
|
44
|
+
| `LazyGcMap` | class | Map with LRU-based automatic expiration |
|
|
45
|
+
| `Bytes` | type alias | `Uint8Array` (replaces `Buffer`) |
|
|
46
|
+
| `PrimitiveTypeMap` | type | Mapping of type name strings to types |
|
|
47
|
+
| `PrimitiveTypeStr` | type | `keyof PrimitiveTypeMap` |
|
|
48
|
+
| `PrimitiveType` | type | Union of all primitive types |
|
|
49
|
+
| `DeepPartial<T>` | type | Recursively makes all properties optional |
|
|
50
|
+
| `Type<T>` | interface | Constructor type for DI and factory patterns |
|
|
51
|
+
| `env` | const | Unified `DEV` / `VER` environment accessor |
|
|
52
|
+
|
|
53
|
+
-> See [docs/types.md](./docs/types.md) for details.
|
|
54
|
+
|
|
55
|
+
### Features
|
|
56
|
+
|
|
57
|
+
| API | Type | Description |
|
|
58
|
+
|-----|------|-------------|
|
|
59
|
+
| `EventEmitter` | class | Type-safe event emitter (EventTarget wrapper) |
|
|
60
|
+
| `DebounceQueue` | class | Async debounce — only the last call executes |
|
|
61
|
+
| `SerialQueue` | class | Async serial execution queue |
|
|
62
|
+
|
|
63
|
+
-> See [docs/features.md](./docs/features.md) for details.
|
|
64
|
+
|
|
65
|
+
### Prototype Extensions (side-effect)
|
|
66
|
+
|
|
67
|
+
| API | Type | Description |
|
|
68
|
+
|-----|------|-------------|
|
|
69
|
+
| `Array` extensions | prototype | 34 methods — query, transform, diff, sort, mutate |
|
|
70
|
+
| `Map` extensions | prototype | `getOrCreate`, `update` |
|
|
71
|
+
| `Set` extensions | prototype | `adds`, `toggle` |
|
|
72
|
+
|
|
73
|
+
-> See [docs/extensions.md](./docs/extensions.md) for details.
|
|
74
|
+
|
|
75
|
+
### Utilities
|
|
76
|
+
|
|
77
|
+
| API | Type | Description |
|
|
78
|
+
|-----|------|-------------|
|
|
79
|
+
| `obj` | namespace | clone, equal, merge, merge3, omit, pick, chain access |
|
|
80
|
+
| `str` | namespace | Korean particles, case conversion, full-width replacement |
|
|
81
|
+
| `num` | namespace | parseInt, parseFloat, format with separators |
|
|
82
|
+
| `bytes` | namespace | concat, hex, base64 conversion |
|
|
83
|
+
| `path` | namespace | POSIX-only join, basename, extname |
|
|
84
|
+
| `json` | namespace | Custom-type-aware JSON stringify/parse |
|
|
85
|
+
| `xml` | namespace | XML parse/stringify via fast-xml-parser |
|
|
86
|
+
| `wait` | namespace | `until` (polling) and `time` (delay) |
|
|
87
|
+
| `transfer` | namespace | Worker-safe encode/decode for custom types |
|
|
88
|
+
| `err` | namespace | Extract message from unknown error |
|
|
89
|
+
| `dt` | namespace | Date format, month normalization, 12h/24h conversion |
|
|
90
|
+
| `primitive` | namespace | Infer `PrimitiveTypeStr` from a value |
|
|
91
|
+
| `js`, `ts`, `html`, `tsql`, `mysql`, `pgsql` | function | Template tag functions for syntax highlighting |
|
|
92
|
+
| `ZipArchive` | class | ZIP read/write/compress with caching |
|
|
93
|
+
|
|
94
|
+
-> See [docs/utils.md](./docs/utils.md) for details.
|
|
95
|
+
|
|
96
|
+
## Quick Usage Examples
|
|
97
|
+
|
|
98
|
+
### DateTime (immutable date/time)
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { DateTime, DateOnly } from "@simplysm/core-common";
|
|
102
|
+
|
|
103
|
+
const now = new DateTime();
|
|
104
|
+
const tomorrow = now.addDays(1);
|
|
105
|
+
const formatted = now.toFormatString("yyyy-MM-dd HH:mm:ss");
|
|
106
|
+
const parsed = DateTime.parse("2025-01-15 10:30:00");
|
|
107
|
+
|
|
108
|
+
const today = new DateOnly();
|
|
109
|
+
const weekInfo = today.getWeekSeqOfYear(); // { year, weekSeq }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Object utilities
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { obj, json } from "@simplysm/core-common";
|
|
116
|
+
|
|
117
|
+
const cloned = obj.clone(deepObject);
|
|
118
|
+
const isEqual = obj.equal(a, b, { ignoreArrayIndex: true });
|
|
119
|
+
const merged = obj.merge(base, override);
|
|
120
|
+
const value = obj.getChainValue(data, "a.b[0].c");
|
|
121
|
+
|
|
122
|
+
// Custom-type-aware JSON
|
|
123
|
+
const str = json.stringify({ date: new DateTime(), id: Uuid.generate() });
|
|
124
|
+
const restored = json.parse(str); // DateTime and Uuid instances restored
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Array extensions
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import "@simplysm/core-common";
|
|
131
|
+
|
|
132
|
+
const items = [3, 1, 4, 1, 5];
|
|
133
|
+
items.distinct(); // [3, 1, 4, 5]
|
|
134
|
+
items.orderBy(); // [1, 1, 3, 4, 5]
|
|
135
|
+
items.sum(); // 14
|
|
136
|
+
items.groupBy((x) => x % 2 === 0 ? "even" : "odd");
|
|
137
|
+
|
|
138
|
+
const users = [{ id: 1, name: "A" }, { id: 2, name: "B" }];
|
|
139
|
+
users.toMap((u) => u.id); // Map { 1 => {...}, 2 => {...} }
|
|
140
|
+
users.single((u) => u.id === 1); // { id: 1, name: "A" }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### EventEmitter
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { EventEmitter } from "@simplysm/core-common";
|
|
147
|
+
|
|
148
|
+
interface MyEvents { data: string; done: void }
|
|
149
|
+
|
|
150
|
+
class MyService extends EventEmitter<MyEvents> {}
|
|
151
|
+
|
|
152
|
+
const svc = new MyService();
|
|
153
|
+
svc.on("data", (msg) => console.log(msg));
|
|
154
|
+
svc.emit("data", "hello");
|
|
155
|
+
svc.emit("done");
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
Apache-2.0
|
package/docs/errors.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Errors
|
|
2
|
+
|
|
3
|
+
Error classes for the Simplysm framework. All classes extend `SdError`, which itself extends `Error`.
|
|
4
|
+
|
|
5
|
+
Source: `src/errors/*.ts`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## `SdError`
|
|
10
|
+
|
|
11
|
+
Error class supporting tree-structured cause chaining. Utilizes ES2024 `cause` property. Messages are joined in reverse order with ` => ` separator, and cause stack traces are appended.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
export class SdError extends Error {
|
|
15
|
+
override cause?: Error;
|
|
16
|
+
|
|
17
|
+
/** Create by wrapping a cause error. Messages are joined in reverse order (upper message => lower message => cause message) */
|
|
18
|
+
constructor(cause: Error, ...messages: string[]);
|
|
19
|
+
/** Create with messages only. Messages are joined in reverse order (upper message => lower message) */
|
|
20
|
+
constructor(...messages: string[]);
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Example:**
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
try {
|
|
28
|
+
await fetch(url);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
throw new SdError(err, "API call failed", "User load failed");
|
|
31
|
+
}
|
|
32
|
+
// Result message: "User load failed => API call failed => original error message"
|
|
33
|
+
|
|
34
|
+
throw new SdError("invalid state", "processing not possible");
|
|
35
|
+
// Result message: "processing not possible => invalid state"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## `ArgumentError`
|
|
41
|
+
|
|
42
|
+
An error thrown when invalid arguments are received. Includes the argument object in YAML format in the message to facilitate debugging. Extends `SdError`.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
export class ArgumentError extends SdError {
|
|
46
|
+
/** Output argument object in YAML format with default message ("Invalid arguments.") */
|
|
47
|
+
constructor(argObj: Record<string, unknown>);
|
|
48
|
+
/** Output argument object in YAML format with a custom message */
|
|
49
|
+
constructor(message: string, argObj: Record<string, unknown>);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Example:**
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
throw new ArgumentError({ userId: 123, name: null });
|
|
57
|
+
// Result message: "Invalid arguments.\n\nuserId: 123\nname: null"
|
|
58
|
+
|
|
59
|
+
throw new ArgumentError("Invalid user", { userId: 123 });
|
|
60
|
+
// Result message: "Invalid user\n\nuserId: 123"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## `NotImplementedError`
|
|
66
|
+
|
|
67
|
+
An error thrown when a feature that has not yet been implemented is called. Extends `SdError`.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
export class NotImplementedError extends SdError {
|
|
71
|
+
/**
|
|
72
|
+
* @param message Additional description message
|
|
73
|
+
*/
|
|
74
|
+
constructor(message?: string);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Example:**
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
class BaseService {
|
|
82
|
+
process(): void {
|
|
83
|
+
throw new NotImplementedError("Implementation required in subclass");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
switch (type) {
|
|
88
|
+
case "A": return handleA();
|
|
89
|
+
case "B": throw new NotImplementedError(`Handling for type ${type}`);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## `TimeoutError`
|
|
96
|
+
|
|
97
|
+
An error that occurs when the waiting time is exceeded. Automatically thrown when the maximum number of attempts is exceeded in `wait.until()`. Extends `SdError`.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
export class TimeoutError extends SdError {
|
|
101
|
+
/**
|
|
102
|
+
* @param count Number of attempts
|
|
103
|
+
* @param message Additional message
|
|
104
|
+
*/
|
|
105
|
+
constructor(count?: number, message?: string);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Example:**
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
try {
|
|
113
|
+
await wait.until(() => isReady, 100, 50);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
if (err instanceof TimeoutError) {
|
|
116
|
+
// "Waiting time exceeded(50 attempts)"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# Prototype Extensions (side-effect)
|
|
2
|
+
|
|
3
|
+
Prototype extensions for `Array`, `Map`, and `Set`. These are applied as side effects when importing `@simplysm/core-common`.
|
|
4
|
+
|
|
5
|
+
**Important:** Importing the package entry point automatically patches these prototypes. If you import only specific sub-modules, you must also import the extension modules or the entry point to get these methods.
|
|
6
|
+
|
|
7
|
+
Source: `src/extensions/*.ts`
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Array Extensions
|
|
12
|
+
|
|
13
|
+
### Readonly methods (return new array or value, do not mutate)
|
|
14
|
+
|
|
15
|
+
#### `single`
|
|
16
|
+
|
|
17
|
+
Return single element matching condition. Throws `ArgumentError` if 2+ elements match.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
single(predicate?: (item: T, index: number) => boolean): T | undefined;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
#### `first`
|
|
24
|
+
|
|
25
|
+
Return first element.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
first(predicate?: (item: T, index: number) => boolean): T | undefined;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### `last`
|
|
32
|
+
|
|
33
|
+
Return last element.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
last(predicate?: (item: T, index: number) => boolean): T | undefined;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### `filterAsync`
|
|
40
|
+
|
|
41
|
+
Async filter (sequential execution).
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
filterAsync(predicate: (item: T, index: number) => Promise<boolean>): Promise<T[]>;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### `filterExists`
|
|
48
|
+
|
|
49
|
+
Remove `null` and `undefined` values.
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
filterExists(): NonNullable<T>[];
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### `ofType`
|
|
56
|
+
|
|
57
|
+
Filter only elements of specific type (`PrimitiveTypeStr` or constructor type).
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
ofType<TKey extends PrimitiveTypeStr>(type: TKey): Extract<T, PrimitiveTypeMap[TKey]>[];
|
|
61
|
+
ofType<TNarrow extends T>(type: Type<TNarrow>): TNarrow[];
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### `mapAsync`
|
|
65
|
+
|
|
66
|
+
Async mapping (sequential execution).
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
mapAsync<R>(selector: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### `mapMany`
|
|
73
|
+
|
|
74
|
+
Flatten nested array, or map then flatten.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
mapMany(): T extends readonly (infer U)[] ? U[] : T;
|
|
78
|
+
mapMany<R>(selector: (item: T, index: number) => R[]): R[];
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### `mapManyAsync`
|
|
82
|
+
|
|
83
|
+
Async mapping and then flatten (sequential execution).
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
mapManyAsync<R>(selector: (item: T, index: number) => Promise<R[]>): Promise<R[]>;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `parallelAsync`
|
|
90
|
+
|
|
91
|
+
Async parallel processing using `Promise.all`.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
parallelAsync<R>(fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### `groupBy`
|
|
98
|
+
|
|
99
|
+
Group by key. O(n) for primitive keys, O(n^2) for object keys (deep comparison).
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
groupBy<K>(keySelector: (item: T, index: number) => K): { key: K; values: T[] }[];
|
|
103
|
+
groupBy<K, V>(
|
|
104
|
+
keySelector: (item: T, index: number) => K,
|
|
105
|
+
valueSelector: (item: T, index: number) => V,
|
|
106
|
+
): { key: K; values: V[] }[];
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### `toMap`
|
|
110
|
+
|
|
111
|
+
Convert to `Map`. Throws `ArgumentError` on duplicate keys.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
toMap<K>(keySelector: (item: T, index: number) => K): Map<K, T>;
|
|
115
|
+
toMap<K, V>(keySelector: (item: T, index: number) => K, valueSelector: (item: T, index: number) => V): Map<K, V>;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### `toMapAsync`
|
|
119
|
+
|
|
120
|
+
Async version of `toMap`.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
toMapAsync<K>(keySelector: (item: T, index: number) => Promise<K>): Promise<Map<K, T>>;
|
|
124
|
+
toMapAsync<K, V>(
|
|
125
|
+
keySelector: (item: T, index: number) => Promise<K> | K,
|
|
126
|
+
valueSelector: (item: T, index: number) => Promise<V> | V,
|
|
127
|
+
): Promise<Map<K, V>>;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### `toArrayMap`
|
|
131
|
+
|
|
132
|
+
Convert to `Map<K, T[]>` (groups values by key).
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
toArrayMap<K>(keySelector: (item: T, index: number) => K): Map<K, T[]>;
|
|
136
|
+
toArrayMap<K, V>(keySelector: (item: T, index: number) => K, valueSelector: (item: T, index: number) => V): Map<K, V[]>;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### `toSetMap`
|
|
140
|
+
|
|
141
|
+
Convert to `Map<K, Set<T>>`.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
toSetMap<K>(keySelector: (item: T, index: number) => K): Map<K, Set<T>>;
|
|
145
|
+
toSetMap<K, V>(keySelector: (item: T, index: number) => K, valueSelector: (item: T, index: number) => V): Map<K, Set<V>>;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### `toMapValues`
|
|
149
|
+
|
|
150
|
+
Group by key, then reduce each group's values.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
toMapValues<K, V>(
|
|
154
|
+
keySelector: (item: T, index: number) => K,
|
|
155
|
+
valueSelector: (items: T[]) => V,
|
|
156
|
+
): Map<K, V>;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### `toObject`
|
|
160
|
+
|
|
161
|
+
Convert to plain object. Throws `ArgumentError` on duplicate keys.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
toObject(keySelector: (item: T, index: number) => string): Record<string, T>;
|
|
165
|
+
toObject<V>(keySelector: (item: T, index: number) => string, valueSelector: (item: T, index: number) => V): Record<string, V>;
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### `toTree`
|
|
169
|
+
|
|
170
|
+
Convert flat array to tree structure. Items with `null`/`undefined` parent key become roots. Uses `toArrayMap` for O(n) complexity.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
toTree<K extends keyof T, P extends keyof T>(keyProp: K, parentKey: P): TreeArray<T>[];
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### `distinct`
|
|
177
|
+
|
|
178
|
+
Remove duplicates. Options: `matchAddress` for reference comparison, `keyFn` for custom key (O(n)). Without `keyFn` on objects: O(n^2).
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
distinct(options?: boolean | { matchAddress?: boolean; keyFn?: (item: T) => string | number }): T[];
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### `orderBy` / `orderByDesc`
|
|
185
|
+
|
|
186
|
+
Sort in ascending or descending order. Supports `string`, `number`, `DateTime`, `DateOnly`, `Time`.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
orderBy(selector?: (item: T) => string | number | DateTime | DateOnly | Time | undefined): T[];
|
|
190
|
+
orderByDesc(selector?: (item: T) => string | number | DateTime | DateOnly | Time | undefined): T[];
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### `diffs`
|
|
194
|
+
|
|
195
|
+
Compare two arrays and return INSERT / DELETE / UPDATE results.
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
diffs<P>(target: P[]): ArrayDiffsResult<T, P>[];
|
|
199
|
+
diffs<P>(target: P[], options: { keys: string[]; excludes?: string[] }): ArrayDiffsResult<T, P>[];
|
|
200
|
+
diffs<P>(target: P[], options: { excludes: string[] }): ArrayDiffsResult<T, P>[];
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### `oneWayDiffs`
|
|
204
|
+
|
|
205
|
+
One-way diff against original items. Returns `"create"`, `"update"`, or `"same"` for each item.
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
oneWayDiffs<K extends keyof T>(
|
|
209
|
+
orgItems: T[] | Map<T[K], T>,
|
|
210
|
+
keyPropNameOrGetValFn: K | ((item: T) => string | number | undefined),
|
|
211
|
+
options?: { includeSame?: boolean; excludes?: string[]; includes?: string[] },
|
|
212
|
+
): ArrayOneWayDiffResult<T>[];
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### `merge`
|
|
216
|
+
|
|
217
|
+
Merge source and target arrays based on diff results.
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
merge<P>(target: P[]): (T | P | (T & P))[];
|
|
221
|
+
merge<P>(target: P[], options: { keys: string[]; excludes?: string[] }): (T | P | (T & P))[];
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### `sum`
|
|
225
|
+
|
|
226
|
+
Return sum of elements. Returns `0` for empty arrays.
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
sum(selector?: (item: T, index: number) => number): number;
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### `min` / `max`
|
|
233
|
+
|
|
234
|
+
Return minimum or maximum value.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
min(): T extends number | string ? T | undefined : never;
|
|
238
|
+
min<P extends number | string>(selector?: (item: T, index: number) => P): P | undefined;
|
|
239
|
+
max(): T extends number | string ? T | undefined : never;
|
|
240
|
+
max<P extends number | string>(selector?: (item: T, index: number) => P): P | undefined;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### `shuffle`
|
|
244
|
+
|
|
245
|
+
Return a shuffled copy (Fisher-Yates algorithm).
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
shuffle(): T[];
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### Mutable methods (modify original array, marked `@mutates`)
|
|
254
|
+
|
|
255
|
+
#### `distinctThis`
|
|
256
|
+
|
|
257
|
+
Remove duplicates from original array.
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
distinctThis(options?: boolean | { matchAddress?: boolean; keyFn?: (item: T) => string | number }): T[];
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### `orderByThis` / `orderByDescThis`
|
|
264
|
+
|
|
265
|
+
Sort original array in ascending or descending order.
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
orderByThis(selector?: (item: T) => string | number | DateTime | DateOnly | Time | undefined): T[];
|
|
269
|
+
orderByDescThis(selector?: (item: T) => string | number | DateTime | DateOnly | Time | undefined): T[];
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### `insert`
|
|
273
|
+
|
|
274
|
+
Insert items at index.
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
insert(index: number, ...items: T[]): this;
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
#### `remove`
|
|
281
|
+
|
|
282
|
+
Remove item or items matching condition.
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
remove(item: T): this;
|
|
286
|
+
remove(selector: (item: T, index: number) => boolean): this;
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
#### `toggle`
|
|
290
|
+
|
|
291
|
+
Toggle item in array (remove if exists, add if not).
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
toggle(item: T): this;
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
#### `clear`
|
|
298
|
+
|
|
299
|
+
Clear all items from array.
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
clear(): this;
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Exported Types
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
export type ArrayDiffsResult<TOriginal, TOther> =
|
|
311
|
+
| { source: undefined; target: TOther } // INSERT
|
|
312
|
+
| { source: TOriginal; target: undefined } // DELETE
|
|
313
|
+
| { source: TOriginal; target: TOther }; // UPDATE
|
|
314
|
+
|
|
315
|
+
export type ArrayOneWayDiffResult<TItem> =
|
|
316
|
+
| { type: "create"; item: TItem; orgItem: undefined }
|
|
317
|
+
| { type: "update"; item: TItem; orgItem: TItem }
|
|
318
|
+
| { type: "same"; item: TItem; orgItem: TItem };
|
|
319
|
+
|
|
320
|
+
export type TreeArray<TNode> = TNode & { children: TreeArray<TNode>[] };
|
|
321
|
+
|
|
322
|
+
/** Type that can be sorted/compared */
|
|
323
|
+
export type ComparableType = string | number | boolean | DateTime | DateOnly | Time | undefined;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Map Extensions
|
|
329
|
+
|
|
330
|
+
#### `getOrCreate`
|
|
331
|
+
|
|
332
|
+
If no value exists for key, set new value and return it. If the second argument is a function, it is called as a factory.
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
getOrCreate(key: K, newValue: V): V;
|
|
336
|
+
getOrCreate(key: K, newValueFn: () => V): V;
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**Caution:** If `V` is a function type, passing the function directly will be recognized as a factory and called. Wrap it in a factory to store the function itself.
|
|
340
|
+
|
|
341
|
+
#### `update`
|
|
342
|
+
|
|
343
|
+
Update value for key using function. Called even if key does not exist.
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
update(key: K, updateFn: (v: V | undefined) => V): void;
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Example:**
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
const countMap = new Map<string, number>();
|
|
353
|
+
countMap.update("key", (v) => (v ?? 0) + 1);
|
|
354
|
+
|
|
355
|
+
map.getOrCreate("users", []).push(newUser);
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Set Extensions
|
|
361
|
+
|
|
362
|
+
#### `adds`
|
|
363
|
+
|
|
364
|
+
Add multiple values at once.
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
adds(...values: T[]): this;
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
#### `toggle`
|
|
371
|
+
|
|
372
|
+
Toggle value (remove if exists, add if not). Optional `addOrDel` parameter to force add or remove.
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
toggle(value: T, addOrDel?: "add" | "del"): this;
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Example:**
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
const set = new Set<number>([1, 2, 3]);
|
|
382
|
+
set.toggle(2); // 2 exists, so remove -> {1, 3}
|
|
383
|
+
set.toggle(4); // 4 doesn't exist, so add -> {1, 3, 4}
|
|
384
|
+
|
|
385
|
+
const isAdmin = true;
|
|
386
|
+
set.toggle(5, isAdmin ? "add" : "del"); // Force add
|
|
387
|
+
```
|