@ddunigma/node 2.1.1 → 3.0.0

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.
Files changed (70) hide show
  1. package/README.md +178 -229
  2. package/dist/DduStream-NP3CMCKK.js +2 -0
  3. package/dist/chunk-FVBZKVPE.js +4 -0
  4. package/dist/index.cjs +7 -0
  5. package/dist/index.d.cts +784 -0
  6. package/dist/index.d.ts +784 -0
  7. package/dist/index.js +5 -0
  8. package/package.json +39 -16
  9. package/dist/cjs/base/BaseDdu.d.ts +0 -53
  10. package/dist/cjs/base/BaseDdu.js +0 -29
  11. package/dist/cjs/base/index.d.ts +0 -1
  12. package/dist/cjs/base/index.js +0 -5
  13. package/dist/cjs/charSets/index.d.ts +0 -18
  14. package/dist/cjs/charSets/index.js +0 -4385
  15. package/dist/cjs/encoders/Ddu64.d.ts +0 -302
  16. package/dist/cjs/encoders/Ddu64.js +0 -1308
  17. package/dist/cjs/encoders/index.d.ts +0 -1
  18. package/dist/cjs/encoders/index.js +0 -5
  19. package/dist/cjs/index.d.ts +0 -5
  20. package/dist/cjs/index.js +0 -16
  21. package/dist/cjs/package.json +0 -3
  22. package/dist/cjs/types/DduDefaultTypes.d.ts +0 -3
  23. package/dist/cjs/types/DduDefaultTypes.js +0 -17
  24. package/dist/cjs/types/DduEnums.d.ts +0 -6
  25. package/dist/cjs/types/DduEnums.js +0 -10
  26. package/dist/cjs/types/DduInterface.d.ts +0 -91
  27. package/dist/cjs/types/DduInterface.js +0 -2
  28. package/dist/cjs/types/index.d.ts +0 -3
  29. package/dist/cjs/types/index.js +0 -19
  30. package/dist/cjs/utils/CharsetBuilder.d.ts +0 -129
  31. package/dist/cjs/utils/CharsetBuilder.js +0 -246
  32. package/dist/cjs/utils/DduPipeline.d.ts +0 -114
  33. package/dist/cjs/utils/DduPipeline.js +0 -229
  34. package/dist/cjs/utils/DduStream.d.ts +0 -64
  35. package/dist/cjs/utils/DduStream.js +0 -221
  36. package/dist/cjs/utils/crypto.d.ts +0 -30
  37. package/dist/cjs/utils/crypto.js +0 -84
  38. package/dist/cjs/utils/index.d.ts +0 -3
  39. package/dist/cjs/utils/index.js +0 -12
  40. package/dist/mjs/base/BaseDdu.d.ts +0 -53
  41. package/dist/mjs/base/BaseDdu.js +0 -23
  42. package/dist/mjs/base/index.d.ts +0 -1
  43. package/dist/mjs/base/index.js +0 -1
  44. package/dist/mjs/charSets/index.d.ts +0 -18
  45. package/dist/mjs/charSets/index.js +0 -4380
  46. package/dist/mjs/encoders/Ddu64.d.ts +0 -302
  47. package/dist/mjs/encoders/Ddu64.js +0 -1343
  48. package/dist/mjs/encoders/index.d.ts +0 -1
  49. package/dist/mjs/encoders/index.js +0 -1
  50. package/dist/mjs/index.d.ts +0 -5
  51. package/dist/mjs/index.js +0 -4
  52. package/dist/mjs/package.json +0 -3
  53. package/dist/mjs/types/DduDefaultTypes.d.ts +0 -3
  54. package/dist/mjs/types/DduDefaultTypes.js +0 -14
  55. package/dist/mjs/types/DduEnums.d.ts +0 -6
  56. package/dist/mjs/types/DduEnums.js +0 -7
  57. package/dist/mjs/types/DduInterface.d.ts +0 -91
  58. package/dist/mjs/types/DduInterface.js +0 -1
  59. package/dist/mjs/types/index.d.ts +0 -3
  60. package/dist/mjs/types/index.js +0 -3
  61. package/dist/mjs/utils/CharsetBuilder.d.ts +0 -129
  62. package/dist/mjs/utils/CharsetBuilder.js +0 -241
  63. package/dist/mjs/utils/DduPipeline.d.ts +0 -114
  64. package/dist/mjs/utils/DduPipeline.js +0 -223
  65. package/dist/mjs/utils/DduStream.d.ts +0 -64
  66. package/dist/mjs/utils/DduStream.js +0 -225
  67. package/dist/mjs/utils/crypto.d.ts +0 -30
  68. package/dist/mjs/utils/crypto.js +0 -78
  69. package/dist/mjs/utils/index.d.ts +0 -3
  70. package/dist/mjs/utils/index.js +0 -3
