cipher-kit 2.0.0-beta.5 → 2.1.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.
- package/README.md +244 -71
- package/dist/{chunk-3FJZA77A.cjs → chunk-BHG5RSUV.cjs} +22 -20
- package/dist/chunk-BHG5RSUV.cjs.map +1 -0
- package/dist/{chunk-7WPZVN7G.js → chunk-CRTOKS3Q.js} +76 -41
- package/dist/chunk-CRTOKS3Q.js.map +1 -0
- package/dist/{chunk-WLLCFK4U.cjs → chunk-HMTHK2IY.cjs} +183 -148
- package/dist/chunk-HMTHK2IY.cjs.map +1 -0
- package/dist/{chunk-BWE6JWHY.js → chunk-RAEBT46G.js} +75 -40
- package/dist/chunk-RAEBT46G.js.map +1 -0
- package/dist/{chunk-YAZRJN6X.js → chunk-RUTGDMVR.js} +21 -20
- package/dist/chunk-RUTGDMVR.js.map +1 -0
- package/dist/{chunk-CEXY7GOU.cjs → chunk-UVEMRK5F.cjs} +189 -154
- package/dist/chunk-UVEMRK5F.cjs.map +1 -0
- package/dist/{export-Du70yDea.d.cts → export-5hmOiU0J.d.cts} +205 -31
- package/dist/{export-DPAoLdh1.d.ts → export-BF9wW56f.d.ts} +205 -31
- package/dist/{export-DjUgZ7dz.d.ts → export-DVERZibl.d.cts} +205 -31
- package/dist/{export-DX7bFv-3.d.cts → export-w8sBcKXw.d.ts} +205 -31
- package/dist/index.cjs +12 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +33 -33
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +2 -2
- package/dist/{validate-DrBddQyu.d.cts → validate-B3uHoP8n.d.cts} +28 -40
- package/dist/{validate-DrBddQyu.d.ts → validate-B3uHoP8n.d.ts} +28 -40
- package/dist/web-api.cjs +33 -33
- package/dist/web-api.d.cts +2 -2
- package/dist/web-api.d.ts +2 -2
- package/dist/web-api.js +2 -2
- package/package.json +7 -7
- package/dist/chunk-3FJZA77A.cjs.map +0 -1
- package/dist/chunk-7WPZVN7G.js.map +0 -1
- package/dist/chunk-BWE6JWHY.js.map +0 -1
- package/dist/chunk-CEXY7GOU.cjs.map +0 -1
- package/dist/chunk-WLLCFK4U.cjs.map +0 -1
- package/dist/chunk-YAZRJN6X.js.map +0 -1
package/README.md
CHANGED
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
<h1 align="center" style="font-weight:900;">cipher-kit</h1>
|
|
5
5
|
|
|
6
|
-
<!-- TODO -->
|
|
7
6
|
<p align="center">
|
|
8
|
-
Secure,
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Secure, Modern, and Cross-Platform <br/>
|
|
8
|
+
Cryptography Helpers for Web, Node.js, <br/>
|
|
9
|
+
Deno, Bun, and Cloudflare Workers
|
|
11
10
|
</p>
|
|
12
11
|
|
|
13
12
|
<a href="https://opensource.org/licenses/MIT" rel="nofollow"><img src="https://img.shields.io/github/license/WolfieLeader/npm?color=DC343B" alt="License"></a>
|
|
14
13
|
<a href="https://www.npmjs.com/package/cipher-kit" rel="nofollow"><img src="https://img.shields.io/npm/v/cipher-kit?color=0078D4" alt="npm version"></a>
|
|
15
|
-
<a href="https://www.npmjs.com/package/cipher-kit" rel="nofollow"><img src="https://img.shields.io/npm/
|
|
14
|
+
<a href="https://www.npmjs.com/package/cipher-kit" rel="nofollow"><img src="https://img.shields.io/npm/dt/cipher-kit.svg?color=03C03C" alt="npm downloads"></a>
|
|
16
15
|
<a href="https://github.com/WolfieLeader/npm" rel="nofollow"><img src="https://img.shields.io/github/stars/WolfieLeader/npm" alt="stars"></a>
|
|
17
16
|
|
|
18
17
|
</div>
|
|
@@ -22,10 +21,10 @@
|
|
|
22
21
|
- 🛡️ **Secure and Flexible** - Uses best practices and modern cryptographic techniques, while providing a flexible and simple API.
|
|
23
22
|
- 📦 **All-in-One Toolkit** – Combines encryption, hashing, encoding, serialization, and more into a single package.
|
|
24
23
|
- 🌐 **Cross-Platform** – Works seamlessly across Web, Node.js, Deno, Bun, and Cloudflare Workers.
|
|
25
|
-
- 💡 **Typed and Ergonomic** - Type
|
|
26
|
-
- 🌳 **Tree-Shakable** -
|
|
24
|
+
- 💡 **Typed and Ergonomic** - Type-safe API with both throwing and non-throwing (`Result`) flavors.
|
|
25
|
+
- 🌳 **Tree-Shakable** - Import from the root or from platform-specific entry points to keep bundles lean.
|
|
27
26
|
- 🚫 **Zero Dependencies** – Fully self-contained, no external libraries required.
|
|
28
|
-
- 🍼 **Explain
|
|
27
|
+
- 🍼 **Explain Like I'm Five** - Newbie-friendly explanations and documentation.
|
|
29
28
|
|
|
30
29
|
## Installation 🔥
|
|
31
30
|
|
|
@@ -39,65 +38,111 @@ pnpm install cipher-kit@latest
|
|
|
39
38
|
bun add cipher-kit@latest
|
|
40
39
|
```
|
|
41
40
|
|
|
42
|
-
##
|
|
43
|
-
|
|
44
|
-
### The `try` Prefix (Non-Throwing `Result` API)
|
|
41
|
+
## Quick Start 🚀
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
```typescript
|
|
44
|
+
// Node.js
|
|
45
|
+
import { createSecretKey, encrypt, decrypt } from "cipher-kit/node";
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
const secretKey = createSecretKey("my-passphrase");
|
|
48
|
+
const encrypted = encrypt("Hello World!", secretKey);
|
|
49
|
+
const decrypted = decrypt(encrypted, secretKey);
|
|
50
|
+
console.log(decrypted); // "Hello World!"
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const message = encrypt('Secret message', secretKey);
|
|
53
|
-
console.log(`Encrypted message: ${message}`);
|
|
52
|
+
// Web - including Deno, Bun, Cloudflare Workers
|
|
53
|
+
import { createSecretKey, encrypt, decrypt } from "cipher-kit/web-api";
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
} else {
|
|
60
|
-
console.error(`Encryption failed: ${message.error.message} - ${message.error.description}`);
|
|
61
|
-
}
|
|
55
|
+
const secretKey = await createSecretKey("my-passphrase");
|
|
56
|
+
const encrypted = await encrypt("Hello World!", secretKey);
|
|
57
|
+
const decrypted = await decrypt(encrypted, secretKey);
|
|
58
|
+
console.log(decrypted); // "Hello World!"
|
|
62
59
|
```
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
## Usage 🪛
|
|
62
|
+
|
|
63
|
+
Table of Contents:
|
|
64
|
+
|
|
65
|
+
- [webKit and nodeKit](#webkit-and-nodekit-objects-)
|
|
66
|
+
- [The try Prefix](#the-try-prefix-non-throwing-result-api-)
|
|
67
|
+
- [Encryption & Decryption](#encryption--decryption-)
|
|
68
|
+
- [Secret Key Creation](#secret-key-creation-)
|
|
69
|
+
- [Encrypting Data](#encrypting-data-)
|
|
70
|
+
- [Decrypting Data](#decrypting-data-)
|
|
71
|
+
- [Encrypting & Decrypting Objects](#encrypting--decrypting-objects-)
|
|
72
|
+
- [Hashing](#hashing-)
|
|
73
|
+
- [UUID Generation](#uuid-generation-)
|
|
74
|
+
- [Password Hashing & Verification](#password-hashing--verification-)
|
|
75
|
+
- [Encoding & Decoding](#encoding--decoding-)
|
|
76
|
+
- [Object Serialization & Deserialization](#object-serialization--deserialization-)
|
|
77
|
+
- [Regex Utilities](#regex-utilities-)
|
|
78
|
+
|
|
79
|
+
### `webKit` and `nodeKit` Objects 📦
|
|
65
80
|
|
|
66
81
|
The `webKit` and `nodeKit` objects provide platform-specific implementations for Web (including Deno, Bun, and Cloudflare Workers) and Node.js environments, respectively.
|
|
67
82
|
|
|
68
83
|
You can also import them directly from `cipher-kit/web-api` and `cipher-kit/node` for smaller bundle sizes.
|
|
69
84
|
|
|
70
85
|
```typescript
|
|
71
|
-
|
|
72
|
-
import { isNodeSecretKey } from
|
|
73
|
-
import { isWebSecretKey } from
|
|
86
|
+
// Option A: import helpers directly
|
|
87
|
+
import { isNodeSecretKey } from "cipher-kit/node";
|
|
88
|
+
import { isWebSecretKey } from "cipher-kit/web-api";
|
|
74
89
|
|
|
75
|
-
// These are the same:
|
|
76
90
|
function isSecretKey(key: unknown): boolean {
|
|
77
91
|
return isNodeSecretKey(key) || isWebSecretKey(key);
|
|
78
92
|
}
|
|
79
93
|
|
|
94
|
+
// Option B: via kits from the root export
|
|
95
|
+
import { webKit, nodeKit } from "cipher-kit";
|
|
96
|
+
|
|
80
97
|
function isSecretKey(key: unknown): boolean {
|
|
81
98
|
return nodeKit.isNodeSecretKey(key) || webKit.isWebSecretKey(key);
|
|
82
99
|
}
|
|
83
100
|
```
|
|
84
101
|
|
|
85
|
-
###
|
|
102
|
+
### The `try` Prefix (Non-Throwing `Result` API) 🤔
|
|
86
103
|
|
|
87
|
-
|
|
104
|
+
The `try` prefix functions return a `Result<T>` object that indicates success or failure without throwing exceptions.
|
|
105
|
+
|
|
106
|
+
This is useful in scenarios where you want to handle errors gracefully without using `try/catch` blocks.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Throwing version - simpler but requires try/catch
|
|
110
|
+
const msg = encrypt("Secret message", secretKey);
|
|
111
|
+
console.log(`Encrypted message: ${msg}`);
|
|
88
112
|
|
|
89
|
-
|
|
113
|
+
// Non-throwing version - returns a Result<T> object
|
|
114
|
+
const msg = tryEncrypt("Secret message", secretKey);
|
|
90
115
|
|
|
91
|
-
|
|
116
|
+
// Either check for success status
|
|
117
|
+
if (msg.success) console.log(`Encrypted message: ${msg.result}`);
|
|
118
|
+
else console.error(`${msg.error.message} - ${msg.error.description}`);
|
|
119
|
+
|
|
120
|
+
// Or Check that there is no error
|
|
121
|
+
if (!msg.error) console.log(`Encrypted message: ${msg.result}`);
|
|
122
|
+
else console.error(`${msg.error.message} - ${msg.error.description}`);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Encryption & Decryption 🤫
|
|
126
|
+
|
|
127
|
+
Encryption is the process of converting readable plaintext into unreadable ciphertext using an algorithm and a secret key to protect its confidentiality. Decryption is the reverse process, using the same algorithm and the correct key to convert the ciphertext back into its original, readable plaintext form.
|
|
128
|
+
|
|
129
|
+
#### _Secret Key Creation_ 🔑
|
|
92
130
|
|
|
93
131
|
Before encrypting or decrypting data, you need to create a secret key.
|
|
132
|
+
The key must be at least 8 characters long.
|
|
94
133
|
|
|
95
134
|
Each key is tied to a specific platform (Web or Node.js) and cannot be used interchangeably.
|
|
96
135
|
|
|
97
136
|
```typescript
|
|
98
|
-
|
|
137
|
+
// Node.js example
|
|
138
|
+
import { createSecretKey } from "cipher-kit/node";
|
|
139
|
+
|
|
140
|
+
const nodeSecretKey = createSecretKey("my-passphrase");
|
|
99
141
|
|
|
100
|
-
|
|
142
|
+
// Web example
|
|
143
|
+
import { createSecretKey } from "cipher-kit/web-api";
|
|
144
|
+
|
|
145
|
+
const webSecretKey = await createSecretKey("my-passphrase");
|
|
101
146
|
```
|
|
102
147
|
|
|
103
148
|
The function accepts an optional `options` as well, which allows you to customize the key derivation process.
|
|
@@ -105,10 +150,10 @@ The function accepts an optional `options` as well, which allows you to customiz
|
|
|
105
150
|
```typescript
|
|
106
151
|
interface CreateSecretKeyOptions {
|
|
107
152
|
// Which encryption algorithm to use (default: "aes256gcm")
|
|
108
|
-
algorithm?:
|
|
153
|
+
algorithm?: "aes256gcm" | "aes192gcm" | "aes128gcm";
|
|
109
154
|
|
|
110
155
|
// Digest algorithm for HKDF (key derivation) (default: "sha256")
|
|
111
|
-
digest?:
|
|
156
|
+
digest?: "sha256" | "sha384" | "sha512";
|
|
112
157
|
|
|
113
158
|
// Optional salt for HKDF (key derivation), if you provide a random one it will return a different key each time (default: "cipher-kit-salt", must be >= 8 characters).
|
|
114
159
|
salt?: string;
|
|
@@ -118,12 +163,24 @@ interface CreateSecretKeyOptions {
|
|
|
118
163
|
}
|
|
119
164
|
```
|
|
120
165
|
|
|
121
|
-
####
|
|
166
|
+
#### _Encrypting Data_ 🔐
|
|
167
|
+
|
|
168
|
+
Ciphertext formats differ between Web and Node.js platforms.
|
|
169
|
+
|
|
170
|
+
- Node.js - `iv.cipher.tag.` (3 parts)
|
|
171
|
+
- Web - `iv.cipherWithTag.` (2 parts)
|
|
122
172
|
|
|
123
173
|
```typescript
|
|
124
|
-
|
|
174
|
+
// Node.js example
|
|
175
|
+
import { encrypt } from "cipher-kit/node";
|
|
176
|
+
|
|
177
|
+
const encrypted = encrypt("Hello, World!", nodeSecretKey);
|
|
178
|
+
console.log(`Encrypted: ${encrypted}`);
|
|
179
|
+
|
|
180
|
+
// Web example
|
|
181
|
+
import { encrypt } from "cipher-kit/web-api";
|
|
125
182
|
|
|
126
|
-
const encrypted = encrypt(
|
|
183
|
+
const encrypted = await encrypt("Hello, World!", webSecretKey);
|
|
127
184
|
console.log(`Encrypted: ${encrypted}`);
|
|
128
185
|
```
|
|
129
186
|
|
|
@@ -132,16 +189,25 @@ The function accepts an optional `options` parameter to customize the output enc
|
|
|
132
189
|
```typescript
|
|
133
190
|
interface EncryptOptions {
|
|
134
191
|
// Output ciphertext encoding(default: "base64url")
|
|
135
|
-
|
|
192
|
+
outputEncoding?: "base64url" | "base64" | "hex";
|
|
136
193
|
}
|
|
137
194
|
```
|
|
138
195
|
|
|
139
|
-
####
|
|
196
|
+
#### _Decrypting Data_ 🔓
|
|
197
|
+
|
|
198
|
+
Since ciphertext formats differ between Web and Node.js platforms, make sure to use the correct `decrypt` function for the platform where the data was encrypted.
|
|
140
199
|
|
|
141
200
|
```typescript
|
|
142
|
-
|
|
201
|
+
// Node.js example
|
|
202
|
+
import { decrypt } from "cipher-kit/node";
|
|
143
203
|
|
|
144
|
-
const decrypted = decrypt(encrypted,
|
|
204
|
+
const decrypted = decrypt(encrypted, nodeSecretKey);
|
|
205
|
+
console.log(`Decrypted: ${decrypted}`);
|
|
206
|
+
|
|
207
|
+
// Web example
|
|
208
|
+
import { decrypt } from "cipher-kit/web-api";
|
|
209
|
+
|
|
210
|
+
const decrypted = await decrypt(encrypted, webSecretKey);
|
|
145
211
|
console.log(`Decrypted: ${decrypted}`);
|
|
146
212
|
```
|
|
147
213
|
|
|
@@ -152,18 +218,53 @@ Make sure to use the same encoding that was used during encryption.
|
|
|
152
218
|
```typescript
|
|
153
219
|
interface DecryptOptions {
|
|
154
220
|
// Input ciphertext encoding (default: "base64url")
|
|
155
|
-
|
|
221
|
+
inputEncoding?: "base64url" | "base64" | "hex";
|
|
156
222
|
}
|
|
157
223
|
```
|
|
158
224
|
|
|
159
|
-
|
|
225
|
+
#### _Encrypting & Decrypting Objects_ 🧬
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// Node.js example
|
|
229
|
+
import { encryptObj, decryptObj } from "cipher-kit/node";
|
|
230
|
+
|
|
231
|
+
const obj = { name: "Alice", age: 30, city: "Wonderland" };
|
|
232
|
+
|
|
233
|
+
const encryptedObj = encryptObj(obj, nodeSecretKey);
|
|
234
|
+
|
|
235
|
+
const decryptedObj = decryptObj<typeof obj>(encryptedObj, nodeSecretKey);
|
|
236
|
+
console.log(`Decrypted Object:`, decryptedObj);
|
|
237
|
+
|
|
238
|
+
// Web example
|
|
239
|
+
import { encryptObj, decryptObj } from "cipher-kit/web-api";
|
|
240
|
+
|
|
241
|
+
const obj = { name: "Alice", age: 30, city: "Wonderland" };
|
|
242
|
+
|
|
243
|
+
const encryptedObj = await encryptObj(obj, webSecretKey);
|
|
244
|
+
|
|
245
|
+
const decryptedObj = await decryptObj<typeof obj>(encryptedObj, webSecretKey);
|
|
246
|
+
console.log(`Decrypted Object:`, decryptedObj);
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The `encryptObj` and `decryptObj` functions accept the same `options` parameters as `encrypt` and `decrypt`, respectively.
|
|
250
|
+
|
|
251
|
+
### Hashing 🪄
|
|
160
252
|
|
|
161
253
|
Hashing is a one-way process that uses an algorithm to transform data of any size into a fixed-length string of characters, called a hash value or digest. It serves as a digital fingerprint for the data, enabling quick data retrieval in hash tables, password storage, and file integrity checks. Key features include its irreversibility (you can't get the original data back from the hash).
|
|
162
254
|
|
|
255
|
+
Not suitable for storing passwords - use `hashPassword` instead.
|
|
256
|
+
|
|
163
257
|
```typescript
|
|
164
|
-
|
|
258
|
+
// Node.js example
|
|
259
|
+
import { hash } from "cipher-kit/node";
|
|
260
|
+
|
|
261
|
+
const hashed = hash("Hello, World!");
|
|
262
|
+
console.log(`Hashed: ${hashed}`);
|
|
165
263
|
|
|
166
|
-
|
|
264
|
+
// Web example
|
|
265
|
+
import { hash } from "cipher-kit/web-api";
|
|
266
|
+
|
|
267
|
+
const hashed = await hash("Hello, World!");
|
|
167
268
|
console.log(`Hashed: ${hashed}`);
|
|
168
269
|
```
|
|
169
270
|
|
|
@@ -172,40 +273,56 @@ The function accepts an optional `options` parameter to customize the hashing pr
|
|
|
172
273
|
```typescript
|
|
173
274
|
interface HashOptions {
|
|
174
275
|
// Digest algorithm to use (default: "sha256").
|
|
175
|
-
digest?:
|
|
276
|
+
digest?: "sha256" | "sha384" | "sha512";
|
|
176
277
|
|
|
177
278
|
// Output encoding (default: "base64url").
|
|
178
|
-
|
|
279
|
+
outputEncoding?: "base64url" | "base64" | "hex";
|
|
179
280
|
}
|
|
180
281
|
```
|
|
181
282
|
|
|
182
|
-
### UUID Generation
|
|
283
|
+
### UUID Generation 🪪
|
|
183
284
|
|
|
184
285
|
UUID (Universally Unique Identifier) is a 128-bit identifier used to uniquely identify information in computer systems. It is designed to be globally unique, meaning that no two UUIDs should be the same, even if generated on different systems or at different times. UUIDs are commonly used in databases, distributed systems, and applications where unique identification is crucial.
|
|
185
286
|
|
|
186
287
|
```typescript
|
|
187
|
-
|
|
288
|
+
// Node.js example
|
|
289
|
+
import { generateUuid } from "cipher-kit/node";
|
|
290
|
+
|
|
291
|
+
const uuid = generateUuid();
|
|
292
|
+
console.log(`Generated UUID: ${uuid}`);
|
|
188
293
|
|
|
189
|
-
|
|
294
|
+
// Web example
|
|
295
|
+
import { generateUuid } from "cipher-kit/web-api";
|
|
296
|
+
|
|
297
|
+
const uuid = generateUuid();
|
|
190
298
|
console.log(`Generated UUID: ${uuid}`);
|
|
191
299
|
```
|
|
192
300
|
|
|
193
|
-
### Password Hashing
|
|
301
|
+
### Password Hashing & Verification 💎
|
|
194
302
|
|
|
195
303
|
Password hashing is a one-way process that transforms a plaintext password into a fixed-length hash. Password hashing is crucial for securely storing passwords in databases, as it protects user credentials from being exposed in case of a data breach.
|
|
196
304
|
|
|
197
305
|
Password hashing is different from general-purpose hashing because it often involves additional techniques like salting and key stretching to enhance security against brute-force attacks, and it's usually slower to compute to make rainbow table attacks less feasible.
|
|
198
306
|
|
|
199
|
-
To verify a password, the same hashing process is applied to the input password, and the resulting hash is compared to the stored hash, in a time
|
|
307
|
+
To verify a password, the same hashing process is applied to the input password, and the resulting hash is compared to the stored hash, in a constant-time comparison to prevent timing attacks.
|
|
200
308
|
|
|
201
309
|
```typescript
|
|
202
|
-
|
|
310
|
+
// Node.js example
|
|
311
|
+
import { hashPassword, verifyPassword } from "cipher-kit/node";
|
|
312
|
+
|
|
313
|
+
const { result, salt } = hashPassword("some-secure-password");
|
|
314
|
+
console.log(`Hashed Password: ${result}`);
|
|
315
|
+
|
|
316
|
+
const isMatch = verifyPassword("some-secure-password", result, salt);
|
|
317
|
+
console.log(`Password match: ${isMatch}`);
|
|
318
|
+
|
|
319
|
+
// Web example
|
|
320
|
+
import { hashPassword, verifyPassword } from "cipher-kit/web-api";
|
|
203
321
|
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
console.log(`Hashed Password: ${hashedPassword}`);
|
|
322
|
+
const { result, salt } = await hashPassword("some-secure-password");
|
|
323
|
+
console.log(`Hashed Password: ${result}`);
|
|
207
324
|
|
|
208
|
-
const isMatch = verifyPassword(password,
|
|
325
|
+
const isMatch = await verifyPassword("some-secure-password", result, salt);
|
|
209
326
|
console.log(`Password match: ${isMatch}`);
|
|
210
327
|
```
|
|
211
328
|
|
|
@@ -214,10 +331,10 @@ The `hashPassword` and `verifyPassword` functions accept an optional `options` p
|
|
|
214
331
|
```typescript
|
|
215
332
|
interface HashPasswordOptions {
|
|
216
333
|
// Digest algorithm to use (default: "sha512").
|
|
217
|
-
digest?:
|
|
334
|
+
digest?: "sha256" | "sha384" | "sha512";
|
|
218
335
|
|
|
219
336
|
// Encoding format for the output hash (default: "base64url").
|
|
220
|
-
|
|
337
|
+
outputEncoding?: "base64url" | "base64" | "hex";
|
|
221
338
|
|
|
222
339
|
// Length of the salt in bytes (default: 16 bytes, min: 8 bytes).
|
|
223
340
|
saltLength?: number;
|
|
@@ -231,10 +348,10 @@ interface HashPasswordOptions {
|
|
|
231
348
|
|
|
232
349
|
interface VerifyPasswordOptions {
|
|
233
350
|
// Digest algorithm used during the original hashing (default: `'sha512'`).
|
|
234
|
-
digest?:
|
|
351
|
+
digest?: "sha256" | "sha384" | "sha512";
|
|
235
352
|
|
|
236
353
|
// Encoding format used during the original hashing (default: `'base64url'`).
|
|
237
|
-
|
|
354
|
+
inputEncoding?: "base64url" | "base64" | "hex";
|
|
238
355
|
|
|
239
356
|
// Number of iterations used during the original hashing (default: `320000`).
|
|
240
357
|
iterations?: number;
|
|
@@ -244,18 +361,74 @@ interface VerifyPasswordOptions {
|
|
|
244
361
|
}
|
|
245
362
|
```
|
|
246
363
|
|
|
247
|
-
|
|
364
|
+
### Encoding & Decoding 🧩
|
|
365
|
+
|
|
366
|
+
Encoding and decoding are processes used to convert data into a specific format for efficient transmission, storage, or representation. Encoding transforms data into a different format using a specific scheme, while decoding reverses this process to retrieve the original data. Common encoding schemes include Base64, Base64URL, and Hexadecimal (Hex).
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// Node.js and Web example - works the same in both
|
|
370
|
+
import { convertStrToBytes, convertBytesToStr, convertEncoding } from "cipher-kit/node"; // or "cipher-kit/web-api"
|
|
371
|
+
|
|
372
|
+
// Encoding
|
|
373
|
+
const buffer = convertStrToBytes("Hello World!", "utf8"); // the input encoding
|
|
374
|
+
console.log(`Encoded: ${buffer}`);
|
|
375
|
+
|
|
376
|
+
// Decoding
|
|
377
|
+
const str = convertBytesToStr(buffer, "utf8"); // the output encoding
|
|
378
|
+
console.log(`Decoded: ${str}`);
|
|
379
|
+
|
|
380
|
+
// Convert between encodings
|
|
381
|
+
const base64 = convertEncoding("Hello World!", "utf8", "base64");
|
|
382
|
+
console.log(`Base64: ${base64}`);
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Object Serialization & Deserialization 🧬
|
|
386
|
+
|
|
387
|
+
Object serialization in JavaScript is the process of converting objects or arrays into a JSON string representation, that can be easily stored or transmitted. Deserialization is the reverse process, where the JSON string is parsed back into its original object or array structure.
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
import { stringifyObj, parseToObj } from "cipher-kit"; // works in both "cipher-kit/web-api" and "cipher-kit/node"
|
|
391
|
+
|
|
392
|
+
const obj = { name: "Alice", age: 30, city: "Wonderland" };
|
|
393
|
+
|
|
394
|
+
const jsonString = stringifyObj(obj);
|
|
395
|
+
console.log(`Serialized: ${jsonString}`);
|
|
396
|
+
|
|
397
|
+
const parsedObj = parseToObj<typeof obj>(jsonString);
|
|
398
|
+
console.log(`Deserialized:`, parsedObj);
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Regex Utilities 🔍
|
|
402
|
+
|
|
403
|
+
Regular expressions (regex) are sequences of characters that form search patterns, used for pattern matching within strings.
|
|
404
|
+
|
|
405
|
+
Before decrypting, you can validate the format (decryption functions already validate internally).
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
import { ENCRYPTED_REGEX, matchEncryptedPattern } from "cipher-kit"; // works in both "cipher-kit/web-api" and "cipher-kit/node"
|
|
409
|
+
|
|
410
|
+
function isEncryptedFormat(message: string): boolean {
|
|
411
|
+
return matchEncryptedPattern(message, "general"); // or "node" or "web"
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// or
|
|
415
|
+
|
|
416
|
+
function isEncryptedFormat(message: string): boolean {
|
|
417
|
+
return ENCRYPTED_REGEX.general.test(message); // or "node" or "web"
|
|
418
|
+
}
|
|
419
|
+
```
|
|
248
420
|
|
|
249
421
|
## Contributions 🤝
|
|
250
422
|
|
|
251
|
-
Want to contribute or suggest a feature?
|
|
423
|
+
Want to contribute or suggest a feature or improvement?
|
|
252
424
|
|
|
253
425
|
- Open an issue or feature request
|
|
254
426
|
- Submit a PR to improve the packages or add new ones
|
|
255
427
|
- Star ⭐ the repo if you like what you see
|
|
256
428
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
Thank you
|
|
429
|
+
<div align="center">
|
|
430
|
+
<br/>
|
|
431
|
+
<div style="font-size: 14px; font-weight:bold;"> ⚒️ Crafted carefully by <a href="https://github.com/WolfieLeader" target="_blank" rel="nofollow">WolfieLeader</a></div>
|
|
432
|
+
<p style="font-size: 12px; font-style: italic;">This project is licensed under the <a href="https://opensource.org/licenses/MIT" target="_blank" rel="nofollow">MIT License</a>.</p>
|
|
433
|
+
<div style="font-size: 12px; font-style: italic; font-weight: 600;">Thank you!</div>
|
|
434
|
+
</div>
|
|
@@ -30,22 +30,22 @@ var ENCRYPTION_ALGORITHMS = Object.freeze({
|
|
|
30
30
|
function $isStr(x, min = 1) {
|
|
31
31
|
return x !== null && x !== void 0 && typeof x === "string" && x.trim().length >= min;
|
|
32
32
|
}
|
|
33
|
-
function $
|
|
33
|
+
function $isPlainObj(x) {
|
|
34
34
|
if (typeof x !== "object" || x === null || x === void 0) return false;
|
|
35
35
|
const proto = Object.getPrototypeOf(x);
|
|
36
36
|
return proto === Object.prototype || proto === null;
|
|
37
37
|
}
|
|
38
|
-
function $
|
|
38
|
+
function $isObj(x) {
|
|
39
39
|
return typeof x === "object" && x !== null && x !== void 0;
|
|
40
40
|
}
|
|
41
41
|
var expectedKeys = /* @__PURE__ */ new Set(["platform", "digest", "algorithm", "key"]);
|
|
42
42
|
function $isSecretKey(x, platform) {
|
|
43
|
-
if (!$
|
|
43
|
+
if (!$isObj(x) || platform !== "node" && platform !== "web" || x.platform !== platform) return null;
|
|
44
44
|
const keys = Object.keys(x);
|
|
45
45
|
if (keys.length !== expectedKeys.size) return null;
|
|
46
46
|
for (const key of keys) if (!expectedKeys.has(key)) return null;
|
|
47
47
|
for (const key of expectedKeys) if (!Object.hasOwn(x, key)) return null;
|
|
48
|
-
if (typeof x.digest !== "string" || !(x.digest in DIGEST_ALGORITHMS) || typeof x.algorithm !== "string" || !(x.algorithm in ENCRYPTION_ALGORITHMS) || !$
|
|
48
|
+
if (typeof x.digest !== "string" || !(x.digest in DIGEST_ALGORITHMS) || typeof x.algorithm !== "string" || !(x.algorithm in ENCRYPTION_ALGORITHMS) || !$isObj(x.key) || x.key.type !== "secret") {
|
|
49
49
|
return null;
|
|
50
50
|
}
|
|
51
51
|
const algorithm = ENCRYPTION_ALGORITHMS[x.algorithm];
|
|
@@ -55,7 +55,7 @@ function $isSecretKey(x, platform) {
|
|
|
55
55
|
}
|
|
56
56
|
return Object.freeze({ ...x, injected: algorithm });
|
|
57
57
|
}
|
|
58
|
-
if (!$
|
|
58
|
+
if (!$isObj(x.key.algorithm) || x.key.algorithm.name !== algorithm.web || typeof x.key.algorithm.length === "number" && x.key.algorithm.length !== algorithm.keyBytes * 8 || typeof x.key.extractable !== "boolean" || !Array.isArray(x.key.usages) || x.key.usages.length !== 2 || !(x.key.usages.includes("encrypt") && x.key.usages.includes("decrypt"))) {
|
|
59
59
|
return null;
|
|
60
60
|
}
|
|
61
61
|
return Object.freeze({ ...x, injected: algorithm });
|
|
@@ -65,7 +65,7 @@ var ENCRYPTED_REGEX = Object.freeze({
|
|
|
65
65
|
web: /^([^.]+)\.([^.]+)\.$/,
|
|
66
66
|
general: /^([^.]+)\.([^.]+)(?:\.([^.]+))?\.$/
|
|
67
67
|
});
|
|
68
|
-
function
|
|
68
|
+
function matchEncryptedPattern(data, format) {
|
|
69
69
|
if (typeof data !== "string") return false;
|
|
70
70
|
if (!(format in ENCRYPTED_REGEX)) throw new Error(`Unknown format: ${format}`);
|
|
71
71
|
return ENCRYPTED_REGEX[format].test(data);
|
|
@@ -73,7 +73,7 @@ function matchPattern(data, format) {
|
|
|
73
73
|
|
|
74
74
|
// src/helpers/error.ts
|
|
75
75
|
function $ok(result) {
|
|
76
|
-
if ($
|
|
76
|
+
if ($isPlainObj(result)) return { success: true, ...result };
|
|
77
77
|
return { success: true, result };
|
|
78
78
|
}
|
|
79
79
|
function $err(err) {
|
|
@@ -101,30 +101,31 @@ function title(platform, title2) {
|
|
|
101
101
|
// src/helpers/object.ts
|
|
102
102
|
function $stringifyObj(obj) {
|
|
103
103
|
try {
|
|
104
|
-
if (!$
|
|
104
|
+
if (!$isPlainObj(obj)) return $err({ msg: "Invalid object", desc: "Input is not a plain object" });
|
|
105
105
|
return $ok(JSON.stringify(obj));
|
|
106
106
|
} catch (error) {
|
|
107
107
|
return $err({ msg: "Utility: Stringify error", desc: $fmtError(error) });
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
function tryStringifyObj(obj) {
|
|
111
|
-
return $stringifyObj(obj);
|
|
112
|
-
}
|
|
113
|
-
function stringifyObj(obj) {
|
|
114
|
-
const { result, error } = $stringifyObj(obj);
|
|
115
|
-
if (error) throw new Error($fmtResultErr(error));
|
|
116
|
-
return result;
|
|
117
|
-
}
|
|
118
110
|
function $parseToObj(str) {
|
|
119
111
|
try {
|
|
120
112
|
if (!$isStr(str)) return $err({ msg: "Utility: Invalid input", desc: "Input is not a valid string" });
|
|
121
113
|
const obj = JSON.parse(str);
|
|
122
|
-
if (!$
|
|
114
|
+
if (!$isPlainObj(obj))
|
|
115
|
+
return $err({ msg: "Utility: Invalid object format", desc: "Parsed data is not a plain object" });
|
|
123
116
|
return $ok({ result: obj });
|
|
124
117
|
} catch (error) {
|
|
125
118
|
return $err({ msg: "Utility: Invalid format", desc: $fmtError(error) });
|
|
126
119
|
}
|
|
127
120
|
}
|
|
121
|
+
function tryStringifyObj(obj) {
|
|
122
|
+
return $stringifyObj(obj);
|
|
123
|
+
}
|
|
124
|
+
function stringifyObj(obj) {
|
|
125
|
+
const { result, error } = $stringifyObj(obj);
|
|
126
|
+
if (error) throw new Error($fmtResultErr(error));
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
128
129
|
function tryParseToObj(str) {
|
|
129
130
|
return $parseToObj(str);
|
|
130
131
|
}
|
|
@@ -137,6 +138,7 @@ function parseToObj(str) {
|
|
|
137
138
|
exports.$err = $err;
|
|
138
139
|
exports.$fmtError = $fmtError;
|
|
139
140
|
exports.$fmtResultErr = $fmtResultErr;
|
|
141
|
+
exports.$isPlainObj = $isPlainObj;
|
|
140
142
|
exports.$isSecretKey = $isSecretKey;
|
|
141
143
|
exports.$isStr = $isStr;
|
|
142
144
|
exports.$ok = $ok;
|
|
@@ -148,11 +150,11 @@ exports.ENCODING = ENCODING;
|
|
|
148
150
|
exports.ENCRYPTED_REGEX = ENCRYPTED_REGEX;
|
|
149
151
|
exports.ENCRYPTION_ALGORITHMS = ENCRYPTION_ALGORITHMS;
|
|
150
152
|
exports.__export = __export;
|
|
151
|
-
exports.
|
|
153
|
+
exports.matchEncryptedPattern = matchEncryptedPattern;
|
|
152
154
|
exports.parseToObj = parseToObj;
|
|
153
155
|
exports.stringifyObj = stringifyObj;
|
|
154
156
|
exports.title = title;
|
|
155
157
|
exports.tryParseToObj = tryParseToObj;
|
|
156
158
|
exports.tryStringifyObj = tryStringifyObj;
|
|
157
|
-
//# sourceMappingURL=chunk-
|
|
158
|
-
//# sourceMappingURL=chunk-
|
|
159
|
+
//# sourceMappingURL=chunk-BHG5RSUV.cjs.map
|
|
160
|
+
//# sourceMappingURL=chunk-BHG5RSUV.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/helpers/consts.ts","../src/helpers/validate.ts","../src/helpers/error.ts","../src/helpers/object.ts"],"names":["nodeCrypto","title"],"mappings":"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","file":"chunk-BHG5RSUV.cjs","sourcesContent":["export const ENCODING = Object.freeze([\"base64\", \"base64url\", \"hex\", \"utf8\", \"latin1\"] as const);\r\n\r\nexport const CIPHER_ENCODING = Object.freeze([\"base64\", \"base64url\", \"hex\"] as const);\r\n\r\nexport const DIGEST_ALGORITHMS = Object.freeze({\r\n sha256: { node: \"sha256\", web: \"SHA-256\" },\r\n sha384: { node: \"sha384\", web: \"SHA-384\" },\r\n sha512: { node: \"sha512\", web: \"SHA-512\" },\r\n} as const);\r\n\r\nexport const ENCRYPTION_ALGORITHMS = Object.freeze({\r\n aes256gcm: { keyBytes: 32, ivLength: 12, node: \"aes-256-gcm\", web: \"AES-GCM\" },\r\n aes192gcm: { keyBytes: 24, ivLength: 12, node: \"aes-192-gcm\", web: \"AES-GCM\" },\r\n aes128gcm: { keyBytes: 16, ivLength: 12, node: \"aes-128-gcm\", web: \"AES-GCM\" },\r\n} as const);\r\n","import nodeCrypto from \"node:crypto\";\r\nimport type { SecretKey } from \"~/helpers/types\";\r\nimport { DIGEST_ALGORITHMS, ENCRYPTION_ALGORITHMS } from \"./consts\";\r\n\r\nexport function $isStr(x: unknown, min = 1): x is string {\r\n return x !== null && x !== undefined && typeof x === \"string\" && x.trim().length >= min;\r\n}\r\n\r\nexport function $isPlainObj<T extends object = Record<string, unknown>>(x: unknown): x is T {\r\n if (typeof x !== \"object\" || x === null || x === undefined) return false;\r\n const proto = Object.getPrototypeOf(x);\r\n return proto === Object.prototype || proto === null;\r\n}\r\n\r\nexport function $isObj(x: unknown): x is Record<string, unknown> {\r\n return typeof x === \"object\" && x !== null && x !== undefined;\r\n}\r\n\r\ntype InjectedSecretKey<Platform extends \"web\" | \"node\"> = SecretKey<Platform> & {\r\n readonly injected: (typeof ENCRYPTION_ALGORITHMS)[keyof typeof ENCRYPTION_ALGORITHMS];\r\n};\r\n\r\nconst expectedKeys = new Set([\"platform\", \"digest\", \"algorithm\", \"key\"]);\r\n\r\nexport function $isSecretKey<Platform extends \"node\" | \"web\">(\r\n x: unknown,\r\n platform: Platform,\r\n): InjectedSecretKey<Platform> | null {\r\n if (!$isObj(x) || (platform !== \"node\" && platform !== \"web\") || x.platform !== platform) return null;\r\n\r\n const keys = Object.keys(x);\r\n if (keys.length !== expectedKeys.size) return null;\r\n for (const key of keys) if (!expectedKeys.has(key)) return null;\r\n for (const key of expectedKeys) if (!Object.hasOwn(x, key)) return null;\r\n\r\n if (\r\n typeof x.digest !== \"string\" ||\r\n !(x.digest in DIGEST_ALGORITHMS) ||\r\n typeof x.algorithm !== \"string\" ||\r\n !(x.algorithm in ENCRYPTION_ALGORITHMS) ||\r\n !$isObj(x.key) ||\r\n x.key.type !== \"secret\"\r\n ) {\r\n return null;\r\n }\r\n\r\n const algorithm = ENCRYPTION_ALGORITHMS[x.algorithm as keyof typeof ENCRYPTION_ALGORITHMS];\r\n\r\n if (platform === \"node\") {\r\n if (\r\n !(x.key instanceof nodeCrypto.KeyObject) ||\r\n (typeof x.key.symmetricKeySize === \"number\" && x.key.symmetricKeySize !== algorithm.keyBytes)\r\n ) {\r\n return null;\r\n }\r\n return Object.freeze({ ...x, injected: algorithm }) as InjectedSecretKey<Platform>;\r\n }\r\n\r\n if (\r\n !$isObj(x.key.algorithm) ||\r\n x.key.algorithm.name !== algorithm.web ||\r\n (typeof x.key.algorithm.length === \"number\" && x.key.algorithm.length !== algorithm.keyBytes * 8) ||\r\n typeof x.key.extractable !== \"boolean\" ||\r\n !Array.isArray(x.key.usages) ||\r\n x.key.usages.length !== 2 ||\r\n !(x.key.usages.includes(\"encrypt\") && x.key.usages.includes(\"decrypt\"))\r\n ) {\r\n return null;\r\n }\r\n return Object.freeze({ ...x, injected: algorithm }) as InjectedSecretKey<Platform>;\r\n}\r\n\r\n/**\r\n * Regular expressions for encrypted data patterns.\r\n *\r\n * - **node**: `\"iv.cipher.tag.\"` (three dot-separated parts plus a trailing dot)\r\n * - **web**: `\"iv.cipherWithTag.\"` (two parts plus a trailing dot)\r\n * - **general**: accepts both shapes (2 or 3 parts) with a trailing dot\r\n *\r\n * Each part is any non-empty string without dots.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You have a secret code you want to check. Before you check it,\r\n * you make sure it looks how a secret code should look.\r\n */\r\nexport const ENCRYPTED_REGEX = Object.freeze({\r\n node: /^([^.]+)\\.([^.]+)\\.([^.]+)\\.$/,\r\n web: /^([^.]+)\\.([^.]+)\\.$/,\r\n general: /^([^.]+)\\.([^.]+)(?:\\.([^.]+))?\\.$/,\r\n});\r\n\r\n/**\r\n * Checks if a string matches an expected encrypted payload shape.\r\n *\r\n * - **node**: `\"iv.cipher.tag.\"` (three dot-separated parts plus a trailing dot)\r\n * - **web**: `\"iv.cipherWithTag.\"` (two parts plus a trailing dot)\r\n * - **general**: accepts both shapes (2 or 3 parts) with a trailing dot\r\n *\r\n * Each part is any non-empty string without dots.\r\n *\r\n * This validates only the **shape**, not whether content is valid base64/hex, etc.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You have a secret code you want to check. Before you check it,\r\n * you make sure it looks how a secret code should look.\r\n *\r\n * @param data - The string to test.\r\n * @param format - Which layout to check: `'node'`, `'web'`, or `'general'`.\r\n * @returns `true` if the string matches the pattern; otherwise `false`.\r\n * @throws {Error} If an unknown `format` is provided.\r\n *\r\n * @example\r\n * ```ts\r\n * matchEncryptedPattern(\"abc.def.ghi.\", \"node\"); // true\r\n * matchEncryptedPattern(\"abc.def.\", \"web\"); // true\r\n * matchEncryptedPattern(\"abc.def.\", \"node\"); // false\r\n * matchEncryptedPattern(\"abc.def.ghi.\", \"general\"); // true\r\n * ```\r\n */\r\nexport function matchEncryptedPattern(data: string, format: \"general\" | \"node\" | \"web\"): boolean {\r\n if (typeof data !== \"string\") return false;\r\n if (!(format in ENCRYPTED_REGEX)) throw new Error(`Unknown format: ${format}`);\r\n return ENCRYPTED_REGEX[format].test(data);\r\n}\r\n","import { $isPlainObj } from \"./validate\";\r\n\r\n/**\r\n * Standardized error object for the `Result` type.\r\n * Always has a brief `message` and a more detailed `description`.\r\n */\r\nexport interface ResultErr {\r\n readonly message: string;\r\n readonly description: string;\r\n}\r\n\r\n/**\r\n * Discriminated union for functions that can succeed or fail.\r\n *\r\n * - On **success**:\r\n * - If `T` is an object - properties of `T` are **spread** into the result\r\n * along with `success: true`.\r\n * - Otherwise - `{ success: true, result: T }`.\r\n * - On **failure**: `{ success: false, error: E }`.\r\n *\r\n * @example\r\n * ```ts\r\n * // Primitive result\r\n * function getNum(): Result<number> {\r\n * return $ok(42);\r\n * }\r\n * const r1 = getNum();\r\n * if (r1.success) console.log(r1.result); // 42\r\n *\r\n * // Object result (spread)\r\n * function getObject(): Result<{ name: string; age: number }> {\r\n * return $ok({ name: 'Alice', age: 30 });\r\n * }\r\n * const r2 = getObject();\r\n * if (r2.success) console.log(r2.name, r2.age); // 'Alice' 30\r\n * ```\r\n */\r\nexport type Result<T, E = ResultErr> = T extends object\r\n ?\r\n | ({ readonly [K in keyof T]: T[K] } & { readonly success: true; readonly error?: undefined })\r\n | ({ readonly [K in keyof T]?: undefined } & { readonly success: false; readonly error: E })\r\n :\r\n | { readonly success: true; readonly result: T; readonly error?: undefined }\r\n | { readonly success: false; readonly error: E; readonly result?: undefined };\r\n\r\nexport function $ok<T>(result?: T): Result<T> {\r\n if ($isPlainObj(result)) return { success: true, ...(result as T & object) } as Result<T>;\r\n return { success: true, result } as Result<T>;\r\n}\r\n\r\nexport function $err(err: { msg: string; desc: string }): Result<never, ResultErr>;\r\nexport function $err(err: ResultErr): Result<never, ResultErr>;\r\nexport function $err(err: { msg: string; desc: string } | ResultErr): Result<never, ResultErr> {\r\n return {\r\n success: false,\r\n error: {\r\n message: \"msg\" in err ? err.msg : err.message,\r\n description: \"desc\" in err ? err.desc : err.description,\r\n },\r\n } as Result<never, ResultErr>;\r\n}\r\n\r\nexport function $fmtError(error: unknown): string {\r\n if (typeof error === \"string\") return error;\r\n if (error instanceof Error) return error.message;\r\n return String(error);\r\n}\r\n\r\nexport function $fmtResultErr(err: ResultErr | undefined): string {\r\n if (!err) return \"Unknown error\";\r\n return `${err.message} - ${err.description}`;\r\n}\r\n\r\nexport function title(platform: \"web\" | \"node\", title: string): string {\r\n return `${platform === \"web\" ? \"Crypto Web API\" : \"Crypto NodeJS API\"} - ${title}`;\r\n}\r\n","import { $err, $fmtError, $fmtResultErr, $ok, type Result } from \"./error\";\r\nimport { $isPlainObj, $isStr } from \"./validate\";\r\n\r\nexport function $stringifyObj<T extends object = Record<string, unknown>>(obj: T): Result<string> {\r\n try {\r\n if (!$isPlainObj(obj)) return $err({ msg: \"Invalid object\", desc: \"Input is not a plain object\" });\r\n return $ok(JSON.stringify(obj));\r\n } catch (error) {\r\n return $err({ msg: \"Utility: Stringify error\", desc: $fmtError(error) });\r\n }\r\n}\r\n\r\nexport function $parseToObj<T extends object = Record<string, unknown>>(str: string): Result<{ result: T }> {\r\n try {\r\n if (!$isStr(str)) return $err({ msg: \"Utility: Invalid input\", desc: \"Input is not a valid string\" });\r\n const obj = JSON.parse(str);\r\n\r\n if (!$isPlainObj(obj))\r\n return $err({ msg: \"Utility: Invalid object format\", desc: \"Parsed data is not a plain object\" });\r\n return $ok({ result: obj as T });\r\n } catch (error) {\r\n return $err({ msg: \"Utility: Invalid format\", desc: $fmtError(error) });\r\n }\r\n}\r\n\r\n/**\r\n * Safely serializes a plain object to JSON without throwing.\r\n *\r\n * Wraps `JSON.stringify` and returns a `Result` containing the JSON string or an error.\r\n * Only plain objects (POJOs) are accepted. Class instances, Maps, Sets, etc. are rejected.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You have a box of toys (your object) and take a photo of it (a JSON string)\r\n * so you can send it to a friend.\r\n *\r\n * @template T - Plain object type to serialize.\r\n * @param obj - The object to stringify (must be a plain object).\r\n * @returns A `Result` with the JSON string on success, or an error.\r\n *\r\n * @example\r\n * ```ts\r\n * const { result, error, success } = tryStringifyObj({ a: 1 });\r\n *\r\n * if (success) console.log(result); // \"{\\\"a\\\":1}\"\r\n * else console.error(error); // { message: \"...\", description: \"...\" }\r\n * ```\r\n */\r\nexport function tryStringifyObj<T extends object = Record<string, unknown>>(obj: T): Result<string> {\r\n return $stringifyObj(obj);\r\n}\r\n\r\n/**\r\n * Serializes a plain object to JSON (throwing).\r\n *\r\n * Wraps `JSON.stringify` and returns the result or throws an error.\r\n * Only plain objects (POJOs) are accepted. Class instances, Maps, Sets, etc. are rejected.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You have a box of toys (your object) and take a photo of it (a JSON string)\r\n * so you can send it to a friend.\r\n *\r\n * @template T - Plain object type to serialize.\r\n * @param obj - The object to stringify (must be a plain object).\r\n * @returns JSON string representation of the object.\r\n * @throws {Error} If `obj` is not a plain object or serialization fails.\r\n *\r\n * @example\r\n * ```ts\r\n * const json = stringifyObj({ a: 1 }); // \"{\\\"a\\\":1}\"\r\n * ```\r\n */\r\nexport function stringifyObj<T extends object = Record<string, unknown>>(obj: T): string {\r\n const { result, error } = $stringifyObj(obj);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n\r\n/**\r\n * Safely parses a JSON string to a plain object (non-throwing).\r\n *\r\n * Wraps `JSON.parse` and returns a `Result` containing the parsed object, or an error.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You rebuild your toy box (an object) from a photo you took (a JSON string).\r\n *\r\n * @template T - The expected object type.\r\n * @param str - The JSON string to parse.\r\n * @returns A `Result` with the parsed object on success, or an error.\r\n *\r\n * @example\r\n * ```ts\r\n * const {result, error, success} = tryParseToObj<{ a: number }>('{\"a\":1}');\r\n *\r\n * if (success) console.log(result); // { a: 1 }\r\n * else console.error(error) // { message: \"...\", description: \"...\" }\r\n * ```\r\n */\r\nexport function tryParseToObj<T extends object = Record<string, unknown>>(str: string): Result<{ result: T }> {\r\n return $parseToObj<T>(str);\r\n}\r\n\r\n/**\r\n * Parses a JSON string to a plain object (throwing).\r\n *\r\n * Wraps `JSON.parse` and returns the parsed object, or throws on failure.\r\n *\r\n * ### 🍼 Explain Like I'm Five\r\n * You rebuild your toy box (an object) from a photo you took (a JSON string).\r\n *\r\n * @template T - The expected object type.\r\n * @param str - The JSON string to parse.\r\n * @returns The parsed plain object.\r\n * @throws {Error} If the string can’t be parsed or doesn’t represent a plain object.\r\n *\r\n * @example\r\n * ```ts\r\n * const obj = parseToObj<{ a: number }>('{\"a\":1}'); // obj.a === 1\r\n * ```\r\n */\r\nexport function parseToObj<T extends object = Record<string, unknown>>(str: string): T {\r\n const { result, error } = $parseToObj<T>(str);\r\n if (error) throw new Error($fmtResultErr(error));\r\n return result;\r\n}\r\n"]}
|