sm-crypto-wasm 0.1.0 → 0.1.2

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.
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 宿主随机数包装(**浏览器版**)。
3
+ *
4
+ * 打包时由 package.json 的 `browser` 字段替换 `random.js`。存在的唯一理由是:
5
+ * `node:crypto` 会让 Webpack 5 / esbuild / Rspack 在解析期构建失败。
6
+ * 与宿主无关的拒绝采样逻辑在 `random.core.ts`。本文件**零 `node:*` 引用**。
7
+ *
8
+ * 安全姿态与 Node 版一致:**只认 `globalThis.crypto`,取不到就抛错
9
+ * (fail-closed),绝不降级到非密码学随机源。**
10
+ */
11
+ import { scalarBelowFrom } from './random.core.js';
12
+ /**
13
+ * 解析宿主 CSPRNG。浏览器里只有 `globalThis.crypto` 一个来源,
14
+ * 拿不到就抛错 —— 宁可不可用,也不用弱随机。
15
+ */
16
+ function resolveHostCrypto() {
17
+ const g = globalThis.crypto;
18
+ if (g && typeof g.getRandomValues === 'function')
19
+ return g;
20
+ throw new Error('sm-crypto-wasm: 当前环境没有 WebCrypto(crypto.getRandomValues)。' +
21
+ '浏览器需要安全上下文(https 或 localhost);若已被移除或未提供,' +
22
+ '请勿继续 —— 本库不会用非密码学随机源代替。');
23
+ }
24
+ /** 宿主 CSPRNG 引用;模块加载时即解析。 */
25
+ const c = resolveHostCrypto();
26
+ /**
27
+ * 取 `n` 字节 CSPRNG 随机数。
28
+ *
29
+ * @param n 字节数,范围 `[0, 65536]`(WebCrypto `getRandomValues` 的规范上限)。
30
+ * @returns 新分配的字节数组(拷贝)。
31
+ * @throws RangeError n 不在范围内或不是整数。
32
+ */
33
+ export function randomBytes(n) {
34
+ if (!Number.isInteger(n) || n < 0 || n > 65536) {
35
+ throw new RangeError(`randomBytes: length must be integer in [0, 65536], got ${n}`);
36
+ }
37
+ const out = new Uint8Array(n);
38
+ if (n > 0)
39
+ c.getRandomValues(out);
40
+ return out;
41
+ }
42
+ /**
43
+ * 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
44
+ *
45
+ * @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
46
+ * @returns 满足 `1 ≤ k < order` 的大整数。
47
+ */
48
+ export function randomScalarBelow(order) {
49
+ return scalarBelowFrom(order, randomBytes);
50
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 随机数共享核心(Node 版与浏览器版共用)。
3
+ *
4
+ * **零 `node:*` 引用** —— 浏览器版的模块图里不能出现 node 内置模块
5
+ * (Webpack / esbuild / Rspack 解析期即失败)。宿主 CSPRNG 的获取方式由
6
+ * `random.ts`(Node)与 `random.browser.ts`(浏览器)各自实现。
7
+ */
8
+ /**
9
+ * 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
10
+ *
11
+ * 算法:256-bit 候选 + 拒绝采样。从宿主 CSPRNG 取 32 字节拼成大整数,
12
+ * 若为 0 或 ≥ `order` 则重抽。SM2 的 n 几乎等于 2²⁵⁶(一次命中),
13
+ * SM9 的 n ≈ 0.712 × 2²⁵⁶(平均约 1.4 次)。偏置上界 ≈ `order / 2²⁵⁶`,
14
+ * 量级在 2⁻¹²⁸ 以下。
15
+ *
16
+ * @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
17
+ * @param randomBytes 宿主 CSPRNG 取字节函数(由调用方注入,保证两侧实现一致)。
18
+ * @returns 满足 `1 ≤ k < order` 的大整数。
19
+ */
20
+ export declare function scalarBelowFrom(order: bigint, randomBytes: (n: number) => Uint8Array): bigint;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 随机数共享核心(Node 版与浏览器版共用)。
3
+ *
4
+ * **零 `node:*` 引用** —— 浏览器版的模块图里不能出现 node 内置模块
5
+ * (Webpack / esbuild / Rspack 解析期即失败)。宿主 CSPRNG 的获取方式由
6
+ * `random.ts`(Node)与 `random.browser.ts`(浏览器)各自实现。
7
+ */
8
+ /**
9
+ * 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
10
+ *
11
+ * 算法:256-bit 候选 + 拒绝采样。从宿主 CSPRNG 取 32 字节拼成大整数,
12
+ * 若为 0 或 ≥ `order` 则重抽。SM2 的 n 几乎等于 2²⁵⁶(一次命中),
13
+ * SM9 的 n ≈ 0.712 × 2²⁵⁶(平均约 1.4 次)。偏置上界 ≈ `order / 2²⁵⁶`,
14
+ * 量级在 2⁻¹²⁸ 以下。
15
+ *
16
+ * @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
17
+ * @param randomBytes 宿主 CSPRNG 取字节函数(由调用方注入,保证两侧实现一致)。
18
+ * @returns 满足 `1 ≤ k < order` 的大整数。
19
+ */
20
+ export function scalarBelowFrom(order, randomBytes) {
21
+ if (order <= 1n)
22
+ throw new RangeError('randomScalarBelow: order must be > 1');
23
+ // 用 256-bit 候选:4 个 64-bit big-endian limb。
24
+ while (true) {
25
+ const buf = randomBytes(32); // 256 bits
26
+ let candidate = 0n;
27
+ for (let i = 0; i < 32; i++) {
28
+ candidate = (candidate << 8n) | BigInt(buf[i] ?? 0);
29
+ }
30
+ // 严格 < order(不允许 0)。
31
+ if (candidate > 0n && candidate < order)
32
+ return candidate;
33
+ // 否则重抽;理论上不到一次。
34
+ }
35
+ }
@@ -1,3 +1,18 @@
1
+ /**
2
+ * 宿主随机数包装(**Node 版**)。
3
+ *
4
+ * 浏览器打包时本文件被 `random.browser.ts` 替换(package.json 的 `browser` 字段),
5
+ * 因为 `node:crypto` 会让 Webpack / esbuild / Rspack 在解析期构建失败。
6
+ * 与宿主无关的拒绝采样逻辑在 `random.core.ts`。
7
+ *
8
+ * 设计要点(spec §9 安全姿态):
9
+ * - **永远走宿主 CSPRNG**:Node 19+ 用 `globalThis.crypto`,Node 18 回退
10
+ * `node:crypto` 的 `webcrypto`(`globalThis.crypto` 直到 Node 19 才默认暴露)。
11
+ * - **取不到就抛错(fail-closed)**:绝不实例化本地 RNG,也绝不静默降级到
12
+ * 非密码学随机源。
13
+ * - **长度边界**:输入 `n ∈ [0, 65536]`,溢出抛 `RangeError`
14
+ * (65536 是 WebCrypto `getRandomValues` 的规范上限)。
15
+ */
1
16
  /**
2
17
  * 取 `n` 字节 CSPRNG 随机数。
3
18
  *
@@ -7,10 +22,7 @@
7
22
  */
8
23
  export declare function randomBytes(n: number): Uint8Array;
9
24
  /**
10
- * 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2/SM9 椭圆曲线 k)。
11
- *
12
- * 算法:64-bit limb rejection sampling。从 `getRandomValues` 取 4 个 64-bit
13
- * limb 拼接成 256-bit 候选值;若 ≥ `order` 则重抽。偏置 ≤ 2^-128,足够低。
25
+ * 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
14
26
  *
15
27
  * @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
16
28
  * @returns 满足 `1 ≤ k < order` 的大整数。
@@ -1,18 +1,39 @@
1
1
  /**
2
- * 宿主随机数包装(host-backed randomness)。
2
+ * 宿主随机数包装(**Node 版**)。
3
+ *
4
+ * 浏览器打包时本文件被 `random.browser.ts` 替换(package.json 的 `browser` 字段),
5
+ * 因为 `node:crypto` 会让 Webpack / esbuild / Rspack 在解析期构建失败。
6
+ * 与宿主无关的拒绝采样逻辑在 `random.core.ts`。
3
7
  *
4
8
  * 设计要点(spec §9 安全姿态):
5
- * - **永远走宿主 CSPRNG**:浏览器 `globalThis.crypto.getRandomValues`、
6
- * Node `webcrypto.getRandomValues`。**绝不**实例化本地 RNG。
7
- * - **拒绝采样(rejection sampling)**:用于 SM2/SM9 椭圆曲线私钥 k;
8
- * 按 64-bit limb 切分,从 256-bit 候选中重抽直至 < order。
9
- * - **长度边界**:输入 `n ∈ [0, 65536]`,溢出抛 `RangeError`。
9
+ * - **永远走宿主 CSPRNG**:Node 19+ 用 `globalThis.crypto`,Node 18 回退
10
+ * `node:crypto` 的 `webcrypto`(`globalThis.crypto` 直到 Node 19 才默认暴露)。
11
+ * - **取不到就抛错(fail-closed)**:绝不实例化本地 RNG,也绝不静默降级到
12
+ * 非密码学随机源。
13
+ * - **长度边界**:输入 `n ∈ [0, 65536]`,溢出抛 `RangeError`
14
+ * (65536 是 WebCrypto `getRandomValues` 的规范上限)。
15
+ */
16
+ import * as nodeCrypto from 'node:crypto';
17
+ import { scalarBelowFrom } from './random.core.js';
18
+ /**
19
+ * 解析宿主 CSPRNG。**取不到就抛错,绝不静默降级到非密码学随机源。**
10
20
  *
11
- * spec/plan 对应:plan Task 3;spec §9 安全姿态、§6 SM2/SM9 API。
21
+ * - Node 19+:`globalThis.crypto`(WebCrypto);
22
+ * - Node 18:`node:crypto` 的 `webcrypto` —— `globalThis.crypto` 直到 Node 19 才
23
+ * 默认暴露,Node 18 需要 `--experimental-global-webcrypto`,所以回退是必要的。
12
24
  */
13
- import { webcrypto } from 'node:crypto';
14
- /** 宿主 CSPRNG 引用;优先 `globalThis.crypto`(浏览器 / Node 19+)。 */
15
- const c = globalThis.crypto ?? webcrypto;
25
+ function resolveHostCrypto() {
26
+ const g = globalThis.crypto;
27
+ if (g && typeof g.getRandomValues === 'function')
28
+ return g;
29
+ const wc = nodeCrypto.webcrypto;
30
+ if (wc && typeof wc.getRandomValues === 'function')
31
+ return wc;
32
+ throw new Error('sm-crypto-wasm: 找不到宿主 CSPRNG(需要 WebCrypto 的 crypto.getRandomValues);' +
33
+ 'Node 18 请加 --experimental-global-webcrypto,或先 polyfill globalThis.crypto。');
34
+ }
35
+ /** 宿主 CSPRNG 引用;模块加载时即解析。 */
36
+ const c = resolveHostCrypto();
16
37
  /**
17
38
  * 取 `n` 字节 CSPRNG 随机数。
18
39
  *
@@ -30,27 +51,11 @@ export function randomBytes(n) {
30
51
  return out;
31
52
  }
32
53
  /**
33
- * 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2/SM9 椭圆曲线 k)。
34
- *
35
- * 算法:64-bit limb rejection sampling。从 `getRandomValues` 取 4 个 64-bit
36
- * limb 拼接成 256-bit 候选值;若 ≥ `order` 则重抽。偏置 ≤ 2^-128,足够低。
54
+ * 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
37
55
  *
38
56
  * @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
39
57
  * @returns 满足 `1 ≤ k < order` 的大整数。
40
58
  */
41
59
  export function randomScalarBelow(order) {
42
- if (order <= 1n)
43
- throw new RangeError('randomScalarBelow: order must be > 1');
44
- // 用 256-bit 候选:4 个 64-bit big-endian limb。
45
- while (true) {
46
- const buf = randomBytes(32); // 256 bits
47
- let candidate = 0n;
48
- for (let i = 0; i < 32; i++) {
49
- candidate = (candidate << 8n) | BigInt(buf[i] ?? 0);
50
- }
51
- // 严格 < order(不允许 0)。
52
- if (candidate > 0n && candidate < order)
53
- return candidate;
54
- // 否则重抽;理论上不到一次。
55
- }
60
+ return scalarBelowFrom(order, randomBytes);
56
61
  }
package/dist/sm2/index.js CHANGED
@@ -65,6 +65,24 @@ function getModule() {
65
65
  throw new Error('sm2: 未初始化,请先 `await ensureLoaded()`。');
66
66
  return mod;
67
67
  }
68
+ /**
69
+ * 现取 WASM memory 视图。
70
+ *
71
+ * **每次都用**,绝不在多次 `alloc` / wasm 调用之间缓存 `new Uint8Array(memory.buffer)`:
72
+ * 任何一次分配都可能让 WASM 堆增长(`memory.grow`),旧的 ArrayBuffer 会被 detach,
73
+ * 复用旧视图会抛 `TypeError: Cannot perform Construct on a detached ArrayBuffer`。
74
+ */
75
+ function memView() {
76
+ return new Uint8Array(getModule().memory.buffer);
77
+ }
78
+ /** 写入字节到 WASM 内存(现取视图)。 */
79
+ function putBytes(b, ptr) {
80
+ memView().set(b, ptr);
81
+ }
82
+ /** 读出 WASM 内存的拷贝(现取视图后 slice,不向上层交出会失效的视图)。 */
83
+ function readBytes(ptr, len) {
84
+ return new Uint8Array(getModule().memory.buffer, ptr, len).slice();
85
+ }
68
86
  /**
69
87
  * 从私钥派生公钥。
70
88
  *
@@ -80,13 +98,13 @@ export function getPublicKey(privateKey) {
80
98
  * `getPublicKey` 与缓存未命中时的 `derivePublicKeyBytes` 都走这里。
81
99
  */
82
100
  function derivePublicKeyRaw(d) {
83
- const { exports, memory } = getModule();
101
+ const { exports } = getModule();
84
102
  const dp = exports.alloc(32);
85
103
  const op = exports.alloc(64);
86
104
  try {
87
- new Uint8Array(memory.buffer).set(d, dp);
105
+ putBytes(d, dp);
88
106
  exports.publicKeyOut(dp, op);
89
- return new Uint8Array(memory.buffer, op, 64).slice();
107
+ return readBytes(op, 64);
90
108
  }
91
109
  finally {
92
110
  exports.freeAll();
@@ -209,7 +227,6 @@ export function sign(privateKey, message) {
209
227
  const m = toBytes(message, 'message');
210
228
  const pub = derivePublicKeyBytes(d);
211
229
  const { exports } = getModule();
212
- const mem = new Uint8Array(getModule().memory.buffer);
213
230
  const dp = exports.alloc(32);
214
231
  const pxp = exports.alloc(32);
215
232
  const pyp = exports.alloc(32);
@@ -217,17 +234,17 @@ export function sign(privateKey, message) {
217
234
  const kp = exports.alloc(32);
218
235
  const op = exports.alloc(64);
219
236
  try {
220
- mem.set(d, dp);
221
- mem.set(pub.subarray(0, 32), pxp);
222
- mem.set(pub.subarray(32, 64), pyp);
223
- mem.set(m, mp);
237
+ putBytes(d, dp);
238
+ putBytes(pub.subarray(0, 32), pxp);
239
+ putBytes(pub.subarray(32, 64), pyp);
240
+ putBytes(m, mp);
224
241
  // 最多重试几次 k(极小概率 r/s 为 0 或 r+k==n)。
225
242
  for (let attempt = 0; attempt < 16; attempt++) {
226
243
  const k = randomK();
227
- mem.set(k, kp);
244
+ putBytes(k, kp);
228
245
  const rc = exports.signOut(dp, pxp, pyp, mp, m.length, kp, op);
229
246
  if (rc === 0) {
230
- return fromBytes(new Uint8Array(mem.buffer, op, 64).slice(), 'hex');
247
+ return fromBytes(readBytes(op, 64), 'hex');
231
248
  }
232
249
  }
233
250
  throw new Error('sm2.sign: failed to produce a valid signature after retries');
@@ -251,16 +268,15 @@ export function verify(publicKey, message, signature) {
251
268
  if (sig.length !== 64)
252
269
  throw new InvalidCiphertextError('sm2: signature must be 64 bytes');
253
270
  const { exports } = getModule();
254
- const mem = new Uint8Array(getModule().memory.buffer);
255
271
  const pxp = exports.alloc(32);
256
272
  const pyp = exports.alloc(32);
257
273
  const mp = exports.alloc(m.length);
258
274
  const sigp = exports.alloc(64);
259
275
  try {
260
- mem.set(x, pxp);
261
- mem.set(y, pyp);
262
- mem.set(m, mp);
263
- mem.set(sig, sigp);
276
+ putBytes(x, pxp);
277
+ putBytes(y, pyp);
278
+ putBytes(m, mp);
279
+ putBytes(sig, sigp);
264
280
  return exports.verifyOut(pxp, pyp, mp, m.length, sigp) === 1;
265
281
  }
266
282
  finally {
@@ -286,21 +302,20 @@ export function encrypt(publicKey, message, opts = {}) {
286
302
  }
287
303
  const m = toBytes(message, 'message');
288
304
  const { exports } = getModule();
289
- const mem = new Uint8Array(getModule().memory.buffer);
290
305
  const pxp = exports.alloc(32);
291
306
  const pyp = exports.alloc(32);
292
307
  const mp = exports.alloc(m.length);
293
308
  const kp = exports.alloc(32);
294
309
  const ctp = exports.alloc(97 + m.length);
295
310
  try {
296
- mem.set(x, pxp);
297
- mem.set(y, pyp);
298
- mem.set(m, mp);
311
+ putBytes(x, pxp);
312
+ putBytes(y, pyp);
313
+ putBytes(m, mp);
299
314
  for (let attempt = 0; attempt < 16; attempt++) {
300
- mem.set(randomK(), kp);
315
+ putBytes(randomK(), kp);
301
316
  const rc = exports.encryptOut(pxp, pyp, mp, m.length, kp, ctp);
302
317
  if (rc === 1) {
303
- const raw = new Uint8Array(mem.buffer, ctp, 97 + m.length).slice();
318
+ const raw = readBytes(ctp, 97 + m.length);
304
319
  return fromBytes(format === 'C1C2C3' ? c1c3c2ToC1c2c3(raw) : raw, 'hex');
305
320
  }
306
321
  }
@@ -354,18 +369,17 @@ function decryptUnsafe(privateKey, ciphertext, opts) {
354
369
  /** 按 C1C3C2 布局解密(wasm 侧约定的布局)。 */
355
370
  function decryptRaw(d, ct) {
356
371
  const { exports } = getModule();
357
- const mem = new Uint8Array(getModule().memory.buffer);
358
372
  const dp = exports.alloc(32);
359
373
  const ctp = exports.alloc(ct.length);
360
374
  const outLen = ct.length - 97;
361
375
  const ptp = exports.alloc(outLen === 0 ? 1 : outLen);
362
376
  try {
363
- mem.set(d, dp);
364
- mem.set(ct, ctp);
377
+ putBytes(d, dp);
378
+ putBytes(ct, ctp);
365
379
  const rc = exports.decryptOut(dp, ctp, ct.length, ptp);
366
380
  if (rc !== 1)
367
381
  throw new DecryptionError('sm2.decrypt: C3 verification failed');
368
- return fromBytes(new Uint8Array(mem.buffer, ptp, outLen).slice(), 'hex');
382
+ return fromBytes(readBytes(ptp, outLen), 'hex');
369
383
  }
370
384
  finally {
371
385
  exports.freeAll();
package/dist/sm4/index.js CHANGED
@@ -34,7 +34,7 @@ function withBuffers(key, data, outLen, fn) {
34
34
  const mem = new Uint8Array(memory.buffer);
35
35
  mem.set(key, keyPtr);
36
36
  mem.set(data, inPtr);
37
- return fn(keyPtr, inPtr, outPtr, new Uint8Array(memory.buffer));
37
+ return fn(keyPtr, inPtr, outPtr);
38
38
  }
39
39
  finally {
40
40
  exports.freeAll();
@@ -244,7 +244,7 @@ function withGcmBuffers(k, v, a, data, fn) {
244
244
  mem.set(a, aadPtr);
245
245
  if (data.length !== 0)
246
246
  mem.set(data, inPtr);
247
- return fn({ keyPtr, ivPtr, aadPtr, inPtr, outPtr, tagPtr }, mem);
247
+ return fn({ keyPtr, ivPtr, aadPtr, inPtr, outPtr, tagPtr });
248
248
  }
249
249
  finally {
250
250
  exports.freeAll();
package/dist/sm9/index.js CHANGED
@@ -354,17 +354,21 @@ export function encrypt(publicKey, id, msg, r) {
354
354
  const idP = exports.alloc(idB.length);
355
355
  const msgP = exports.alloc(msgB.length);
356
356
  const rP = exports.alloc(32);
357
- const ctP = exports.alloc(4096);
357
+ // 密文布局 C1(65) || C3(32) || C2(|msg|),长度随明文增长 —— 按实际长度分配,
358
+ // 并把容量一并交给 wasm 侧校验。曾硬编码 alloc(4096):明文超过 3999 字节时
359
+ // sm9Encrypt 会写越界、破坏 GC 堆(几轮后 RuntimeError: memory access out of bounds)。
360
+ const ctCap = 65 + 32 + msgB.length;
361
+ const ctP = exports.alloc(ctCap);
358
362
  try {
359
363
  putBytes(key.bytes, keyP);
360
364
  putBytes(idB, idP);
361
365
  putBytes(msgB, msgP);
362
366
  putBytes(rB, rP);
363
367
  const ctLen = key.kind === 'public'
364
- ? exports.sm9EncryptPubOut(keyP, idP, idB.length, msgP, msgB.length, rP, ctP)
365
- : exports.sm9EncryptOut(keyP, idP, idB.length, msgP, msgB.length, rP, ctP);
368
+ ? exports.sm9EncryptPubOut(keyP, idP, idB.length, msgP, msgB.length, rP, ctP, ctCap)
369
+ : exports.sm9EncryptOut(keyP, idP, idB.length, msgP, msgB.length, rP, ctP, ctCap);
366
370
  if (ctLen < 0)
367
- throw new Error('sm9: encrypt 失败');
371
+ throw new Error('sm9: encrypt 失败(密文缓冲容量不足)');
368
372
  return readHex(ctP, ctLen);
369
373
  }
370
374
  finally {
package/dist/sm9/sm9.wasm CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sm-crypto-wasm",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "国密 SM2/SM3/SM4/SM9/ZUC 的 WASM 实现,Node 18+ 与现代浏览器均可使用,按算法分包懒加载。",
5
5
  "description:en": "SM2/SM3/SM4/SM9/ZUC (China crypto) implemented in AssemblyScript -> WASM. Node 18+ & modern browsers, per-algorithm lazy loading.",
6
6
  "license": "Apache-2.0",
@@ -30,6 +30,10 @@
30
30
  },
31
31
  "main": "./dist/index.js",
32
32
  "types": "./dist/index.d.ts",
33
+ "browser": {
34
+ "./dist/runtime/loadWasm.js": "./dist/runtime/loadWasm.browser.js",
35
+ "./dist/runtime/random.js": "./dist/runtime/random.browser.js"
36
+ },
33
37
  "exports": {
34
38
  ".": {
35
39
  "types": "./dist/index.d.ts",
@@ -55,6 +59,10 @@
55
59
  "types": "./dist/zuc/index.d.ts",
56
60
  "import": "./dist/zuc/index.js"
57
61
  },
62
+ "./esbuild-plugin": {
63
+ "types": "./dist/esbuild-plugin.d.ts",
64
+ "import": "./dist/esbuild-plugin.js"
65
+ },
58
66
  "./package.json": "./package.json"
59
67
  },
60
68
  "files": [