@simplysm/core-common 14.0.1 → 14.0.4
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 +154 -0
- package/dist/env.d.ts +3 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +1 -1
- package/dist/env.js.map +1 -1
- package/docs/array-extensions.md +430 -0
- package/docs/env.md +52 -0
- package/docs/errors.md +104 -0
- package/docs/features.md +128 -0
- package/docs/type-utilities.md +91 -0
- package/docs/types.md +307 -0
- package/docs/utils.md +641 -0
- package/package.json +6 -2
- package/src/env.ts +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# @simplysm/core-common
|
|
2
|
+
|
|
3
|
+
Platform-neutral utility library for the Simplysm framework. Provides type-safe primitives, collection extensions, error hierarchy, async utilities, and serialization helpers that work in both browser and Node.js environments.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @simplysm/core-common
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API Overview
|
|
12
|
+
|
|
13
|
+
### Environment
|
|
14
|
+
|
|
15
|
+
| API | Type | Description |
|
|
16
|
+
|-----|------|-------------|
|
|
17
|
+
| `env` | `object` | Unified environment variables from `import.meta.env` and `process.env` |
|
|
18
|
+
| `parseBoolEnv` | `function` | Parse a value to boolean (`"true"`, `"1"`, `"yes"`, `"on"` -> `true`) |
|
|
19
|
+
|
|
20
|
+
-> See [docs/env.md](./docs/env.md) for details.
|
|
21
|
+
|
|
22
|
+
### Array / Set / Map Extensions
|
|
23
|
+
|
|
24
|
+
| API | Type | Description |
|
|
25
|
+
|-----|------|-------------|
|
|
26
|
+
| `Array.prototype.*` | extension | 30+ methods: `single`, `first`, `last`, `groupBy`, `toMap`, `distinct`, `orderBy`, `diffs`, `sum`, `min`, `max`, etc. |
|
|
27
|
+
| `Set.prototype.adds` | extension | Add multiple values at once |
|
|
28
|
+
| `Set.prototype.toggle` | extension | Toggle a value (add if absent, remove if present) |
|
|
29
|
+
| `Map.prototype.getOrCreate` | extension | Get value or create with default/factory |
|
|
30
|
+
| `Map.prototype.update` | extension | Update a value using a function |
|
|
31
|
+
| `ArrayDiffsResult` | type | Result type for `diffs()` |
|
|
32
|
+
| `ArrayOneWayDiffResult` | type | Result type for `oneWayDiffs()` |
|
|
33
|
+
| `TreeArray` | type | Tree node type for `toTree()` |
|
|
34
|
+
| `ComparableType` | type | Union of types usable in ordering |
|
|
35
|
+
|
|
36
|
+
-> See [docs/array-extensions.md](./docs/array-extensions.md) for details.
|
|
37
|
+
|
|
38
|
+
### Errors
|
|
39
|
+
|
|
40
|
+
| API | Type | Description |
|
|
41
|
+
|-----|------|-------------|
|
|
42
|
+
| `SdError` | class | Tree-structured error with cause chaining |
|
|
43
|
+
| `ArgumentError` | class | Invalid argument error with YAML-formatted details |
|
|
44
|
+
| `NotImplementedError` | class | Unimplemented feature error |
|
|
45
|
+
| `TimeoutError` | class | Timeout exceeded error |
|
|
46
|
+
|
|
47
|
+
-> See [docs/errors.md](./docs/errors.md) for details.
|
|
48
|
+
|
|
49
|
+
### Types
|
|
50
|
+
|
|
51
|
+
| API | Type | Description |
|
|
52
|
+
|-----|------|-------------|
|
|
53
|
+
| `Uuid` | class | UUID v4 generation and parsing |
|
|
54
|
+
| `LazyGcMap` | class | Auto-expiring Map with LRU-style GC |
|
|
55
|
+
| `DateTime` | class | Immutable date-time wrapper with millisecond precision |
|
|
56
|
+
| `DateOnly` | class | Immutable date-only wrapper (no time component) |
|
|
57
|
+
| `Time` | class | Immutable time-only wrapper (no date component, 24h cyclic) |
|
|
58
|
+
|
|
59
|
+
-> See [docs/types.md](./docs/types.md) for details.
|
|
60
|
+
|
|
61
|
+
### Features
|
|
62
|
+
|
|
63
|
+
| API | Type | Description |
|
|
64
|
+
|-----|------|-------------|
|
|
65
|
+
| `DebounceQueue` | class | Async debounce queue -- only executes the last enqueued function |
|
|
66
|
+
| `SerialQueue` | class | Async serial queue -- executes functions one at a time in order |
|
|
67
|
+
| `EventEmitter` | class | Type-safe event emitter built on EventTarget |
|
|
68
|
+
|
|
69
|
+
-> See [docs/features.md](./docs/features.md) for details.
|
|
70
|
+
|
|
71
|
+
### Utils (Namespace Exports)
|
|
72
|
+
|
|
73
|
+
| API | Type | Description |
|
|
74
|
+
|-----|------|-------------|
|
|
75
|
+
| `obj` | namespace | Deep clone, deep equal, deep merge, 3-way merge, omit, pick, chain access, type-safe keys/entries |
|
|
76
|
+
| `str` | namespace | Korean suffix, full-width conversion, case transforms, `isNullOrEmpty`, `insert` |
|
|
77
|
+
| `num` | namespace | parseInt, parseFloat, parseRoundedInt, isNullOrEmpty, format |
|
|
78
|
+
| `bytes` | namespace | Uint8Array concat, hex, base64 conversions |
|
|
79
|
+
| `path` | namespace | POSIX-style path join, basename, extname (browser-safe) |
|
|
80
|
+
| `json` | namespace | JSON stringify/parse with custom type support (DateTime, Uuid, Map, Set, etc.) |
|
|
81
|
+
| `xml` | namespace | XML parse/stringify via fast-xml-parser |
|
|
82
|
+
| `wait` | namespace | `until` (poll condition) and `time` (delay) |
|
|
83
|
+
| `transfer` | namespace | Worker-transferable encode/decode for custom types |
|
|
84
|
+
| `err` | namespace | Extract error message from unknown |
|
|
85
|
+
| `dt` | namespace | Date/time format strings (C#-style patterns) |
|
|
86
|
+
| `primitive` | namespace | Runtime primitive type string detection |
|
|
87
|
+
| `js`, `ts`, `html`, `tsql`, `mysql`, `pgsql` | template tag | IDE syntax highlighting template tags with indent normalization |
|
|
88
|
+
| `ZipArchive` | class | ZIP read/write/compress/extract |
|
|
89
|
+
|
|
90
|
+
-> See [docs/utils.md](./docs/utils.md) for details.
|
|
91
|
+
|
|
92
|
+
### Type Utilities
|
|
93
|
+
|
|
94
|
+
| API | Type | Description |
|
|
95
|
+
|-----|------|-------------|
|
|
96
|
+
| `Bytes` | type alias | `Uint8Array` (replaces `Buffer`) |
|
|
97
|
+
| `PrimitiveTypeMap` | type | Maps type-string keys to their TypeScript types |
|
|
98
|
+
| `PrimitiveTypeStr` | type | `"string" \| "number" \| "boolean" \| "DateTime" \| "DateOnly" \| "Time" \| "Uuid" \| "Bytes"` |
|
|
99
|
+
| `PrimitiveType` | type | Union of all primitive type values |
|
|
100
|
+
| `DeepPartial<T>` | type | Recursively makes all properties optional |
|
|
101
|
+
| `Type<T>` | interface | Constructor type (`new (...args) => T`) |
|
|
102
|
+
|
|
103
|
+
-> See [docs/type-utilities.md](./docs/type-utilities.md) for details.
|
|
104
|
+
|
|
105
|
+
## Usage Examples
|
|
106
|
+
|
|
107
|
+
### Deep cloning and comparing objects
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { obj } from "@simplysm/core-common";
|
|
111
|
+
|
|
112
|
+
const original = { a: 1, b: { c: [2, 3] } };
|
|
113
|
+
const cloned = obj.clone(original);
|
|
114
|
+
obj.equal(original, cloned); // true
|
|
115
|
+
|
|
116
|
+
const merged = obj.merge(original, { b: { d: 4 } });
|
|
117
|
+
// { a: 1, b: { c: [2, 3], d: 4 } }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Using Array extensions
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import "@simplysm/core-common";
|
|
124
|
+
|
|
125
|
+
const users = [
|
|
126
|
+
{ id: 1, role: "admin", name: "Alice" },
|
|
127
|
+
{ id: 2, role: "user", name: "Bob" },
|
|
128
|
+
{ id: 3, role: "user", name: "Carol" },
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
const grouped = users.groupBy((u) => u.role);
|
|
132
|
+
// [{ key: "admin", values: [Alice] }, { key: "user", values: [Bob, Carol] }]
|
|
133
|
+
|
|
134
|
+
const userMap = users.toMap((u) => u.id);
|
|
135
|
+
// Map { 1 => Alice, 2 => Bob, 3 => Carol }
|
|
136
|
+
|
|
137
|
+
const sorted = users.orderBy((u) => u.name);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### JSON serialization with custom types
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { json, DateTime, Uuid } from "@simplysm/core-common";
|
|
144
|
+
|
|
145
|
+
const data = {
|
|
146
|
+
id: Uuid.generate(),
|
|
147
|
+
createdAt: new DateTime(2025, 6, 15, 10, 30, 0),
|
|
148
|
+
tags: new Set(["a", "b"]),
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const serialized = json.stringify(data);
|
|
152
|
+
const restored = json.parse(serialized);
|
|
153
|
+
// restored.id is Uuid, restored.createdAt is DateTime, restored.tags is Set
|
|
154
|
+
```
|
package/dist/env.d.ts
CHANGED
package/dist/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAEA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAEA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,aAAc,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;KAAG;IAC1D,UAAU,UAAU;QAClB,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;KAC7B;CACF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEpD;AAMD,eAAO,MAAM,GAAG,EAAE;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAKxB,CAAC"}
|
package/dist/env.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
export function parseBoolEnv(value) {
|
|
6
6
|
return ["true", "1", "yes", "on"].includes(String(value ?? "").toLowerCase());
|
|
7
7
|
}
|
|
8
|
-
const _metaEnv = import.meta.env
|
|
8
|
+
const _metaEnv = { ...import.meta.env };
|
|
9
9
|
const _processEnv = typeof process !== "undefined" ? process.env : {};
|
|
10
10
|
const _raw = { ..._metaEnv, ..._processEnv };
|
|
11
11
|
export const env = {
|
package/dist/env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,QAAQ,GAA4B,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;AACjE,MAAM,WAAW,GAA4B,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/F,MAAM,IAAI,GAA4B,EAAE,GAAG,QAAQ,EAAE,GAAG,WAAW,EAAE,CAAC;AAEtE,MAAM,CAAC,MAAM,GAAG,GAIZ;IACF,GAAG,IAAI;IACP,GAAG,EAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,GAAG,EAAE,IAAI,CAAC,KAAK,CAAuB;CACvC,CAAC"}
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
# Array / Set / Map Extensions
|
|
2
|
+
|
|
3
|
+
Prototype extensions added to `Array`, `Set`, and `Map` at import time. Importing `@simplysm/core-common` automatically installs these extensions.
|
|
4
|
+
|
|
5
|
+
## Array -- Readonly Extensions
|
|
6
|
+
|
|
7
|
+
These methods return new arrays/values without mutating the original.
|
|
8
|
+
|
|
9
|
+
### `single(predicate?)`
|
|
10
|
+
|
|
11
|
+
Returns the single matching element, or `undefined` if none. Throws `ArgumentError` if more than one element matches.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
single(predicate?: (item: T, index: number) => boolean): T | undefined;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### `first(predicate?)`
|
|
18
|
+
|
|
19
|
+
Returns the first matching element, or `undefined` if none.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
first(predicate?: (item: T, index: number) => boolean): T | undefined;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### `last(predicate?)`
|
|
26
|
+
|
|
27
|
+
Returns the last matching element, or `undefined` if none.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
last(predicate?: (item: T, index: number) => boolean): T | undefined;
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### `filterAsync(predicate)`
|
|
34
|
+
|
|
35
|
+
Async sequential filter.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
filterAsync(predicate: (item: T, index: number) => Promise<boolean>): Promise<T[]>;
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `filterExists()`
|
|
42
|
+
|
|
43
|
+
Removes `null` and `undefined` elements. Returns `NonNullable<T>[]`.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
filterExists(): NonNullable<T>[];
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `ofType(type)`
|
|
50
|
+
|
|
51
|
+
Filters elements by type. Accepts a `PrimitiveTypeStr` (`"string"`, `"number"`, `"boolean"`, `"DateTime"`, `"DateOnly"`, `"Time"`, `"Uuid"`, `"Bytes"`) or a constructor function.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
ofType<K extends PrimitiveTypeStr>(type: K): Extract<T, PrimitiveTypeMap[K]>[];
|
|
55
|
+
ofType<N extends T>(type: Type<N>): N[];
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### `mapAsync(selector)`
|
|
59
|
+
|
|
60
|
+
Async sequential map.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
mapAsync<R>(selector: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `mapMany(selector?)`
|
|
67
|
+
|
|
68
|
+
Maps then flattens one level. Removes `null`/`undefined` from results.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
mapMany(): T extends readonly (infer U)[] ? U[] : T;
|
|
72
|
+
mapMany<R>(selector: (item: T, index: number) => R[]): R[];
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### `mapManyAsync(selector?)`
|
|
76
|
+
|
|
77
|
+
Async version of `mapMany`.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
mapManyAsync<R>(selector: (item: T, index: number) => Promise<R[]>): Promise<R[]>;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `parallelAsync(fn)`
|
|
84
|
+
|
|
85
|
+
Runs `fn` for all items in parallel via `Promise.all`. If any rejects, the entire result rejects.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
parallelAsync<R>(fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### `groupBy(keySelector, valueSelector?)`
|
|
92
|
+
|
|
93
|
+
Groups items by key. Supports both primitive keys (O(n) via Map) and object keys (O(n^2) via deep equality).
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
groupBy<K>(keySelector: (item: T, index: number) => K): { key: K; values: T[] }[];
|
|
97
|
+
groupBy<K, V>(
|
|
98
|
+
keySelector: (item: T, index: number) => K,
|
|
99
|
+
valueSelector: (item: T, index: number) => V,
|
|
100
|
+
): { key: K; values: V[] }[];
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `toMap(keySelector, valueSelector?)`
|
|
104
|
+
|
|
105
|
+
Converts array to `Map`. Throws `ArgumentError` on duplicate keys.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
toMap<K>(keySelector: (item: T, index: number) => K): Map<K, T>;
|
|
109
|
+
toMap<K, V>(
|
|
110
|
+
keySelector: (item: T, index: number) => K,
|
|
111
|
+
valueSelector: (item: T, index: number) => V,
|
|
112
|
+
): Map<K, V>;
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `toMapAsync(keySelector, valueSelector?)`
|
|
116
|
+
|
|
117
|
+
Async version of `toMap`.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
toMapAsync<K>(keySelector: (item: T, index: number) => Promise<K>): Promise<Map<K, T>>;
|
|
121
|
+
toMapAsync<K, V>(
|
|
122
|
+
keySelector: (item: T, index: number) => Promise<K> | K,
|
|
123
|
+
valueSelector: (item: T, index: number) => Promise<V> | V,
|
|
124
|
+
): Promise<Map<K, V>>;
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `toArrayMap(keySelector, valueSelector?)`
|
|
128
|
+
|
|
129
|
+
Converts array to `Map<K, V[]>` (groups values into arrays per key).
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
toArrayMap<K>(keySelector: (item: T, index: number) => K): Map<K, T[]>;
|
|
133
|
+
toArrayMap<K, V>(
|
|
134
|
+
keySelector: (item: T, index: number) => K,
|
|
135
|
+
valueSelector: (item: T, index: number) => V,
|
|
136
|
+
): Map<K, V[]>;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `toSetMap(keySelector, valueSelector?)`
|
|
140
|
+
|
|
141
|
+
Converts array to `Map<K, Set<V>>`.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
toSetMap<K>(keySelector: (item: T, index: number) => K): Map<K, Set<T>>;
|
|
145
|
+
toSetMap<K, V>(
|
|
146
|
+
keySelector: (item: T, index: number) => K,
|
|
147
|
+
valueSelector: (item: T, index: number) => V,
|
|
148
|
+
): Map<K, Set<V>>;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### `toMapValues(keySelector, valueSelector)`
|
|
152
|
+
|
|
153
|
+
Groups by key, then applies `valueSelector` to the group array to produce the final value.
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
toMapValues<K, V>(
|
|
157
|
+
keySelector: (item: T, index: number) => K,
|
|
158
|
+
valueSelector: (items: T[]) => V,
|
|
159
|
+
): Map<K, V>;
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### `toObject(keySelector, valueSelector?)`
|
|
163
|
+
|
|
164
|
+
Converts array to a plain object. Throws `ArgumentError` on duplicate keys.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
toObject(keySelector: (item: T, index: number) => string): Record<string, T>;
|
|
168
|
+
toObject<V>(
|
|
169
|
+
keySelector: (item: T, index: number) => string,
|
|
170
|
+
valueSelector: (item: T, index: number) => V,
|
|
171
|
+
): Record<string, V>;
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### `toTree(keyProp, parentKey)`
|
|
175
|
+
|
|
176
|
+
Converts a flat array to a tree structure. Items where `parentKey` is `null`/`undefined` become roots. O(n) complexity.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
toTree<K extends keyof T, P extends keyof T>(keyProp: K, parentKey: P): TreeArray<T>[];
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const items = [
|
|
184
|
+
{ id: 1, parentId: undefined, name: "root" },
|
|
185
|
+
{ id: 2, parentId: 1, name: "child" },
|
|
186
|
+
];
|
|
187
|
+
const tree = items.toTree("id", "parentId");
|
|
188
|
+
// [{ id: 1, name: "root", children: [{ id: 2, name: "child", children: [] }] }]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### `distinct(options?)`
|
|
192
|
+
|
|
193
|
+
Returns a new array with duplicates removed.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
distinct(options?: boolean | {
|
|
197
|
+
matchAddress?: boolean;
|
|
198
|
+
keyFn?: (item: T) => string | number;
|
|
199
|
+
}): T[];
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
| Option | Description |
|
|
203
|
+
|--------|-------------|
|
|
204
|
+
| `true` or `{ matchAddress: true }` | Use reference equality (Set-based, O(n)) |
|
|
205
|
+
| `{ keyFn }` | Use custom key function (O(n)) |
|
|
206
|
+
| No options | Deep equality comparison (O(n^2) for objects) |
|
|
207
|
+
|
|
208
|
+
### `orderBy(selector?)` / `orderByDesc(selector?)`
|
|
209
|
+
|
|
210
|
+
Returns a sorted copy. Supports `string`, `number`, `DateTime`, `DateOnly`, `Time`.
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
orderBy(selector?: (item: T) => string | number | DateTime | DateOnly | Time | undefined): T[];
|
|
214
|
+
orderByDesc(selector?: (item: T) => string | number | DateTime | DateOnly | Time | undefined): T[];
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### `diffs(target, options?)`
|
|
218
|
+
|
|
219
|
+
Compares two arrays and returns INSERT/DELETE/UPDATE diff results.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
diffs<P>(target: P[], options?: {
|
|
223
|
+
keys?: string[];
|
|
224
|
+
excludes?: string[];
|
|
225
|
+
}): ArrayDiffsResult<T, P>[];
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Result items:
|
|
229
|
+
- `{ source: T, target: undefined }` -- item exists only in source (DELETE)
|
|
230
|
+
- `{ source: undefined, target: P }` -- item exists only in target (INSERT)
|
|
231
|
+
- `{ source: T, target: P }` -- item exists in both but differs (UPDATE)
|
|
232
|
+
|
|
233
|
+
### `oneWayDiffs(orgItems, keyPropNameOrGetValFn, options?)`
|
|
234
|
+
|
|
235
|
+
One-way diff: compares current array against original items.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
oneWayDiffs<K extends keyof T>(
|
|
239
|
+
orgItems: T[] | Map<T[K], T>,
|
|
240
|
+
keyPropNameOrGetValFn: K | ((item: T) => string | number | undefined),
|
|
241
|
+
options?: { includeSame?: boolean; excludes?: string[]; includes?: string[] },
|
|
242
|
+
): ArrayOneWayDiffResult<T>[];
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Result types: `"create"`, `"update"`, `"same"` (only if `includeSame: true`).
|
|
246
|
+
|
|
247
|
+
### `merge(target, options?)`
|
|
248
|
+
|
|
249
|
+
Merges two arrays using deep merge on matched items and appends unmatched target items.
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
merge<P>(target: P[], options?: {
|
|
253
|
+
keys?: string[];
|
|
254
|
+
excludes?: string[];
|
|
255
|
+
}): (T | P | (T & P))[];
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### `sum(selector?)`
|
|
259
|
+
|
|
260
|
+
Returns the sum of elements. Returns `0` for empty arrays.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
sum(selector?: (item: T, index: number) => number): number;
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### `min(selector?)` / `max(selector?)`
|
|
267
|
+
|
|
268
|
+
Returns the minimum/maximum element. Returns `undefined` for empty arrays.
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
min(selector?: (item: T, index: number) => string | number): string | number | undefined;
|
|
272
|
+
max(selector?: (item: T, index: number) => string | number): string | number | undefined;
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### `shuffle()`
|
|
276
|
+
|
|
277
|
+
Returns a new array with elements in random order (Fisher-Yates algorithm).
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
shuffle(): T[];
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Array -- Mutable Extensions
|
|
284
|
+
|
|
285
|
+
These methods mutate the original array in place.
|
|
286
|
+
|
|
287
|
+
### `distinctThis(options?)`
|
|
288
|
+
|
|
289
|
+
Removes duplicates from the original array in place. Same options as `distinct()`.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
distinctThis(options?: boolean | {
|
|
293
|
+
matchAddress?: boolean;
|
|
294
|
+
keyFn?: (item: T) => string | number;
|
|
295
|
+
}): T[];
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### `orderByThis(selector?)` / `orderByDescThis(selector?)`
|
|
299
|
+
|
|
300
|
+
Sorts the original array in place.
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
orderByThis(selector?: (item: T) => string | number | DateTime | DateOnly | Time | undefined): T[];
|
|
304
|
+
orderByDescThis(selector?: (item: T) => string | number | DateTime | DateOnly | Time | undefined): T[];
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### `insert(index, ...items)`
|
|
308
|
+
|
|
309
|
+
Inserts items at the given index in place.
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
insert(index: number, ...items: T[]): this;
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### `remove(itemOrSelector)`
|
|
316
|
+
|
|
317
|
+
Removes matching items in place.
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
remove(item: T): this;
|
|
321
|
+
remove(selector: (item: T, index: number) => boolean): this;
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### `toggle(item)`
|
|
325
|
+
|
|
326
|
+
Toggles an item: removes if present, adds if absent.
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
toggle(item: T): this;
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### `clear()`
|
|
333
|
+
|
|
334
|
+
Removes all items from the array.
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
clear(): this;
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Set Extensions
|
|
341
|
+
|
|
342
|
+
### `adds(...values)`
|
|
343
|
+
|
|
344
|
+
Adds multiple values at once. Returns `this` for chaining.
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
adds(...values: T[]): this;
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### `toggle(value, addOrDel?)`
|
|
351
|
+
|
|
352
|
+
Toggles a value in the Set. Optionally force add or delete.
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
toggle(value: T, addOrDel?: "add" | "del"): this;
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
| Parameter | Type | Description |
|
|
359
|
+
|-----------|------|-------------|
|
|
360
|
+
| `value` | `T` | Value to toggle |
|
|
361
|
+
| `addOrDel` | `"add" \| "del" \| undefined` | Force add or delete. Omit for auto-toggle |
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
const set = new Set([1, 2, 3]);
|
|
365
|
+
set.toggle(2); // removes 2 -> {1, 3}
|
|
366
|
+
set.toggle(4); // adds 4 -> {1, 3, 4}
|
|
367
|
+
set.toggle(5, "add"); // force add -> {1, 3, 4, 5}
|
|
368
|
+
set.toggle(5, "del"); // force del -> {1, 3, 4}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Map Extensions
|
|
372
|
+
|
|
373
|
+
### `getOrCreate(key, newValue)`
|
|
374
|
+
|
|
375
|
+
Returns the value for `key`. If the key does not exist, creates it with `newValue` (or calls `newValue` as a factory function if it is a function).
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
getOrCreate(key: K, newValue: V): V;
|
|
379
|
+
getOrCreate(key: K, newValueFn: () => V): V;
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Note:** If `V` is a function type, wrap it in a factory to avoid it being called:
|
|
383
|
+
```typescript
|
|
384
|
+
fnMap.getOrCreate("key", () => myFunction);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### `update(key, updateFn)`
|
|
388
|
+
|
|
389
|
+
Updates a key's value using a function. If the key does not exist, `updateFn` receives `undefined`.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
update(key: K, updateFn: (v: V | undefined) => V): void;
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
const counts = new Map<string, number>();
|
|
397
|
+
counts.update("hits", (v) => (v ?? 0) + 1);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Exported Types
|
|
401
|
+
|
|
402
|
+
### `ArrayDiffsResult<TOriginal, TOther>`
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
type ArrayDiffsResult<TOriginal, TOther> =
|
|
406
|
+
| { source: undefined; target: TOther } // INSERT
|
|
407
|
+
| { source: TOriginal; target: undefined } // DELETE
|
|
408
|
+
| { source: TOriginal; target: TOther }; // UPDATE
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### `ArrayOneWayDiffResult<TItem>`
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
type ArrayOneWayDiffResult<TItem> =
|
|
415
|
+
| { type: "create"; item: TItem; orgItem: undefined }
|
|
416
|
+
| { type: "update"; item: TItem; orgItem: TItem }
|
|
417
|
+
| { type: "same"; item: TItem; orgItem: TItem };
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### `TreeArray<TNode>`
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
type TreeArray<TNode> = TNode & { children: TreeArray<TNode>[] };
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### `ComparableType`
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
type ComparableType = string | number | boolean | DateTime | DateOnly | Time | undefined;
|
|
430
|
+
```
|
package/docs/env.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Environment
|
|
2
|
+
|
|
3
|
+
Unified access to environment variables from both `import.meta.env` (Vite/browser) and `process.env` (Node.js). `process.env` takes precedence when both are available.
|
|
4
|
+
|
|
5
|
+
## `parseBoolEnv`
|
|
6
|
+
|
|
7
|
+
Parses an unknown value to a boolean. Returns `true` for `"true"`, `"1"`, `"yes"`, `"on"` (case-insensitive), `false` for everything else.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
function parseBoolEnv(value: unknown): boolean;
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Description |
|
|
14
|
+
|-----------|------|-------------|
|
|
15
|
+
| `value` | `unknown` | The value to parse |
|
|
16
|
+
|
|
17
|
+
**Returns:** `boolean`
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
parseBoolEnv("true"); // true
|
|
21
|
+
parseBoolEnv("1"); // true
|
|
22
|
+
parseBoolEnv("yes"); // true
|
|
23
|
+
parseBoolEnv("on"); // true
|
|
24
|
+
parseBoolEnv("false"); // false
|
|
25
|
+
parseBoolEnv(undefined); // false
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## `env`
|
|
29
|
+
|
|
30
|
+
A pre-built environment object that merges `import.meta.env` and `process.env`.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const env: {
|
|
34
|
+
DEV: boolean;
|
|
35
|
+
VER?: string;
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
};
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Property | Type | Description |
|
|
41
|
+
|----------|------|-------------|
|
|
42
|
+
| `DEV` | `boolean` | Whether in development mode (parsed via `parseBoolEnv` from `DEV` env var) |
|
|
43
|
+
| `VER` | `string \| undefined` | Version string from `VER` env var |
|
|
44
|
+
| `[key]` | `unknown` | Any other environment variable |
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { env } from "@simplysm/core-common";
|
|
48
|
+
|
|
49
|
+
if (env.DEV) {
|
|
50
|
+
// development-only logic
|
|
51
|
+
}
|
|
52
|
+
```
|