@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 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
@@ -1,6 +1,8 @@
1
1
  declare global {
2
+ interface ImportMetaEnv extends Record<string, unknown> {
3
+ }
2
4
  interface ImportMeta {
3
- env?: Record<string, unknown>;
5
+ readonly env: ImportMetaEnv;
4
6
  }
5
7
  }
6
8
  /**
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,UAAU;QAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B;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"}
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":"AAQA;;;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,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;AAChE,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"}
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
+ ```