Commit Graph
50 Commits
Author SHA1 Message Date
LongYinanandDevin fe73c75332 feat!: redesign bcrypt API while preserving stored hash verification (#1207)
* feat!: redesign bcrypt creation API and narrow Argon2 verification options

* chore: keep major API changes scoped to bcrypt

* fix(bcrypt): preserve Node 10 and 12 runtime compatibility

* fix(bcrypt): support cancellation without global AbortController

* fix(bcrypt): polish the 2.0 API and packaging before release

- Settle the public Promise before removing the abort listener, and remove
  it with the same options, so throwing or capture-flag EventTargets cannot
  hang a call or leak the listener.
- Keep signal.reason as a non-enumerable AbortError cause, so callers can
  tell a TimeoutError or custom reason apart.
- Throw TypeError for option values of the wrong type and keep RangeError
  for out-of-range or malformed values.
- Copy only the 72 password bytes bcrypt reads for async work, and wipe
  those copies on drop; replace the unreachable!() salt branch.
- Declare package exports so internal files cannot be deep-imported.
- Stop declaring the WASI package as an optional dependency, so installs do
  not pull it and a missing native binary fails loudly.
- Leave the version bump to the release step, add the 2.0.0 changelog, and
  preapprove this repo's own @node-rs/* packages.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(bcrypt): keep the event loop alive while waiting for AbortSignal.timeout

AbortSignal.timeout() uses a timer that does not keep the event loop
alive, and the controlled backend has no native work that would. The
test passed only when other tests kept the worker busy, and failed with
"Promise returned by test never resolved" on the Linux and WASI jobs.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore: resolve bcrypt-previous from the registry

With the bcrypt workspace back at 1.10.9, yarn's transparent workspaces
link bcrypt-previous (npm:@node-rs/bcrypt@1.10.9) to the workspace
itself whenever the lockfile is regenerated, so the previous-release
tests would compare the new code with itself. No workspace depends on
another, so only the workspace: protocol needs to link workspaces.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore(bcrypt): regenerate bindings with @napi-rs/cli 3.10.5

main moved @napi-rs/cli from 3.9.0 to 3.10.5. Regenerate the bcrypt
loaders and type declarations from native and wasm32-wasip1-threads
builds so the committed files match what the release build produces.
binding.js still parses as ES2019 for Node 10 and 12.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(bcrypt): keep custom signal errors from breaking settled or aborted calls

A signal whose removeEventListener throws made a successful call raise an
unhandled rejection, which exits modern Node, and on abort it skipped the
native cancellation. A signal whose reason getter throws stopped the abort
listener before it settled, so the call stayed pending or resolved after
the abort. Ignore cleanup failures once the call has settled, and read the
optional reason defensively.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* feat(bcrypt): add parseOptions and Node-style error codes for 2.0

Bundle two more contract changes into the 2.0 break so the error and
rehash-on-login story is complete at release:

- Thrown errors carry Node-style codes: TypeError ERR_INVALID_ARG_TYPE,
  RangeError ERR_OUT_OF_RANGE (including native InvalidArg translations),
  and AbortError ABORT_ERR, matching Node's own AbortError.
- parseOptions(hash) returns { version, cost } through the verifier's
  parser (bcrypt::HashParts), so every hash verify accepts is parseable,
  including +4 costs and imported 2x labels; hashes verify always rejects
  throw RangeError. Mirrors argon2's parseOptions in this repo.
- AbortSignalLike.removeEventListener declares the options argument the
  adapter actually passes.
- Drop unused blowfish and quickcheck dependencies from the bcrypt crate
  and the workspace.

Native and wasm32-wasip1-threads bindings are regenerated. api.cjs still
parses as ES2019 for Node 10 and 12.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(bcrypt): tidy parseOptions review nits

- Use one options type for add/removeEventListener on AbortSignalLike,
  matching the comment that the same object is passed to both.
- Give parseOptions its own arity message; it never had positional
  options to drop.
- Cover the 60-byte non-ASCII parser branch in the test, which the
  previous 61-byte input missed.
- Mention in the README that parseOptions throws for hashes verify
  always rejects.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* feat(bcrypt): give the backend contract error a code

A stale platform package now fails at import time with
code ERR_BCRYPT_INCOMPATIBLE_BINARY, so every error the package throws
sits inside the documented ErrorCode contract.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* feat(bcrypt): switch hashing backend to bcrypt-rust

Replace the bcrypt 0.19 dependency with bcrypt-rust 1.0, keeping the crate
name so imports stay valid. The new crate is a drop-in for every API used
here except one deliberate difference: its HashParts parser is strict and
rejects the noncanonical '+4' cost spelling the old backend accepted.

Verify and parseOptions now use a lenient parser that mirrors the old
backend byte-for-byte (60 ASCII bytes, 2a/2b/2x/2y markers, u32::parse for
cost), then hand the canonicalized hash to bcrypt::verify so the cost gate
and constant-time compare stay in the library.

~20% faster than the C++ backend on hash and verify.

* bench: make benchmark comparisons 1:1 and migrate argon2 to tinybench

bcrypt: uniform call signatures via per-impl adapters, a fixed salt so the
hash suite measures hashing only, a real sync compare for verify (it
previously timed an unawaited async call with a hash inside the loop), one
cost across suites, all five implementations in every suite, and a compact
sorted table with latency, rme, ops/s, and a ratio to the baseline.

argon2: replace the hand-rolled performance.now() harness with tinybench
fixed-iteration runs, keep the per-iteration tag assertion and the
native-sync / native-async / js-wasm grouping, and adopt the same report
format.

* bench: narrow tinybench TaskResult before reading stats

TaskResult is a discriminated union; latency/throughput exist only on
completed and aborted-with-statistics. CI type-checks benchmark files
through packages/argon2/tsconfig.json, unlike oxnode which strips types.

* perf(bcrypt): fast-path genSaltSync options; note wasm salt overhead in bench comment

genSaltSync previously ran every call through options() + creation(),
allocating a fresh Object.create(null) copy even for {} and {cost}. Now
undefined uses a frozen empty options object and reusable options shapes
validate in place — identical error paths preserved for non-plain or
extra-key objects. Saves ~200ns of JS wrapper overhead per call.

* deps: bump bcrypt-rust to 1.0.1

Picks up the crypt_blowfish fused-round kernel shape
(Brooooooklyn/bcrypt-rust@f65cd90): bcrypt() at cost 10 on x86_64
drops ~19% (138.2ms -> 112.6ms vs vendored C at 108.7ms); arm64
unchanged.

* perf(bcrypt): cut genSaltSync overhead; pool OS entropy per thread

genSaltSync now serves 16-byte salts from a 256-byte thread-local pool
refilled by SysRng (direct getrandom), amortizing one syscall over 16
salts without any userspace PRNG in between. The salt string is built
without format! machinery and the bcrypt base64 engine is constructed
once in a LazyLock instead of per call.

api.cjs gains a specialized fast path for the {cost,version} shapes:
getOwnPropertyNames/getOwnPropertySymbols cover the same key space as
Reflect.ownKeys at half the cost, the sync() closure is inlined, and
anything the fast path cannot prove valid funnels into a shared full-
validation path so error types, codes, and messages stay identical.

x86_64 sandbox (30k-iteration amortized loop, best of reps):
  @node-rs/bcrypt ~380-560ns vs wasm OpenBSD ~550-730ns
  per-call min: 210-250ns vs 320-370ns; bench.js min 290ns vs 860ns.

* deps: bump bcrypt-rust to 1.0.2

Picks up the xor_late asm boundary (Brooooooklyn/bcrypt-rust#8): LLVM no
longer reassociates the fused round into two serial xors after the S-box
loads. On x86_64 EPYC the raw kernel now beats crypt_blowfish C
(~54.6ms vs ~54.9ms at cost 10) and JS hashSync/verifySync beat node
bcrypt (C++); arm64/wasm32 untouched via cfg fallback. CodSpeed: -4% on
all single-hash benches.

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-05 23:33:58 +08:00
renovate[bot] cfa1f9b90e chore(deps): update rust crate jieba-rs to 0.11 (#1213)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-09-16 19:57:21 +00:00
LongYinan b0ea4a01b8 perf(argon2): simdutf8, crate PHC decode, argon2-rust 1.1 (#1189)
* perf(argon2): validate UTF-8 with simdutf8 before owning the string

Check the JS byte slice with simdutf8, then build String via
from_utf8_unchecked so from_utf8 does not walk the bytes again.

* perf(argon2): decode PHC Base64 with a LUT instead of the engine

Salt and tag fields are tens of bytes. A 256-entry table plus leftover-bit
checks beats the generic base64 crate, and on aarch64 argon2-rust's SIMD
would not even run for a 16-byte salt.

* perf(argon2): decode PHC fields with argon2-rust SIMD Base64

Use the new encode_base64/decode_base64 entry points. decode_phc still
owns param order and data=. Temporarily depends on the local
argon2-rust tree until 1.1.0 is on crates.io.

* feat(argon2): consume argon2-rust 1.1 PHC APIs

Drop the local PHC parser, encoder, and compare. Hash and verify go
through hash_encoded_with_ad, decode_phc, and verify_with_ad.
Require argon2-rust 1.1 from crates.io; the sibling path dep is gone.
2026-08-13 16:30:24 +08:00
LongYinan 2b8b617be0 feat(argon2): switch backend to argon2-rust (#1188)
* feat(argon2): switch backend to argon2-rust

Replace RustCrypto argon2 with argon2-rust 1.0.0 from crates.io.
Keep the public JS API. Verify accepts node-argon2 PHC param order
and associatedData. The bench pins the same raw params, salt, and
tag across implementations.

* fix(argon2): keep Node 22 tests and bound verify threads

Skip the node:crypto.argon2Sync comparison on runtimes that lack it.
Cap worker threads at available parallelism so a PHC p= value cannot
spawn one OS thread per lane. Allocate tag buffers with try_reserve
instead of vec! so a huge outputLen is a JS error, not a process abort.

* fix(argon2): skip node-argon2 interop when its native addon is missing

node-argon2 0.45 has no darwin-x64 prebuild. Intel macOS CI runs x64
Node on an ARM host, so a static import crashed the whole spec file.

* test(argon2): compare against node:crypto argon2

Drop the node-argon2 native addon from tests. Interop uses the
builtin KDF, which is available on every CI host that has it.
2026-08-13 11:01:01 +08:00
renovate[bot]andLongYinan 05156db802 chore(deps): update rust crate jsonwebtoken to v11 (#1177)
* chore(deps): update rust crate jsonwebtoken to v11

* fix(jsonwebtoken): handle non-exhaustive Algorithm in v11

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: LongYinan <lynweklm@gmail.com>
2026-08-13 00:23:31 +08:00
renovate[bot] f94ebe901b chore(deps): update rust crate base64 to 0.23 (#1176)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-23 18:41:54 +00:00
renovate[bot] 6755acf779 chore(deps): update rust crate jieba-rs to 0.10 (#1140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-22 09:30:52 +00:00
renovate[bot] 9252fd481d chore(deps): update rust crate jieba-rs to 0.9 (#1114)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-13 01:47:39 +00:00
renovate[bot] f32a3e9594 chore(deps): update rust crate blowfish to 0.10 (#1112)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-10 17:45:42 +00:00
renovate[bot] 2339940c42 chore(deps): update rust crate argon2 to v0.6.0-rc.8 (#1098)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-22 05:45:37 +00:00
renovate[bot] cc8658b586 chore(deps): update rust crate bcrypt to 0.19 (#1088)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 10:12:09 +00:00
renovate[bot] e41883f64e chore(deps): update rust crate rand to 0.10 (#1073)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-08 18:04:56 +00:00
renovate[bot] 23ea4f8cf9 chore(deps): update rust crate argon2 to v0.6.0-rc.7 (#1072)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-03 02:46:06 +00:00
LongYinan 9764f3cf63 fix(deps): update rand_core (#1069) 2026-02-02 11:04:11 +08:00
renovate[bot] 2386a451f1 fix(deps): update rust crate rand_core to 0.10 (#1067)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-01 15:09:50 +00:00
renovate[bot] bddeab6d2f chore(deps): update rust crate argon2 to v0.6.0-rc.6 (#1060)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-21 18:34:15 +00:00
renovate[bot] c72d009069 chore(deps): update rust crate bcrypt to 0.18 (#1050)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-12 06:18:13 +00:00
d85d35a23e fix(deps): upgrade napi-rs/cli and fix build errors (#1048)
---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: LongYinan <lynweklm@gmail.com>
2026-01-12 14:01:13 +08:00
renovate[bot] aa5ce0c20c chore(deps): update rust crate jieba-rs to 0.8 (#1034)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-16 04:28:05 +00:00
renovate[bot] 8d41794c88 chore(deps): update rust crate bcrypt to 0.17 (#967)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-30 10:08:00 +00:00
renovate[bot] 3029a99f12 fix(deps): update rust crate rand_core to 0.9 (#963)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 21:30:46 +00:00
renovate[bot] 07cbdc0dfa chore(deps): update rust crate rand to 0.9 (#961)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 18:23:29 +00:00
renovate[bot] e97f7a7c28 chore(deps): update rust crate bcrypt to 0.16 (#929)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 15:59:45 +00:00
LongYinan ef96dda6e4 chore: refresh the infra (#915) 2024-10-27 15:04:00 +08:00
Linus UnnebäckandLongYinan 50be77d07d bench(bcrypt): add @cwasm/openbsd-bcrypt and @cwasm/openwall-bcrypt to benchmark (#649)
* Add @cwasm/openbsd-bcrypt and @cwasm/openwall-bcrypt to benchmark

* tinybench

---------

Co-authored-by: LongYinan <lynweklm@gmail.com>
2024-10-27 00:18:23 +08:00
LongYinan afd2a544b8 feat!(jieba): upgrade to jieba@0.7 (#912)
Also provide customizable API for Jieba and TfIdf
2024-10-26 23:25:58 +08:00
renovate[bot] 09acc08567 chore(deps): update rust crate napi-derive to 3.0.0-alpha (#905)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-14 01:33:40 +00:00
renovate[bot] fb1a6fa096 chore(deps): update rust crate napi to 3.0.0-alpha (#904)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-13 22:52:56 +00:00
LongYinan f66abff2c5 feat(*): upgrade to NAPI-RS 3.0 alpha (#897) 2024-10-03 17:51:06 +02:00
LongYinan 8b58703433 chore: deprecate deno-lint (#852) 2024-07-04 12:02:01 +08:00
LongYinan 4fb4bcc504 chore(*): upgrade NAPI-RS and bindings (#825) 2024-04-19 17:51:38 +08:00
LongYinan 2337ec38ce chore: upgrade dependencies 2023-07-20 10:05:43 +08:00
LongYinan a0ef7deb79 feat(*): upgrade dependencies 2023-04-24 08:47:02 +08:00
nebarf 70ceb14a0b feat(jsonwebtoken): jsonwebtoken implementation 2023-02-26 15:13:54 +01:00
LongYinan 8ae3d85dd5 perf(crc32): enable sse4.2 for x86 platform 2023-01-11 20:06:25 +08:00
LongYinan 58962a11cb chore: upgrade dependencies 2022-05-29 22:05:00 +08:00
LongYinan 9f953e0853 chore: upgrade napi to crates version (#633) 2022-05-07 21:00:30 +08:00
LongYinan 792f69d7ac fix(*): Node.js worker_threads safe by upgrade NAPI-RS version (#623) 2022-05-07 15:40:05 +08:00
LongYinan 6adc51dd43 feat(argon2): implement argon2 2021-12-29 21:17:35 +08:00
LongYinan 5ea38c3bfd feat(xxhash): init xxhash32 and xxhash64 2021-10-21 23:26:06 +08:00
LongYinan 0b2254ee11 chore: switch to nightly toochain, strip symbols (#463) 2021-07-18 23:53:02 +08:00
LongYinan 309fe82582 chore: upgrade dependencies 2021-03-02 15:19:32 +08:00
LongYinan 1f1a7902bb chore: upgrade all deps 2021-01-14 11:46:56 +08:00
LongYinan 0fa588eddb feat: upgrade to napi@1 2020-12-03 00:00:34 +08:00
LongYinan 025abfb754 feat: start deno lint 2020-08-19 00:05:13 +08:00
LongYinan 0e315051e2 feat: support linux musl 2020-06-12 02:23:30 +08:00
LongYinan 666f122275 feat(bcrypt): fastest bcrypt 2020-05-15 17:43:27 +08:00
LongYinan 9676a439ca feat(jieba): impl jieba-rs binding 2020-05-02 12:35:21 +08:00
LongYinan dfba81fbee feat(crc32): init 2020-04-20 00:39:43 +08:00
LongYinan 626fdf45ce Init 2020-03-18 00:10:21 +08:00