package/README.md CHANGED
@@ -1,23 +1,16 @@
1
- ## ddunigma Node
1
+ # ddunigma Node
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/@ddunigma%2Fnode.svg)](https://www.npmjs.com/package/@ddunigma/node)
4
4
 
5
- ## Overview
6
-
7
- Node.js implementation of [ddunigma](https://github.com/i3l3/ddunigma) (Python original)
8
-
9
5
  커스텀 charset을 사용하는 Base64 스타일 인코더/디코더 라이브러리입니다.
10
6
 
11
- ### Credits
7
+ V2 추가사항
12
8
 
13
- - Original Python Implementation by:
14
- - [@i3ls](https://github.com/i3l3)
15
- - [@gunu3371](https://github.com/gunu3371)
16
- - Original Repository: [ddunigma](https://github.com/i3l3/ddunigma)
9
+ - 이제 한글 종성 결합 시스템을 활용하여 8개 기본 문자 × 8개 종성으로 64가지 조합을 만들어, 6비트를 한 글자로 표현합니다.
17
10
 
18
11
  ## Requirements
19
12
 
20
- - **Node.js >= 14.0.0**
13
+ - **Node.js >= 18.0.0**
21
14
 
22
15
  ## Install
23
16
 
@@ -25,136 +18,143 @@ Node.js implementation of [ddunigma](https://github.com/i3l3/ddunigma) (Python o
25
18
  npm install @ddunigma/node
26
19
  ```
27
20
 
28
- ## Usage
21
+ ## Quick Start
22
+
23
+ ```typescript
24
+ import { Ddu64, DduSetSymbol } from "@ddunigma/node";
25
+
26
+ // V2 (기본, 한글 종성 결합 64개)
27
+ const ddu = new Ddu64();
28
+ ddu.encode("안녕하세요"); // "뎯땩잇땨뎪뎨잇잉뎯욱잇우뎯땨읶뎨뎯땩듂잊"
29
+ ddu.decode("뎯땩잇땨뎪뎨잇잉뎯욱잇우뎯땨읶뎨뎯땩듂잊"); // "안녕하세요"
30
+
31
+ // V1 (구버전 호환, 8개 문자 쌍 방식)
32
+ const dduV1 = new Ddu64(undefined, undefined, { dduSetSymbol: DduSetSymbol.DDU_V1 });
33
+ dduV1.encode("안녕하세요"); // ".우땨땨이?땨뜌.이.뜌이?이!.우우땨이?우뜌.우땨뜌이이.뜌.우땨땨!이이야"
34
+ dduV1.decode(".우땨땨이?땨뜌.이.뜌이?이!.우우땨이?우뜌.우땨뜌이이.뜌.우땨땨!이이야"); // "안녕하세요"
35
+ ```
36
+
37
+ ---
38
+
39
+ ## 기본 사용법
29
40
 
30
- ### 기본 인코딩/디코딩
41
+ ### 인코딩/디코딩
31
42
 
32
43
  ```typescript
33
44
  import { Ddu64 } from "@ddunigma/node";
34
45
 
35
- // 커스텀 charset으로 인코더 생성
36
- const encoder = new Ddu64("우따야", "뭐");
37
-
38
- const text = "Hello World!";
39
- const encoded = encoder.encode(text);
40
- console.log(encoded); // 우따야뭐우따... (인코딩된 문자열)
46
+ const encoder = new Ddu64();
41
47
 
48
+ const encoded = encoder.encode("Hello World!");
42
49
  const decoded = encoder.decode(encoded);
43
- console.log(decoded); // "Hello World!"
44
50
  ```
45
51
 
46
- ### 미리 정의된 Charset 사용
52
+ ### 커스텀 charset
47
53
 
48
54
  ```typescript
49
- import { Ddu64, DduSetSymbol } from "@ddunigma/node";
55
+ // 문자열 또는 배열로 charset 지정
56
+ const encoder = new Ddu64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "=");
50
57
 
51
- // ONECHARSET (64개 문자)
52
- const encoder1 = new Ddu64(undefined, undefined, {
53
- dduSetSymbol: DduSetSymbol.ONECHARSET,
58
+ // 종성 결합 커스텀 charset (dduChar × codaChar 동적 생성)
59
+ const encoder2 = new Ddu64(["가", "나", "다", "라"], "뭐", {
60
+ codaChar: ["", "ㄱ", "ㄲ", "ㄷ"], // 4×4 = 16개 조합
54
61
  });
62
+ ```
55
63
 
56
- // DDU (8개 문자)
57
- const encoder2 = new Ddu64(undefined, undefined, {
58
- dduSetSymbol: DduSetSymbol.DDU,
59
- });
64
+ ### 프리셋
60
65
 
61
- // TWOCHARSET (1024개 문자)
62
- const encoder3 = new Ddu64(undefined, undefined, {
63
- dduSetSymbol: DduSetSymbol.TWOCHARSET,
64
- });
66
+ | Symbol | 문자 수 | 비트 | 설명 |
67
+ | ------------ | ------- | ---- | ------------------------------------ |
68
+ | `DDU` | 64 | 6 | 한글 종성 결합 (8 기본문자 × 8 종성) |
69
+ | `DDU_V1` | 8 | 3 | 구버전 호환 (뜌땨이우야!?.) |
70
+ | `ONECHARSET` | 64 | 6 | 영문 + 숫자 + 특수문자 |
65
71
 
66
- // THREECHARSET (32768개 문자)
67
- const encoder4 = new Ddu64(undefined, undefined, {
68
- dduSetSymbol: DduSetSymbol.THREECHARSET,
69
- });
72
+ ```typescript
73
+ import { Ddu64, DduSetSymbol } from "@ddunigma/node";
70
74
 
71
- const text = "안녕하세요";
72
- const encoded = encoder1.encode(text);
73
- const decoded = encoder1.decode(encoded);
75
+ const encoder = new Ddu64(undefined, undefined, {
76
+ dduSetSymbol: DduSetSymbol.ONECHARSET,
77
+ });
74
78
  ```
75
79
 
76
- ### 압축 인코딩
80
+ ---
81
+
82
+ ## 고급 기능
83
+
84
+ ### 압축
85
+
86
+ deflate(기본) 또는 brotli 압축을 지원합니다. 디코딩 시 자동으로 압축 여부를 감지합니다.
77
87
 
78
88
  ```typescript
79
- import { Ddu64 } from "@ddunigma/node";
89
+ const encoder = new Ddu64();
80
90
 
81
- // 생성자에서 기본 압축 활성화
82
- const encoder = new Ddu64(undefined, undefined, {
91
+ // 호출 옵션으로 지정
92
+ const encoded = encoder.encode(longText, { compress: true });
93
+ const decoded = encoder.decode(encoded);
94
+
95
+ // 생성자에서 기본 활성화
96
+ const compressEncoder = new Ddu64(undefined, undefined, {
83
97
  compress: true,
98
+ compressionAlgorithm: "brotli",
99
+ compressionLevel: 6,
84
100
  });
85
-
86
- // 또는 encode 호출 시 압축 옵션 지정
87
- const text = "반복되는 긴 텍스트...".repeat(100);
88
- const encoded = encoder.encode(text, { compress: true });
89
- const decoded = encoder.decode(encoded); // 자동으로 압축 해제
90
101
  ```
91
102
 
92
- ### URL-Safe 인코딩
103
+ ### 암호화
93
104
 
94
- ```typescript
95
- import { Ddu64 } from "@ddunigma/node";
105
+ AES-256-GCM 암호화를 내장합니다. 동일한 키로 생성된 인코더만 복호화할 수 있습니다.
96
106
 
107
+ ```typescript
97
108
  const encoder = new Ddu64(undefined, undefined, {
98
- dduSetSymbol: DduSetSymbol.ONECHARSET,
99
- urlSafe: true, // +, /, = 를 URL 안전 문자로 변환
109
+ encryptionKey: "my-secret-key",
100
110
  });
101
111
 
102
- const encoded = encoder.encode("Hello World!");
103
- // URL에서 안전하게 사용 가능한 문자열 반환
112
+ const encoded = encoder.encode("비밀 메시지");
113
+ const decoded = encoder.decode(encoded);
104
114
  ```
105
115
 
106
- ### 체크섬 (무결성 검증)
116
+ ### 체크섬
107
117
 
108
- ```typescript
109
- import { Ddu64 } from "@ddunigma/node";
118
+ CRC32 체크섬으로 데이터 무결성을 검증합니다.
110
119
 
111
- const encoder = new Ddu64(undefined, undefined, {
112
- checksum: true, // CRC32 체크섬 활성화
113
- });
120
+ ```typescript
121
+ const encoder = new Ddu64();
114
122
 
115
- const encoded = encoder.encode("Important data", { checksum: true });
116
- const decoded = encoder.decode(encoded); // 자동으로 체크섬 검증
117
- // 체크섬 불일치 시 에러 발생
123
+ const encoded = encoder.encode(data, { checksum: true });
124
+ const decoded = encoder.decode(encoded, { checksum: true });
125
+ // 데이터 변조 시 에러 발생
118
126
  ```
119
127
 
120
- ### 암호화
128
+ ### URL-Safe
121
129
 
122
- ```typescript
123
- import { Ddu64 } from "@ddunigma/node";
130
+ `+`, `/`, `=` 를 URL 안전 문자(`-`, `_`, `.`)로 변환합니다.
124
131
 
125
- const encoder = new Ddu64(undefined, undefined, {
126
- encryptionKey: "my-secret-key-123", // AES-256-GCM 암호화
132
+ ```typescript
133
+ const encoder = new Ddu64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "=", {
134
+ urlSafe: true,
127
135
  });
128
-
129
- const encoded = encoder.encode("Secret message!");
130
- const decoded = encoder.decode(encoded); // 자동으로 복호화
131
136
  ```
132
137
 
138
+ > charset/padding에 `-`, `_`, `.` 가 포함되면 urlSafe를 활성화할 수 없습니다.
139
+
133
140
  ### 청크 분할
134
141
 
135
142
  ```typescript
136
- import { Ddu64 } from "@ddunigma/node";
137
-
138
143
  const encoder = new Ddu64();
139
144
 
140
- const encoded = encoder.encode(longData, {
141
- chunkSize: 76, // 76자마다 분할
142
- chunkSeparator: "\n", // 줄바꿈으로 구분
145
+ const encoded = encoder.encode(data, {
146
+ chunkSize: 76,
147
+ chunkSeparator: "\n",
143
148
  });
144
- // 결과: "ABCDxyz...\nEFGHijk...\n..."
145
-
146
- const decoded = encoder.decode(encoded); // 자동으로 줄바꿈 제거
149
+ // 디코딩 시 구분자 자동 제거
147
150
  ```
148
151
 
149
- ### 비동기 인코딩/디코딩
150
-
151
- ```typescript
152
- import { Ddu64 } from "@ddunigma/node";
152
+ ### 비동기 처리
153
153
 
154
- const encoder = new Ddu64();
154
+ 대용량 데이터에서 이벤트 루프 블로킹을 방지합니다.
155
155
 
156
- // 비동기 인코딩 (이벤트 루프 블로킹 방지)
157
- const encoded = await encoder.encodeAsync(largeData);
156
+ ```typescript
157
+ const encoded = await encoder.encodeAsync(largeBuffer);
158
158
  const decoded = await encoder.decodeAsync(encoded);
159
159
  const buffer = await encoder.decodeToBufferAsync(encoded);
160
160
  ```
@@ -162,81 +162,51 @@ const buffer = await encoder.decodeToBufferAsync(encoded);
162
162
  ### 진행률 콜백
163
163
 
164
164
  ```typescript
165
- import { Ddu64 } from "@ddunigma/node";
166
-
167
- const encoder = new Ddu64();
168
-
169
- encoder.encode(largeData, {
170
- onProgress: (info) => {
171
- console.log(`진행률: ${info.percent}%`);
172
- console.log(`처리됨: ${info.processedBytes}/${info.totalBytes}`);
165
+ encoder.encode(data, {
166
+ onProgress: ({ percent, stage }) => {
167
+ console.log(`${percent}% (${stage})`);
173
168
  },
174
169
  });
175
170
  ```
176
171
 
177
- ### 통계/분석
172
+ ### 통계
178
173
 
179
174
  ```typescript
180
- import { Ddu64 } from "@ddunigma/node";
181
-
182
- const encoder = new Ddu64();
183
-
184
- const stats = encoder.getStats("Test data", { compress: true });
185
- console.log(stats);
186
- // {
187
- // originalSize: 9,
188
- // encodedSize: 12,
189
- // compressedSize: 17,
190
- // compressionRatio: 1.89,
191
- // expansionRatio: 1.33,
192
- // charsetSize: 64,
193
- // bitLength: 6
194
- // }
175
+ const stats = encoder.getStats(data, { compress: true });
176
+ // { originalSize, encodedSize, compressedSize, compressionRatio, expansionRatio, charsetSize, bitLength }
195
177
  ```
196
178
 
197
179
  ### Zip Bomb 방어
198
180
 
199
181
  ```typescript
200
- import { Ddu64 } from "@ddunigma/node";
201
-
202
182
  const encoder = new Ddu64(undefined, undefined, {
203
183
  maxDecodedBytes: 10 * 1024 * 1024, // 10MB
204
184
  maxDecompressedBytes: 50 * 1024 * 1024, // 50MB
205
185
  });
206
-
207
- // 제한 초과 시 에러 발생
208
186
  ```
209
187
 
210
188
  ---
211
189
 
212
190
  ## CharsetBuilder
213
191
 
214
- 커스텀 charset을 쉽게 생성할 수 있는 빌더 유틸리티입니다.
192
+ 커스텀 charset을 빌더 패턴으로 생성합니다.
215
193
 
216
194
  ```typescript
217
195
  import { CharsetBuilder } from "@ddunigma/node";
218
196
 
219
- // 유니코드 범위에서 생성
220
- const chars1 = CharsetBuilder.fromUnicodeRange(0x4e00, 0x4e3f).build();
221
-
222
- // Base64 문자셋
223
- const chars2 = CharsetBuilder.base64().build();
197
+ // 유니코드 범위
198
+ CharsetBuilder.fromUnicodeRange(0x4e00, 0x4e3f).build();
224
199
 
225
- // 혼동 문자 제외 (0, O, 1, l, I 등)
226
- const chars3 = CharsetBuilder.base64().excludeConfusing().build();
200
+ // Base64에서 혼동 문자 제외
201
+ CharsetBuilder.base64().excludeConfusing().build();
227
202
 
228
- // 2의 제곱수로 제한
229
- const chars4 = CharsetBuilder.fromString("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
203
+ // 2의 제곱수로 제한 + 시드 셔플
204
+ CharsetBuilder.fromString("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
230
205
  .limitToPowerOfTwo()
231
- .build(); // 32자
206
+ .shuffle(12345)
207
+ .build();
232
208
 
233
- // URL 안전 문자만
234
- const chars5 = CharsetBuilder.base64().excludeUrlUnsafe().build();
235
-
236
- // 시드 기반 셔플
237
- const chars6 = CharsetBuilder.base64().shuffle(12345).build();
238
-
239
- // 패딩 문자와 함께 빌드
209
+ // 패딩 문자 자동 선택
240
210
  const { charset, padding } = CharsetBuilder.base64().buildWithPadding();
241
211
  ```
242
212
 
@@ -244,44 +214,37 @@ const { charset, padding } = CharsetBuilder.base64().buildWithPadding();
244
214
 
245
215
  ## DduPipeline
246
216
 
247
- 다단계 인코딩/암호화/압축을 조합할 있는 파이프라인 빌더입니다.
217
+ 압축 암호화 인코딩을 체이닝하고, `reverse()`로 역순 복원합니다.
248
218
 
249
219
  ```typescript
250
220
  import { DduPipeline, Ddu64 } from "@ddunigma/node";
251
221
 
252
- const encoder = new Ddu64();
253
-
254
- // 압축 → 암호화 → 인코딩 파이프라인
255
- const pipeline = new DduPipeline()
256
- .compress()
257
- .encrypt("my-secret-key")
258
- .encode(encoder);
259
-
260
- const encoded = pipeline.processToString("Hello World!");
222
+ const pipeline = new DduPipeline().compress(6, "brotli").encrypt("secret-key").encode(new Ddu64());
261
223
 
262
- // 역순 파이프라인으로 복원
224
+ const encoded = pipeline.processToString("Hello");
263
225
  const decoded = pipeline.reverse().processToString(encoded);
264
226
  ```
265
227
 
266
228
  ---
267
229
 
268
- ## 스트림 지원
230
+ ## 스트림
269
231
 
270
- 대용량 파일 처리를 위한 스트림 인코딩/디코딩을 지원합니다.
232
+ 대용량 파일을 메모리 효율적으로 처리합니다. 스트림 헤더로 압축/암호화를 자동 감지합니다.
271
233
 
272
234
  ```typescript
273
235
  import { Ddu64, createEncodeStream, createDecodeStream } from "@ddunigma/node";
274
236
  import fs from "fs";
275
237
 
276
- const encoder = new Ddu64();
238
+ const encoder = new Ddu64(undefined, undefined, {
239
+ compress: true,
240
+ encryptionKey: "stream-key",
241
+ });
277
242
 
278
- // 인코딩 스트림
279
243
  fs.createReadStream("input.bin")
280
244
  .pipe(createEncodeStream(encoder))
281
- .pipe(fs.createWriteStream("output.txt"));
245
+ .pipe(fs.createWriteStream("output.ddu"));
282
246
 
283
- // 디코딩 스트림
284
- fs.createReadStream("output.txt")
247
+ fs.createReadStream("output.ddu")
285
248
  .pipe(createDecodeStream(encoder))
286
249
  .pipe(fs.createWriteStream("restored.bin"));
287
250
  ```
@@ -292,99 +255,85 @@ fs.createReadStream("output.txt")
292
255
 
293
256
  ### `new Ddu64(dduChar?, paddingChar?, options?)`
294
257
 
295
- 인코더 인스턴스를 생성합니다.
296
-
297
- **Parameters:**
298
-
299
258
  | Parameter | Type | Description |
300
259
  | ------------- | ----------------------- | ------------------------ |
301
260
  | `dduChar` | `string \| string[]` | charset 문자열 또는 배열 |
302
261
  | `paddingChar` | `string` | 패딩 문자 |
303
262
  | `options` | `DduConstructorOptions` | 옵션 객체 |
304
263
 
305
- **DduConstructorOptions:**
306
-
307
- | Option | Type | Default | Description |
308
- | ---------------------- | ---------------- | ----------- | --------------------------- |
309
- | `dduSetSymbol` | `DduSetSymbol` | `DDU` | 미리 정의된 charset 심볼 |
310
- | `encoding` | `BufferEncoding` | `'utf-8'` | 문자열 인코딩 |
311
- | `usePowerOfTwo` | `boolean` | `true` | 2의 제곱수 강제 여부 |
312
- | `useBuildErrorReturn` | `boolean` | `false` | 에러 발생 시 throw 여부 |
313
- | `compress` | `boolean` | `false` | 기본 압축 활성화 |
314
- | `maxDecodedBytes` | `number` | `67108864` | 최대 디코딩 바이트 (64MB) |
315
- | `maxDecompressedBytes` | `number` | `67108864` | 최대 압축해제 바이트 (64MB) |
316
- | `urlSafe` | `boolean` | `false` | URL-Safe 모드 |
317
- | `encryptionKey` | `string` | `undefined` | AES-256-GCM 암호화 키 |
318
- | `checksum` | `boolean` | `false` | CRC32 체크섬 활성화 |
319
- | `chunkSize` | `number` | `undefined` | 청크 분할 크기 |
320
- | `chunkSeparator` | `string` | `'\n'` | 청크 구분자 |
321
-
322
- ### `encode(data, options?): string`
323
-
324
- 데이터를 인코딩합니다.
325
-
326
- **DduOptions:**
327
-
328
- | Option | Type | Description |
329
- | ---------------- | ---------- | ---------------- |
330
- | `compress` | `boolean` | 압축 사용 여부 |
331
- | `checksum` | `boolean` | 체크섬 추가 여부 |
332
- | `chunkSize` | `number` | 청크 분할 크기 |
333
- | `chunkSeparator` | `string` | 청크 구분자 |
334
- | `onProgress` | `function` | 진행률 콜백 |
335
-
336
- ### `decode(encoded, options?): string`
337
-
338
- 인코딩된 문자열을 디코딩합니다.
339
-
340
- ### `decodeToBuffer(encoded, options?): Buffer`
341
-
342
- 인코딩된 문자열을 Buffer로 직접 디코딩합니다.
264
+ **생성자 옵션:**
265
+
266
+ | Option | Type | Default | Description |
267
+ | ---------------------- | ----------------------- | ----------- | ----------------------------- |
268
+ | `dduSetSymbol` | `DduSetSymbol` | `DDU` | 프리셋 심볼 |
269
+ | `codaChar` | `string[]` | | 종성 배열 (동적 charset 생성) |
270
+ | `encoding` | `BufferEncoding` | `'utf-8'` | 문자열 인코딩 |
271
+ | `compress` | `boolean` | `false` | 기본 압축 활성화 |
272
+ | `compressionAlgorithm` | `"deflate" \| "brotli"` | `'deflate'` | 압축 알고리즘 |
273
+ | `compressionLevel` | `number` | `6` | 압축 레벨 |
274
+ | `urlSafe` | `boolean` | `false` | URL-Safe 모드 |
275
+ | `encryptionKey` | `string` | | AES-256-GCM 암호화 키 |
276
+ | `checksum` | `boolean` | `false` | CRC32 체크섬 |
277
+ | `chunkSize` | `number` | | 청크 분할 크기 |
278
+ | `chunkSeparator` | `string` | `'\n'` | 청크 구분자 |
279
+ | `maxDecodedBytes` | `number` | `67108864` | 최대 디코딩 바이트 (64MB) |
280
+ | `maxDecompressedBytes` | `number` | `67108864` | 최대 압축해제 바이트 (64MB) |
281
+ | `throwOnError` | `boolean` | `false` | 초기화 오류 시 throw |
282
+ | `useRepeatPadding` | `boolean` | `false` | 패딩 문자 반복 방식 |
283
+ | `usePowerOfTwo` | `boolean` | `true` | 2의 제곱수 charset 강제 |
284
+
285
+ **메서드:**
286
+
287
+ | Method | Return | Description |
288
+ | ---------------------------------------- | ----------------- | -------------------- |
289
+ | `encode(data, options?)` | `string` | 인코딩 |
290
+ | `decode(encoded, options?)` | `string` | 디코딩 |
291
+ | `decodeToBuffer(encoded, options?)` | `Buffer` | Buffer로 디코딩 |
292
+ | `encodeAsync(data, options?)` | `Promise<string>` | 비동기 인코딩 |
293
+ | `decodeAsync(encoded, options?)` | `Promise<string>` | 비동기 디코딩 |
294
+ | `decodeToBufferAsync(encoded, options?)` | `Promise<Buffer>` | 비동기 Buffer 디코딩 |
295
+ | `getStats(data, options?)` | `DduEncodeStats` | 인코딩 통계 |
296
+ | `getCharSetInfo()` | `CharSetInfo` | charset 정보 |
297
+
298
+ **encode/decode 옵션 (DduOptions):**
299
+
300
+ | Option | Type | Description |
301
+ | ---------------------- | ----------------------- | -------------------- |
302
+ | `compress` | `boolean` | 압축 사용 |
303
+ | `compressionAlgorithm` | `"deflate" \| "brotli"` | 압축 알고리즘 |
304
+ | `compressionLevel` | `number` | 압축 레벨 |
305
+ | `checksum` | `boolean` | 체크섬 추가/검증 |
306
+ | `chunkSize` | `number` | 청크 분할 크기 |
307
+ | `chunkSeparator` | `string` | 청크 구분자 |
308
+ | `maxDecodedBytes` | `number` | 최대 디코딩 바이트 |
309
+ | `maxDecompressedBytes` | `number` | 최대 압축해제 바이트 |
310
+ | `onProgress` | `function` | 진행률 콜백 |
343
311
 
344
- ### `encodeAsync(data, options?): Promise<string>`
345
-
346
- 비동기로 데이터를 인코딩합니다.
347
-
348
- ### `decodeAsync(encoded, options?): Promise<string>`
349
-
350
- 비동기로 데이터를 디코딩합니다.
351
-
352
- ### `decodeToBufferAsync(encoded, options?): Promise<Buffer>`
353
-
354
- 비동기로 Buffer로 디코딩합니다.
355
-
356
- ### `getStats(data, options?): DduEncodeStats`
312
+ ---
357
313
 
358
- 인코딩 통계 정보를 반환합니다.
314
+ ## Testing
359
315
 
360
- ```typescript
361
- {
362
- originalSize: number; // 원본 데이터 크기
363
- encodedSize: number; // 인코딩된 문자열 길이
364
- compressedSize?: number; // 압축된 크기
365
- compressionRatio?: number; // 압축률 (0-1)
366
- expansionRatio: number; // 인코딩 확장 비율
367
- charsetSize: number; // charset 크기
368
- bitLength: number; // 비트 길이
369
- }
316
+ ```bash
317
+ pnpm test # 전체 테스트
318
+ pnpm test:watch # 워치 모드
319
+ pnpm test:coverage # 커버리지
370
320
  ```
371
321
 
372
- ### `getCharSetInfo(): CharSetInfo`
322
+ ## Build & Verify
373
323
 
374
- 현재 인코더의 charset 정보를 반환합니다.
324
+ ```bash
325
+ pnpm build
326
+ pnpm lint
327
+ pnpm bench
328
+ pnpm pack:check
329
+ ```
375
330
 
376
331
  ---
377
332
 
378
- ## DduSetSymbol
379
-
380
- | Symbol | 문자 수 | 비트 길이 | 설명 |
381
- | -------------- | ------- | --------- | ------------------------- |
382
- | `DDU` | 8 | 3 | 한글 + 특수문자 기본 세트 |
383
- | `ONECHARSET` | 64 | 6 | 영문 + 숫자 + 특수문자 |
384
- | `TWOCHARSET` | 1024 | 10 | 2글자 조합 세트 |
385
- | `THREECHARSET` | 32768 | 15 | 3글자 조합 세트 |
333
+ ## Credits
386
334
 
387
- ---
335
+ - Original: [@i3ls](https://github.com/i3l3), [@gunu3371](https://github.com/gunu3371)
336
+ - Repository: [ddunigma](https://github.com/i3l3/ddunigma)
388
337
 
389
338
  ## License
390
339
 
@@ -0,0 +1,2 @@
1
+ export{o as DduDecodeStream,n as DduEncodeStream,q as createDecodeStream,p as createEncodeStream}from'./chunk-FVBZKVPE.js';//# sourceMappingURL=DduStream-NP3CMCKK.js.map
2
+ //# sourceMappingURL=DduStream-NP3CMCKK.js.map
@@ -0,0 +1,4 @@
1
+ import {Transform,PassThrough}from'stream';import {createBrotliCompress,constants,createDeflate,createBrotliDecompress,createInflate}from'zlib';var N=["-","_","."],E={"":0,\u3131:1,\u3132:2,\u3133:3,\u3134:4,\u3135:5,\u3136:6,\u3137:7,\u3139:8,\u313A:9,\u313B:10,\u313C:11,\u313D:12,\u313E:13,\u313F:14,\u3140:15,\u3141:16,\u3142:17,\u3144:18,\u3145:19,\u3146:20,\u3147:21,\u3148:22,\u314A:23,\u314B:24,\u314C:25,\u314D:26,\u314E:27};function x(n,e){let r=n.charCodeAt(0);if(r<44032||r>55203)return n;let t=r-(r-44032)%28,i=E[e]??0;return String.fromCharCode(t+i)}function P(n,e){let r=new Array(n.length*e.length),t=0;for(let i of n)for(let o of e)r[t++]=x(i,o);return r}var z=(()=>{let n=new Uint32Array(256);for(let e=0;e<256;e++){let r=e;for(let t=0;t<8;t++)r=r>>>1^(r&1?3988292384:0);n[e]=r;}return n})(),H="ELYSIA",K="GRISEO",b="CHK",W="ENC";function F(n){return n.length===0?n:n.replaceAll("+","-").replaceAll("/","_").replaceAll("=",".")}function j(n){return n.length===0?n:n.replaceAll("-","+").replaceAll("_","/").replaceAll(".","=")}function U(n,e,r){if(e<=0||n.length<=e)return n;let t=Math.ceil(n.length/e),i=new Array(t*2-1),o=0;for(let s=0;s<n.length;s+=e)i[o++]=n.slice(s,s+e),s+e<n.length&&(i[o++]=r);return i.join("")}function h(n,e){if(n.length===0)return n;let r=n.replace(/[\r\n]/g,""),t=e&&e!==`
2
+ `&&e!==`\r
3
+ `&&e!=="\r"?e:"";return t.length>0&&r.includes(t)&&(r=r.replaceAll(t,"")),r}function J(n){let e=4294967295;for(let r=0;r<n.length;r++)e=e>>>8^z[(e^n[r])&255];return ((e^4294967295)>>>0).toString(16).padStart(8,"0")}function $(n){let e=n.lastIndexOf(b);if(e===-1)return {data:n,checksum:null};let r=n.slice(e+b.length);return r.length!==8||!/^[0-9a-f]+$/i.test(r)?{data:n,checksum:null}:{data:n.slice(0,e),checksum:r.toLowerCase()}}function C(n,e){let t=n===void 0||!Number.isFinite(n)?6:Math.floor(n);return Math.min(e==="brotli"?11:9,Math.max(0,t))}var c="DDS1";function v(n){return n.length*2+c.length+2}function k(n,e){let r=e.compressionAlgorithm==="brotli"?"B":e.compressionAlgorithm==="deflate"?"D":"N",t=e.encrypted?"1":"0";return `${n}${c}${r}${t}${n}`}function R(n,e){let r=v(e);if(n.length<r||!n.startsWith(e))return null;let t=e.length;if(n.slice(t,t+c.length)!==c)throw new Error("[DduStream decode] Invalid stream header magic");let o=n[t+c.length],s=n[t+c.length+1];if(n.slice(r-e.length,r)!==e)throw new Error("[DduStream decode] Invalid stream header terminator");let a=o==="B"?"brotli":o==="D"?"deflate":o==="N"?void 0:null;if(a===null)throw new Error("[DduStream decode] Invalid stream header compression flag");if(s!=="0"&&s!=="1")throw new Error("[DduStream decode] Invalid stream header encryption flag");return {compressionAlgorithm:a,encrypted:s==="1"}}var m=class extends Transform{prefix;emittedPrefix=false;constructor(e){super(),this.prefix=e;}_transform(e,r,t){try{this.emittedPrefix||(this.push(this.prefix),this.emittedPrefix=!0),this.push(e),t();}catch(i){t(i);}}};function _(n,e){let r=new PassThrough,t=r.write.bind(r);return r.write=function(i,o,s){return typeof o=="function"?n.write(i,o):n.write(i,o,s)},r.end=function(i,o,s){return typeof i=="function"?n.end(i):typeof o=="function"?n.end(i,o):n.end(i,o,s)},e.on("data",i=>t(i)),e.on("end",()=>r.push(null)),e.on("error",i=>r.destroy(i)),n.on("error",i=>r.destroy(i)),r}function A(n){if(n.length===0)return new PassThrough;for(let r=0;r<n.length-1;r++)n[r].pipe(n[r+1]);let e=_(n[0],n[n.length-1]);for(let r of n)r.on("error",t=>{e.destroy(t);});return e}var p=class extends Transform{encoder;buffers;bufferOffset;totalLength;chunkSize;footerCompressionAlgorithm;footerEncrypted;constructor(e,r){super({...r,readableObjectMode:false,writableObjectMode:false}),this.encoder=e;let t=e.getCharSetInfo();this.buffers=[],this.bufferOffset=0,this.totalLength=0,this.footerCompressionAlgorithm=r?.compress??t.defaultCompress?r?.compressionAlgorithm??t.defaultCompressionAlgorithm:void 0,this.footerEncrypted=(r?.encrypt??true)&&t.hasEncryptionKey,this.chunkSize=this.calculateChunkSize(t.bitLength);}calculateChunkSize(e){let r=(o,s)=>s===0?o:r(s,o%s),t=8*e/r(8,e),i=Math.max(1,Math.floor(4096/t));return t*i}_transform(e,r,t){try{for(this.buffers.push(e),this.totalLength+=e.length;this.totalLength>=this.chunkSize;){let i=this.readBytes(this.chunkSize),o=this.encodeChunk(i,!1);this.push(o);}t();}catch(i){t(i);}}_flush(e){try{if(this.totalLength>0){let r=this.readBytes(this.totalLength),t=this.encodeChunk(r,!0);this.push(t);}e();}catch(r){e(r);}}encodeChunk(e,r){return this.encoder.encodeRawBuffer(e,{compressionAlgorithm:r?this.footerCompressionAlgorithm:void 0,encrypted:r?this.footerEncrypted:false,omitFooter:!r})}readBytes(e){if(e<=0||this.totalLength<e)return Buffer.alloc(0);let r=this.buffers[0];if(this.buffers.length===1&&r&&this.bufferOffset===0&&r.length===e)return this.buffers=[],this.totalLength=0,r;let t=Buffer.allocUnsafe(e),i=0;for(;i<e&&this.buffers.length>0;){let o=this.buffers[0],s=o.length-this.bufferOffset,f=Math.min(e-i,s);o.copy(t,i,this.bufferOffset,this.bufferOffset+f),i+=f,this.bufferOffset+=f,this.bufferOffset>=o.length&&(this.buffers.shift(),this.bufferOffset=0);}return this.totalLength-=e,t}},g=class extends Transform{encoder;options;normalizedBuffer;chunkSize;chunkSeparator;separatorTail;constructor(e,r){super({...r,readableObjectMode:false,writableObjectMode:false}),this.encoder=e,this.options={...r,compress:false,checksum:false,encrypt:false},this.normalizedBuffer="";let t=e.getCharSetInfo();this.chunkSeparator=r?.chunkSeparator??t.defaultChunkSeparator,this.separatorTail="",this.chunkSize=1024;}_transform(e,r,t){try{this.appendNormalizedChunk(e.toString("utf-8"));let i=100;for(;this.normalizedBuffer.length>=this.chunkSize+i;){let o=this.normalizedBuffer.slice(0,this.chunkSize);this.normalizedBuffer=this.normalizedBuffer.slice(this.chunkSize);let s=this.encoder.decodeToBuffer(o,this.options);this.push(s);}t();}catch(i){t(i);}}_flush(e){try{if(this.separatorTail.length>0&&(this.normalizedBuffer+=h(this.separatorTail,this.chunkSeparator),this.separatorTail=""),this.normalizedBuffer.length>0){let r=this.encoder.decodeStreamToBuffer(this.normalizedBuffer,this.options);this.push(r),this.normalizedBuffer="";}e();}catch(r){e(r);}}appendNormalizedChunk(e){if(e.length===0)return;let r=Math.max(0,this.chunkSeparator.length-1);if(r===0){this.normalizedBuffer+=h(e,this.chunkSeparator);return}let t=this.separatorTail+e;if(t.length<=r){this.separatorTail=t;return}let i=t.length-r,o=t.slice(0,i);this.separatorTail=t.slice(i),this.normalizedBuffer+=h(o,this.chunkSeparator);}},y=class extends Transform{encoder;options;normalizedBuffer;chunkSeparator;separatorTail;paddingChar;headerLength;mode;innerStream;constructor(e,r){super({...r,readableObjectMode:false,writableObjectMode:false}),this.encoder=e,this.options={...r},this.normalizedBuffer="";let t=e.getCharSetInfo();this.paddingChar=t.paddingChar,this.headerLength=v(t.paddingChar),this.chunkSeparator=r?.chunkSeparator??t.defaultChunkSeparator,this.separatorTail="",this.mode="pending",this.innerStream=null;}_transform(e,r,t){try{if(this.appendNormalizedChunk(e.toString("utf-8")),this.maybeInitializeMode(!1),this.mode==="pipeline"){this.flushBufferedToInner(t);return}t();}catch(i){t(i);}}_flush(e){try{if(this.separatorTail.length>0&&(this.normalizedBuffer+=h(this.separatorTail,this.chunkSeparator),this.separatorTail=""),this.maybeInitializeMode(!0),this.mode==="pipeline"){this.finishInnerPipeline(e);return}if(this.normalizedBuffer.length>0){let r=this.encoder.decodeStreamToBuffer(this.normalizedBuffer,this.options);this.push(r),this.normalizedBuffer="";}e();}catch(r){e(r);}}appendNormalizedChunk(e){if(e.length===0)return;let r=Math.max(0,this.chunkSeparator.length-1);if(r===0){this.normalizedBuffer+=h(e,this.chunkSeparator);return}let t=this.separatorTail+e;if(t.length<=r){this.separatorTail=t;return}let i=t.length-r,o=t.slice(0,i);this.separatorTail=t.slice(i),this.normalizedBuffer+=h(o,this.chunkSeparator);}maybeInitializeMode(e){if(this.mode!=="pending")return;let r=this.paddingChar.length;if(this.normalizedBuffer.length<r){e&&this.normalizedBuffer.length>0&&(this.mode="legacy");return}if(!this.normalizedBuffer.startsWith(this.paddingChar)){this.mode="legacy";return}if(this.normalizedBuffer.length<this.headerLength){if(e)throw new Error("[DduStream decode] Incomplete stream header");return}let t=this.normalizedBuffer.slice(0,this.headerLength),i=R(t,this.paddingChar);if(!i){this.mode="legacy";return}this.mode="pipeline",this.normalizedBuffer=this.normalizedBuffer.slice(this.headerLength),this.initializeInnerPipeline(i);}initializeInnerPipeline(e){if(e.encrypted&&!this.encoder.getCharSetInfo().hasEncryptionKey)throw new Error("[DduStream decode] Encrypted stream requires an encryptionKey");let r=L(this.encoder,{...this.options,streamAutoDetect:false,compress:!!e.compressionAlgorithm,compressionAlgorithm:e.compressionAlgorithm,encrypt:e.encrypted});r.on("data",t=>{this.push(t);}),r.on("error",t=>{this.destroy(t);}),this.innerStream=r;}flushBufferedToInner(e){if(!this.innerStream||this.normalizedBuffer.length===0){e();return}let r=this.normalizedBuffer;if(this.normalizedBuffer="",!this.innerStream.write(Buffer.from(r,"utf-8"))){this.innerStream.once("drain",()=>e());return}e();}finishInnerPipeline(e){if(!this.innerStream){e();return}let r=this.innerStream,t=()=>{r.once("end",()=>e()),r.end();};if(this.normalizedBuffer.length>0){let i=this.normalizedBuffer;if(this.normalizedBuffer="",!r.write(Buffer.from(i,"utf-8"))){r.once("drain",t);return}}t();}};function Z(n,e){let r=n.getCharSetInfo(),t=e?.compress??r.defaultCompress,i=(e?.encrypt??true)&&r.hasEncryptionKey,o=e?.streamAutoDetect!==false,s=t?e?.compressionAlgorithm??r.defaultCompressionAlgorithm:void 0,f=new p(n,e),a=[];if(t){let l=s??r.defaultCompressionAlgorithm,u=l==="brotli",S=C(e?.compressionLevel??r.defaultCompressionLevel,l),T=u?createBrotliCompress({params:{[constants.BROTLI_PARAM_QUALITY]:Math.min(11,Math.max(0,S))}}):createDeflate({level:Math.min(9,Math.max(0,S))});a.push(T);}if(i){let l=n.createEncryptionStream();l&&a.push(l);}return a.push(f),o&&a.push(new m(k(r.paddingChar,{compressionAlgorithm:s,encrypted:i}))),a.length===1?f:A(a)}function L(n,e){if(e?.streamAutoDetect??true)return new y(n,e);let t=n.getCharSetInfo(),i=e?.compress??t.defaultCompress,o=(e?.encrypt??true)&&t.hasEncryptionKey,s=new g(n,e),f=[s];if(o){let a=n.createDecryptionStream();a&&f.push(a);}if(i){let u=(e?.compressionAlgorithm??t.defaultCompressionAlgorithm)==="brotli"?createBrotliDecompress():createInflate();f.push(u);}return f.length===1?s:A(f)}export{N as a,P as b,H as c,K as d,b as e,W as f,F as g,j as h,U as i,h as j,J as k,$ as l,C as m,p as n,g as o,Z as p,L as q};//# sourceMappingURL=chunk-FVBZKVPE.js.map
4
+ //# sourceMappingURL=chunk-FVBZKVPE.js.map