@simplysm/core-common 13.0.100 → 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 +86 -92
- package/dist/common.types.d.ts +14 -14
- package/dist/common.types.js +2 -1
- package/dist/common.types.js.map +1 -6
- package/dist/env.d.ts +8 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +13 -9
- package/dist/env.js.map +1 -6
- package/dist/errors/argument-error.d.ts +10 -10
- package/dist/errors/argument-error.d.ts.map +1 -1
- package/dist/errors/argument-error.js +31 -14
- package/dist/errors/argument-error.js.map +1 -6
- package/dist/errors/not-implemented-error.d.ts +8 -8
- package/dist/errors/not-implemented-error.js +30 -12
- package/dist/errors/not-implemented-error.js.map +1 -6
- package/dist/errors/sd-error.d.ts +10 -10
- package/dist/errors/sd-error.d.ts.map +1 -1
- package/dist/errors/sd-error.js +45 -24
- package/dist/errors/sd-error.js.map +1 -6
- package/dist/errors/timeout-error.d.ts +10 -10
- package/dist/errors/timeout-error.js +34 -15
- package/dist/errors/timeout-error.js.map +1 -6
- package/dist/extensions/arr-ext.d.ts +2 -2
- package/dist/extensions/arr-ext.helpers.d.ts +10 -10
- package/dist/extensions/arr-ext.helpers.js +112 -89
- package/dist/extensions/arr-ext.helpers.js.map +1 -6
- package/dist/extensions/arr-ext.js +458 -422
- package/dist/extensions/arr-ext.js.map +1 -6
- package/dist/extensions/arr-ext.types.d.ts +57 -57
- package/dist/extensions/arr-ext.types.d.ts.map +1 -1
- package/dist/extensions/arr-ext.types.js +6 -1
- package/dist/extensions/arr-ext.types.js.map +1 -6
- package/dist/extensions/map-ext.d.ts +16 -16
- package/dist/extensions/map-ext.js +27 -22
- package/dist/extensions/map-ext.js.map +1 -6
- package/dist/extensions/set-ext.d.ts +11 -11
- package/dist/extensions/set-ext.js +32 -25
- package/dist/extensions/set-ext.js.map +1 -6
- package/dist/features/debounce-queue.d.ts +17 -17
- package/dist/features/debounce-queue.js +98 -70
- package/dist/features/debounce-queue.js.map +1 -6
- package/dist/features/event-emitter.d.ts +20 -20
- package/dist/features/event-emitter.js +101 -78
- package/dist/features/event-emitter.js.map +1 -6
- package/dist/features/serial-queue.d.ts +11 -11
- package/dist/features/serial-queue.js +78 -57
- package/dist/features/serial-queue.js.map +1 -6
- package/dist/globals.d.ts +4 -4
- package/dist/globals.js +9 -1
- package/dist/globals.js.map +1 -6
- package/dist/index.js +28 -27
- package/dist/index.js.map +1 -6
- package/dist/types/date-only.d.ts +64 -64
- package/dist/types/date-only.d.ts.map +1 -1
- package/dist/types/date-only.js +263 -252
- package/dist/types/date-only.js.map +1 -6
- package/dist/types/date-time.d.ts +36 -36
- package/dist/types/date-time.d.ts.map +1 -1
- package/dist/types/date-time.js +196 -288
- package/dist/types/date-time.js.map +1 -6
- package/dist/types/lazy-gc-map.d.ts +26 -26
- package/dist/types/lazy-gc-map.d.ts.map +1 -1
- package/dist/types/lazy-gc-map.js +202 -159
- package/dist/types/lazy-gc-map.js.map +1 -6
- package/dist/types/time.d.ts +23 -23
- package/dist/types/time.d.ts.map +1 -1
- package/dist/types/time.js +169 -158
- package/dist/types/time.js.map +1 -6
- package/dist/types/uuid.d.ts +11 -11
- package/dist/types/uuid.d.ts.map +1 -1
- package/dist/types/uuid.js +95 -70
- package/dist/types/uuid.js.map +1 -6
- package/dist/utils/bytes.d.ts +17 -17
- package/dist/utils/bytes.js +137 -81
- package/dist/utils/bytes.js.map +1 -6
- package/dist/utils/date-format.d.ts +40 -40
- package/dist/utils/date-format.js +187 -101
- package/dist/utils/date-format.js.map +1 -6
- package/dist/utils/error.d.ts +4 -4
- package/dist/utils/error.js +11 -6
- package/dist/utils/error.js.map +1 -6
- package/dist/utils/json.d.ts +19 -19
- package/dist/utils/json.js +187 -135
- package/dist/utils/json.js.map +1 -6
- package/dist/utils/num.d.ts +20 -20
- package/dist/utils/num.js +76 -34
- package/dist/utils/num.js.map +1 -6
- package/dist/utils/obj.d.ts +111 -111
- package/dist/utils/obj.d.ts.map +1 -1
- package/dist/utils/obj.js +706 -496
- package/dist/utils/obj.js.map +1 -6
- package/dist/utils/path.d.ts +10 -10
- package/dist/utils/path.js +35 -18
- package/dist/utils/path.js.map +1 -6
- package/dist/utils/primitive.d.ts +5 -5
- package/dist/utils/primitive.js +34 -14
- package/dist/utils/primitive.js.map +1 -6
- package/dist/utils/str.d.ts +38 -38
- package/dist/utils/str.js +217 -113
- package/dist/utils/str.js.map +1 -6
- package/dist/utils/template-strings.d.ts +26 -26
- package/dist/utils/template-strings.js +113 -40
- package/dist/utils/template-strings.js.map +1 -6
- package/dist/utils/transferable.d.ts +18 -18
- package/dist/utils/transferable.js +218 -151
- package/dist/utils/transferable.js.map +1 -6
- package/dist/utils/wait.d.ts +9 -9
- package/dist/utils/wait.js +30 -15
- package/dist/utils/wait.js.map +1 -6
- package/dist/utils/xml.d.ts +13 -13
- package/dist/utils/xml.js +84 -46
- package/dist/utils/xml.js.map +1 -6
- package/dist/utils/zip.d.ts +22 -22
- package/dist/utils/zip.js +172 -148
- package/dist/utils/zip.js.map +1 -6
- package/docs/array-extensions.md +430 -0
- package/docs/env.md +52 -0
- package/docs/errors.md +41 -56
- package/docs/features.md +82 -97
- package/docs/type-utilities.md +91 -0
- package/docs/types.md +221 -201
- package/docs/utils.md +319 -435
- package/package.json +7 -5
- package/src/common.types.ts +14 -14
- package/src/env.ts +12 -3
- package/src/errors/argument-error.ts +15 -15
- package/src/errors/not-implemented-error.ts +9 -9
- package/src/errors/sd-error.ts +12 -12
- package/src/errors/timeout-error.ts +12 -12
- package/src/extensions/arr-ext.helpers.ts +16 -16
- package/src/extensions/arr-ext.ts +35 -35
- package/src/extensions/arr-ext.types.ts +57 -57
- package/src/extensions/map-ext.ts +16 -16
- package/src/extensions/set-ext.ts +11 -11
- package/src/features/debounce-queue.ts +23 -23
- package/src/features/event-emitter.ts +25 -25
- package/src/features/serial-queue.ts +13 -13
- package/src/globals.ts +4 -4
- package/src/index.ts +5 -5
- package/src/types/date-only.ts +84 -83
- package/src/types/date-time.ts +43 -42
- package/src/types/lazy-gc-map.ts +44 -44
- package/src/types/time.ts +29 -29
- package/src/types/uuid.ts +15 -15
- package/src/utils/bytes.ts +35 -35
- package/src/utils/date-format.ts +59 -59
- package/src/utils/error.ts +4 -4
- package/src/utils/json.ts +41 -41
- package/src/utils/num.ts +20 -20
- package/src/utils/obj.ts +138 -138
- package/src/utils/path.ts +10 -10
- package/src/utils/primitive.ts +6 -6
- package/src/utils/str.ts +48 -48
- package/src/utils/template-strings.ts +29 -29
- package/src/utils/transferable.ts +38 -38
- package/src/utils/wait.ts +10 -10
- package/src/utils/xml.ts +19 -19
- package/src/utils/zip.ts +25 -25
- package/docs/extensions.md +0 -387
- package/tests/errors/errors.spec.ts +0 -80
- package/tests/extensions/array-extension.spec.ts +0 -654
- package/tests/extensions/map-extension.spec.ts +0 -117
- package/tests/extensions/set-extension.spec.ts +0 -67
- package/tests/types/date-only.spec.ts +0 -533
- package/tests/types/date-time.spec.ts +0 -246
- package/tests/types/lazy-gc-map.spec.ts +0 -606
- package/tests/types/time.spec.ts +0 -428
- package/tests/types/uuid.spec.ts +0 -74
- package/tests/utils/bytes-utils.spec.ts +0 -197
- package/tests/utils/date-format.spec.ts +0 -350
- package/tests/utils/debounce-queue.spec.ts +0 -226
- package/tests/utils/json.spec.ts +0 -400
- package/tests/utils/number.spec.ts +0 -136
- package/tests/utils/object.spec.ts +0 -810
- package/tests/utils/path.spec.ts +0 -70
- package/tests/utils/primitive.spec.ts +0 -43
- package/tests/utils/sd-event-emitter.spec.ts +0 -189
- package/tests/utils/serial-queue.spec.ts +0 -305
- package/tests/utils/string.spec.ts +0 -265
- package/tests/utils/template-strings.spec.ts +0 -48
- package/tests/utils/transferable.spec.ts +0 -639
- package/tests/utils/wait.spec.ts +0 -123
- package/tests/utils/xml.spec.ts +0 -146
- package/tests/utils/zip.spec.ts +0 -221
package/src/utils/xml.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* XML
|
|
2
|
+
* XML 변환 유틸리티
|
|
3
3
|
*/
|
|
4
4
|
import type { XmlBuilderOptions } from "fast-xml-parser";
|
|
5
5
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
@@ -7,14 +7,14 @@ import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
|
7
7
|
//#region parse
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* @param str XML
|
|
12
|
-
* @param options
|
|
13
|
-
* @param options.stripTagPrefix
|
|
14
|
-
* @returns
|
|
15
|
-
* -
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
10
|
+
* XML 문자열을 객체로 파싱
|
|
11
|
+
* @param str XML 문자열
|
|
12
|
+
* @param options 옵션
|
|
13
|
+
* @param options.stripTagPrefix 태그 접두사(네임스페이스) 제거 여부
|
|
14
|
+
* @returns 파싱된 객체. 구조:
|
|
15
|
+
* - 속성: `$` 객체에 그룹화
|
|
16
|
+
* - 텍스트 노드: `_` key에 저장
|
|
17
|
+
* - 자식 요소: array로 변환 (루트 요소 제외)
|
|
18
18
|
* @example
|
|
19
19
|
* xmlParse('<root id="1"><item>hello</item></root>');
|
|
20
20
|
* // { root: { $: { id: "1" }, item: [{ _: "hello" }] } }
|
|
@@ -40,10 +40,10 @@ export function parse(str: string, options?: { stripTagPrefix?: boolean }): unkn
|
|
|
40
40
|
//#region stringify
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
44
|
-
* @param obj
|
|
45
|
-
* @param options fast-xml-parser XmlBuilderOptions (
|
|
46
|
-
* @returns XML
|
|
43
|
+
* 객체를 XML 문자열로 직렬화
|
|
44
|
+
* @param obj 직렬화할 객체
|
|
45
|
+
* @param options fast-xml-parser XmlBuilderOptions (선택사항)
|
|
46
|
+
* @returns XML 문자열
|
|
47
47
|
* @example
|
|
48
48
|
* xmlStringify({
|
|
49
49
|
* root: {
|
|
@@ -69,10 +69,10 @@ export function stringify(obj: unknown, options?: XmlBuilderOptions): string {
|
|
|
69
69
|
//#region private
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
73
|
-
* @note
|
|
74
|
-
*
|
|
75
|
-
*
|
|
72
|
+
* 태그 이름에서 네임스페이스 접두사 제거
|
|
73
|
+
* @note XML 파싱 결과에서 "ns:tag" 형식의 네임스페이스 접두사를 제거하여 태그 이름만 남김.
|
|
74
|
+
* 네임스페이스를 고려하지 않고 XML 데이터에 일관되게 접근 가능.
|
|
75
|
+
* 단, 속성은 접두사가 유지됨.
|
|
76
76
|
*/
|
|
77
77
|
function stripTagPrefix(obj: unknown): unknown {
|
|
78
78
|
if (Array.isArray(obj)) {
|
|
@@ -86,11 +86,11 @@ function stripTagPrefix(obj: unknown): unknown {
|
|
|
86
86
|
for (const key of Object.keys(record)) {
|
|
87
87
|
const value = record[key];
|
|
88
88
|
|
|
89
|
-
//
|
|
89
|
+
// 속성은 접두사를 제거하면 안 됨
|
|
90
90
|
if (key === "$") {
|
|
91
91
|
newObj[key] = value;
|
|
92
92
|
} else {
|
|
93
|
-
//
|
|
93
|
+
// 첫 번째 ":"를 기준으로 태그 이름에서만 접두사 제거
|
|
94
94
|
const colonIndex = key.indexOf(":");
|
|
95
95
|
const cleanKey = colonIndex !== -1 ? key.slice(colonIndex + 1) : key;
|
|
96
96
|
newObj[cleanKey] = stripTagPrefix(value);
|
package/src/utils/zip.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ZIP
|
|
2
|
+
* ZIP 파일 처리 유틸리티
|
|
3
3
|
*/
|
|
4
4
|
import type { FileEntry } from "@zip.js/zip.js";
|
|
5
5
|
import {
|
|
@@ -18,25 +18,25 @@ export interface ZipArchiveProgress {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* ZIP
|
|
21
|
+
* ZIP 아카이브 처리 클래스
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* ZIP 파일의 읽기, 쓰기, 압축, 해제를 처리.
|
|
24
|
+
* 동일 파일의 중복 해제를 방지하기 위해 내부 캐싱 사용.
|
|
25
25
|
*
|
|
26
26
|
* @example
|
|
27
|
-
* //
|
|
27
|
+
* // ZIP 파일 읽기
|
|
28
28
|
* await using archive = new ZipArchive(zipBytes);
|
|
29
29
|
* const content = await archive.get("file.txt");
|
|
30
30
|
*
|
|
31
31
|
* @example
|
|
32
|
-
* //
|
|
32
|
+
* // ZIP 파일 생성
|
|
33
33
|
* await using archive = new ZipArchive();
|
|
34
34
|
* archive.write("file.txt", textBytes);
|
|
35
35
|
* archive.write("data.json", jsonBytes);
|
|
36
36
|
* const zipBytes = await archive.compress();
|
|
37
37
|
*
|
|
38
38
|
* @example
|
|
39
|
-
* //
|
|
39
|
+
* // 모든 파일 추출 (진행률 보고 포함)
|
|
40
40
|
* await using archive = new ZipArchive(zipBytes);
|
|
41
41
|
* const files = await archive.extractAll((progress) => {
|
|
42
42
|
* console.log(`${progress.fileName}: ${progress.extractedSize}/${progress.totalSize}`);
|
|
@@ -48,8 +48,8 @@ export class ZipArchive {
|
|
|
48
48
|
private _entries?: Awaited<ReturnType<ZipReader<Blob | Bytes>["getEntries"]>>;
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
52
|
-
* @param data ZIP
|
|
51
|
+
* ZipArchive 생성
|
|
52
|
+
* @param data ZIP 데이터 (생략하면 새 아카이브 생성)
|
|
53
53
|
*/
|
|
54
54
|
constructor(data?: Blob | Bytes) {
|
|
55
55
|
if (!data) return;
|
|
@@ -70,8 +70,8 @@ export class ZipArchive {
|
|
|
70
70
|
|
|
71
71
|
//#region extractAll
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
74
|
-
* @param progressCallback
|
|
73
|
+
* 모든 파일 추출
|
|
74
|
+
* @param progressCallback 진행률 콜백
|
|
75
75
|
*/
|
|
76
76
|
async extractAll(
|
|
77
77
|
progressCallback?: (progress: ZipArchiveProgress) => void,
|
|
@@ -79,7 +79,7 @@ export class ZipArchive {
|
|
|
79
79
|
const entries = await this._getEntries();
|
|
80
80
|
if (entries == null) return this._cache;
|
|
81
81
|
|
|
82
|
-
//
|
|
82
|
+
// 추출할 전체 크기 계산
|
|
83
83
|
const totalSize = entries
|
|
84
84
|
.filter((e) => !e.directory)
|
|
85
85
|
.reduce((acc, e) => acc + e.uncompressedSize, 0);
|
|
@@ -112,7 +112,7 @@ export class ZipArchive {
|
|
|
112
112
|
|
|
113
113
|
this._cache.set(entry.filename, entryBytes);
|
|
114
114
|
|
|
115
|
-
//
|
|
115
|
+
// 개별 파일 완료 시 누적
|
|
116
116
|
totalExtracted += entry.uncompressedSize;
|
|
117
117
|
|
|
118
118
|
progressCallback?.({
|
|
@@ -128,8 +128,8 @@ export class ZipArchive {
|
|
|
128
128
|
|
|
129
129
|
//#region get
|
|
130
130
|
/**
|
|
131
|
-
*
|
|
132
|
-
* @param fileName
|
|
131
|
+
* 특정 파일 추출
|
|
132
|
+
* @param fileName 파일 이름
|
|
133
133
|
*/
|
|
134
134
|
async get(fileName: string): Promise<Bytes | undefined> {
|
|
135
135
|
if (this._cache.has(fileName)) {
|
|
@@ -156,8 +156,8 @@ export class ZipArchive {
|
|
|
156
156
|
|
|
157
157
|
//#region exists
|
|
158
158
|
/**
|
|
159
|
-
*
|
|
160
|
-
* @param fileName
|
|
159
|
+
* 파일 존재 여부 확인
|
|
160
|
+
* @param fileName 파일 이름
|
|
161
161
|
*/
|
|
162
162
|
async exists(fileName: string): Promise<boolean> {
|
|
163
163
|
if (this._cache.has(fileName)) {
|
|
@@ -176,9 +176,9 @@ export class ZipArchive {
|
|
|
176
176
|
|
|
177
177
|
//#region write
|
|
178
178
|
/**
|
|
179
|
-
*
|
|
180
|
-
* @param fileName
|
|
181
|
-
* @param bytes
|
|
179
|
+
* 파일 쓰기 (캐시에 저장)
|
|
180
|
+
* @param fileName 파일 이름
|
|
181
|
+
* @param bytes 파일 내용
|
|
182
182
|
*/
|
|
183
183
|
write(fileName: string, bytes: Bytes): void {
|
|
184
184
|
this._cache.set(fileName, bytes);
|
|
@@ -187,11 +187,11 @@ export class ZipArchive {
|
|
|
187
187
|
|
|
188
188
|
//#region compress
|
|
189
189
|
/**
|
|
190
|
-
*
|
|
190
|
+
* 캐시된 파일을 ZIP으로 압축
|
|
191
191
|
*
|
|
192
192
|
* @remarks
|
|
193
|
-
*
|
|
194
|
-
*
|
|
193
|
+
* 내부적으로 `extractAll()`을 호출하여 모든 파일을 메모리에 로드한 후 압축.
|
|
194
|
+
* 대용량 ZIP 파일 처리 시 메모리 사용량에 주의.
|
|
195
195
|
*/
|
|
196
196
|
async compress(): Promise<Bytes> {
|
|
197
197
|
const fileMap = await this.extractAll();
|
|
@@ -211,7 +211,7 @@ export class ZipArchive {
|
|
|
211
211
|
|
|
212
212
|
//#region close
|
|
213
213
|
/**
|
|
214
|
-
*
|
|
214
|
+
* 리더 닫기 및 캐시 비우기
|
|
215
215
|
*/
|
|
216
216
|
async close(): Promise<void> {
|
|
217
217
|
await this._reader?.close();
|
|
@@ -219,7 +219,7 @@ export class ZipArchive {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
/**
|
|
222
|
-
*
|
|
222
|
+
* await using 지원
|
|
223
223
|
*/
|
|
224
224
|
async [Symbol.asyncDispose](): Promise<void> {
|
|
225
225
|
await this.close();
|
package/docs/extensions.md
DELETED
|
@@ -1,387 +0,0 @@
|
|
|
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
|
-
```
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { SdError, ArgumentError } from "@simplysm/core-common";
|
|
3
|
-
|
|
4
|
-
describe("Errors", () => {
|
|
5
|
-
//#region SdError
|
|
6
|
-
|
|
7
|
-
describe("SdError", () => {
|
|
8
|
-
it("Creates with cause", () => {
|
|
9
|
-
const cause = new Error("original error");
|
|
10
|
-
const error = new SdError(cause, "wrapped message");
|
|
11
|
-
|
|
12
|
-
// Message combined as "wrapped message => original error" format
|
|
13
|
-
expect(error.message).toContain("wrapped message");
|
|
14
|
-
expect(error.message).toContain("original error");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("Integrates cause message", () => {
|
|
18
|
-
const cause = new Error("cause message");
|
|
19
|
-
const error = new SdError(cause, "main message");
|
|
20
|
-
|
|
21
|
-
expect(error.message).toContain("main message");
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("Handles multi-level cause chain", () => {
|
|
25
|
-
const root = new Error("root error");
|
|
26
|
-
const middle = new SdError(root, "middle error");
|
|
27
|
-
const top = new SdError(middle, "top error");
|
|
28
|
-
|
|
29
|
-
expect(top.message).toContain("top error");
|
|
30
|
-
expect(top.message).toContain("middle error");
|
|
31
|
-
expect(top.message).toContain("root error");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("Integrates cause stack to current stack", () => {
|
|
35
|
-
const cause = new Error("cause error");
|
|
36
|
-
const error = new SdError(cause, "main error");
|
|
37
|
-
|
|
38
|
-
expect(error.stack).toContain("---- cause stack ----");
|
|
39
|
-
expect(error.stack).toContain(cause.stack);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("Converts non-Error object passed as cause using String()", () => {
|
|
43
|
-
// number
|
|
44
|
-
const errorFromNumber = new SdError(42, "number cause");
|
|
45
|
-
expect(errorFromNumber.message).toContain("42");
|
|
46
|
-
|
|
47
|
-
// object
|
|
48
|
-
const errorFromObject = new SdError({ code: 500, reason: "server error" }, "object cause");
|
|
49
|
-
expect(errorFromObject.message).toContain("object cause");
|
|
50
|
-
|
|
51
|
-
// null/undefined
|
|
52
|
-
const errorFromNull = new SdError(null, "null cause");
|
|
53
|
-
expect(errorFromNull.message).toContain("null cause");
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
//#endregion
|
|
58
|
-
|
|
59
|
-
//#region ArgumentError
|
|
60
|
-
|
|
61
|
-
describe("ArgumentError", () => {
|
|
62
|
-
it("Creates with argObj", () => {
|
|
63
|
-
const error = new ArgumentError("invalid argument", { param: "value", expected: "string" });
|
|
64
|
-
|
|
65
|
-
// argObj included in message as YAML format
|
|
66
|
-
expect(error.message).toContain("invalid argument");
|
|
67
|
-
expect(error.message).toContain("param");
|
|
68
|
-
expect(error.message).toContain("value");
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("Creates with only argObj without message", () => {
|
|
72
|
-
const error = new ArgumentError({ key: "value" });
|
|
73
|
-
|
|
74
|
-
expect(error.message).toContain("Invalid arguments");
|
|
75
|
-
expect(error.message).toContain("key");
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
//#endregion
|
|
80
|
-
});
|