@sebastienrousseau/crypto-testing 0.0.7

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/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ This project is dual-licensed under either of:
2
+
3
+ * Apache License, Version 2.0 — see LICENSE-APACHE
4
+ * MIT license — see LICENSE-MIT
5
+
6
+ at your option.
7
+
8
+ SPDX-License-Identifier: Apache-2.0 OR MIT
package/README.md ADDED
@@ -0,0 +1,295 @@
1
+ <!-- SPDX-License-Identifier: Apache-2.0 OR MIT -->
2
+
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/sebastienrousseau/crypto-service/main/assets/crypto-testing-logo.svg" alt="crypto-testing logo" width="360" />
5
+ </p>
6
+
7
+ <h1 align="center">@sebastienrousseau/crypto-testing</h1>
8
+
9
+ <p align="center">
10
+ Deterministic keys, fast mocks, and test fixtures for crypto-lib — make your CI/CD pipeline fast and reproducible.
11
+ </p>
12
+
13
+ <p align="center">
14
+ <a href="https://github.com/sebastienrousseau/crypto-service/actions"><img src="https://img.shields.io/github/actions/workflow/status/sebastienrousseau/crypto-service/ci.yml?branch=main&style=for-the-badge&logo=github" alt="Build" /></a>
15
+ <a href="https://coveralls.io/github/sebastienrousseau/crypto-service?branch=main"><img src="https://img.shields.io/coveralls/github/sebastienrousseau/crypto-service?branch=main&style=for-the-badge" alt="Coverage" /></a>
16
+ <a href="https://www.npmjs.com/package/@sebastienrousseau/crypto-testing"><img src="https://img.shields.io/npm/v/@sebastienrousseau/crypto-testing.svg?style=for-the-badge&color=f14041&logo=npm" alt="Registry" /></a>
17
+ <a href="https://sebastienrousseau.github.io/crypto-service/"><img src="https://img.shields.io/badge/docs-TypeDoc-blue.svg?style=for-the-badge&labelColor=555555&logo=typescript" alt="Docs" /></a>
18
+ <a href="https://scorecard.dev/viewer/?uri=github.com/sebastienrousseau/crypto-service" title="ossf-scorecard"><img src="https://img.shields.io/badge/OpenSSF-Scorecard-blue?style=for-the-badge&logo=openssf" alt="OpenSSF Scorecard" /></a>
19
+ <a href="https://github.com/sebastienrousseau/crypto-service/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0%20OR%20MIT-blue.svg?style=for-the-badge" alt="License: Apache-2.0 OR MIT" /></a>
20
+ <a href="https://github.com/sebastienrousseau/crypto-service/blob/main/docs/POLICIES.md"><img src="https://img.shields.io/badge/Node.js-%3E%3D22-93450a.svg?style=for-the-badge&logo=node.js" alt="Node.js 22 or newer" /></a>
21
+ </p>
22
+
23
+ ---
24
+
25
+ ## Contents
26
+
27
+ **Getting started**
28
+
29
+ - [Install](#install) — installation via pnpm, npm, or yarn
30
+ - [Requirements](#requirements) — runtime floor and environment prerequisites
31
+ - [Quick Start](#quick-start) — minimal working usage sample
32
+
33
+ **The Crypto Service ecosystem**
34
+
35
+ - [The Crypto Service ecosystem](#the-crypto-service-ecosystem) — full 14-package suite overview
36
+
37
+ **Package reference**
38
+
39
+ - [Reference & Usage](#overview) — features, configuration, and capabilities
40
+ - [Examples](#examples) — runnable sample code
41
+
42
+ **Operational**
43
+
44
+ - [Development](#development) — build, lint, format, and test targets
45
+ - [Security](#security) — vulnerability disclosure and cryptographic invariants
46
+ - [Documentation](#documentation) — TypeDoc API docs and ecosystem guides
47
+ - [Stability guarantees](#stability-guarantees) — SemVer axis and release policy
48
+ - [License](#license)
49
+
50
+ ---
51
+
52
+ ## Install
53
+
54
+ ```bash
55
+ pnpm add @sebastienrousseau/crypto-testing
56
+ # or
57
+ npm install @sebastienrousseau/crypto-testing
58
+ # or
59
+ yarn add @sebastienrousseau/crypto-testing
60
+ ```
61
+
62
+ <p align="right"><a href="#contents">Back to Top</a></p>
63
+
64
+ ---
65
+
66
+ ## Requirements
67
+
68
+ - **Node.js**: `^22.0.0` or `>=24.0.0` (active and maintenance LTS releases)
69
+ - **Package Manager**: `pnpm >=9` (recommended) or `npm >=10`
70
+ - **TypeScript**: `>=5.0` (when compiling with TypeScript)
71
+
72
+ <p align="right"><a href="#contents">Back to Top</a></p>
73
+
74
+ ---
75
+
76
+ ## Quick Start
77
+
78
+ ```ts
79
+ import {
80
+ TEST_KEYS,
81
+ TEST_VECTORS,
82
+ mockEncrypt,
83
+ mockDecrypt,
84
+ createTestKeyring,
85
+ expectValidHex,
86
+ expectSignVerifyRoundTrip,
87
+ } from "@sebastienrousseau/crypto-testing";
88
+
89
+ // Use deterministic keys instead of generating new ones every run
90
+ const { publicKey, privateKey } = TEST_KEYS.ed25519;
91
+
92
+ // Mock encrypt/decrypt for fast unit tests
93
+ const ct = mockEncrypt(TEST_KEYS.aes256, "secret data");
94
+ const pt = mockDecrypt(TEST_KEYS.aes256, ct);
95
+
96
+ // Validate outputs
97
+ expectValidHex(publicKey, 32);
98
+
99
+ // Full sign/verify round-trip with real crypto-lib
100
+ expectSignVerifyRoundTrip("ed25519");
101
+ ```
102
+
103
+ <p align="right"><a href="#contents">Back to Top</a></p>
104
+
105
+ ---
106
+
107
+ ## The Crypto Service ecosystem
108
+
109
+ Crypto Service provides a complete cryptography stack across 14 specialized packages:
110
+
111
+ | Package | Role | Description |
112
+ | :---------------------------------------------------------------------------- | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------- |
113
+ | [`@sebastienrousseau/crypto-api`](../crypto-api) | API Schemas | Shared TypeScript types and utilities for the Crypto Service Suite, defining the canonical API surface. |
114
+ | [`@sebastienrousseau/crypto-cli`](../crypto-cli) | Terminal CLI | An interactive command-line interface for cryptographic operations, supporting both legacy OpenPGP and modern post-quantum algorithms. |
115
+ | [`@sebastienrousseau/crypto-edge`](../crypto-edge) | Edge Runtime | Edge-runtime cryptographic operations using the Web Crypto API, optimized for Cloudflare Workers, Vercel Edge, and Deno. |
116
+ | [`@sebastienrousseau/crypto-kms`](../crypto-kms) | Cloud KMS | Unified Key Management Service interface for AWS KMS, GCP Cloud KMS, Azure Key Vault, and HashiCorp Vault. |
117
+ | [`@sebastienrousseau/crypto-lib`](../crypto-lib) | Core Library | A modern cryptographic library for TypeScript, with post-quantum support, zero unsafe dependencies, and 100% test coverage. |
118
+ | [`@sebastienrousseau/crypto-middleware`](../crypto-middleware) | Middleware | Framework-agnostic cryptographic middleware for Express, Fastify, and Koa applications. |
119
+ | [`@sebastienrousseau/crypto-prisma`](../crypto-prisma) | ORM Adapter | Transparent field-level encryption extension for Prisma Client, using XChaCha20-Poly1305. |
120
+ | [`@sebastienrousseau/crypto-react`](../crypto-react) | React Hooks | React hooks and context provider for client-side cryptographic operations with zero boilerplate. |
121
+ | [`@sebastienrousseau/crypto-sdk`](../crypto-sdk) | Client SDK | A zero-dependency, typed HTTP client for the Crypto Service REST API, with full post-quantum support. |
122
+ | [`@sebastienrousseau/crypto-server`](../crypto-server) | HTTP API | A hardened Fastify REST API for cryptographic operations, with rate limiting, OpenAPI schemas, and post-quantum endpoints. |
123
+ | **[`@sebastienrousseau/crypto-testing`](../crypto-testing)** _(this package)_ | **Test Support** | **Deterministic keys, fast mocks, and test fixtures for crypto-lib** |
124
+ | [`@sebastienrousseau/crypto-typeorm`](../crypto-typeorm) | ORM Adapter | TypeORM column-level encryption with a single decorator, powered by crypto-lib. |
125
+ | [`@sebastienrousseau/crypto-vue`](../crypto-vue) | Vue Composables | Vue 3 composables for client-side cryptography |
126
+ | [`@sebastienrousseau/crypto-wasm`](../crypto-wasm) | Acceleration | WebAssembly performance accelerator for crypto-lib |
127
+
128
+ <p align="right"><a href="#contents">Back to Top</a></p>
129
+
130
+ ---
131
+
132
+ ## Overview
133
+
134
+ crypto-testing is the test utility package for the Crypto Service
135
+ Suite. It provides pre-generated deterministic keys, instant
136
+ XOR-based mock functions for expensive crypto operations, one-call
137
+ fixture generators, and assertion helpers for hex, Base64, key-pair,
138
+ and round-trip validations. Use it to make your CI/CD pipeline fast
139
+ and reproducible without sacrificing coverage.
140
+
141
+ <p align="right"><a href="#contents">Back to Top</a></p>
142
+
143
+ ## Features
144
+
145
+ | Category | What you get |
146
+ | :------------- | :---------------------------------------------------------------------------- |
147
+ | **Keys** | Pre-generated Ed25519, X25519, P-256, AES-256, and HMAC key pairs/keys |
148
+ | **Vectors** | Known plaintext with expected SHA-256, SHA3-256, and BLAKE3 digests |
149
+ | **Mocks** | Instant XOR-based fakes for encrypt, decrypt, sign, verify, password hashing |
150
+ | **Fixtures** | One-call generators for keyrings, encrypted messages, signed messages, hashes |
151
+ | **Assertions** | Hex, Base64, key-pair, encrypt/decrypt, and sign/verify round-trip helpers |
152
+
153
+ <p align="right"><a href="#contents">Back to Top</a></p>
154
+
155
+ ## Deterministic Keys
156
+
157
+ `TEST_KEYS` provides well-known key pairs that never change between
158
+ runs:
159
+
160
+ | Key | Algorithm | Description |
161
+ | :-------- | :-------- | :------------------------------- |
162
+ | `ed25519` | Ed25519 | 32-byte signing key pair |
163
+ | `x25519` | X25519 | 32-byte key-exchange pair |
164
+ | `p256` | P-256 | ECDSA signing key pair |
165
+ | `aes256` | AES-256 | 32-byte symmetric encryption key |
166
+ | `hmacKey` | HMAC | 32-byte HMAC key |
167
+
168
+ `TEST_VECTORS` includes a known plaintext and its expected hashes.
169
+
170
+ <p align="right"><a href="#contents">Back to Top</a></p>
171
+
172
+ ## Mock Functions
173
+
174
+ Replace expensive crypto operations with instant, deterministic
175
+ fakes:
176
+
177
+ | Function | Replaces | Speed |
178
+ | :-------------------- | :----------------------- | :-------- |
179
+ | `mockHashPassword` | Argon2id (100+ ms) | < 0.01 ms |
180
+ | `mockGenerateKeyPair` | Real key generation | < 0.01 ms |
181
+ | `mockEncrypt` | XChaCha20-Poly1305 | < 0.01 ms |
182
+ | `mockDecrypt` | XChaCha20-Poly1305 | < 0.01 ms |
183
+ | `mockSign` | Ed25519/ECDSA signatures | < 0.01 ms |
184
+ | `mockVerify` | Signature verification | < 0.01 ms |
185
+
186
+ All mock functions use XOR internally -- they are **not
187
+ cryptographically secure** but are deterministic and round-trip
188
+ correctly.
189
+
190
+ <p align="right"><a href="#contents">Back to Top</a></p>
191
+
192
+ ## Fixtures
193
+
194
+ Fixture generators produce complete test data structures in one
195
+ call:
196
+
197
+ | Function | Returns |
198
+ | :----------------------------- | :-------------------------------------------------------------- |
199
+ | `createTestKeyring()` | Keyring with signing, exchange, ECDSA, symmetric, and HMAC keys |
200
+ | `createTestEncryptedMessage()` | Key + plaintext + mock ciphertext |
201
+ | `createTestSignedMessage()` | Key pair + message + mock signature |
202
+ | `createTestPasswordHash()` | Hash + salt + params + PHC string |
203
+
204
+ <p align="right"><a href="#contents">Back to Top</a></p>
205
+
206
+ ## Assertion Helpers
207
+
208
+ One-liner assertions that throw descriptive errors on failure:
209
+
210
+ | Helper | Checks |
211
+ | :------------------------------ | :------------------------------------- |
212
+ | `expectValidHex(value, len?)` | Valid hex string, optional byte length |
213
+ | `expectValidBase64(value)` | Valid Base64 string with round-trip |
214
+ | `expectKeyPair(kp)` | Non-empty hex keys, pub != priv |
215
+ | `expectEncryptDecryptRoundTrip` | Real secretbox encrypt then decrypt |
216
+ | `expectSignVerifyRoundTrip` | Real keygen, sign, and verify |
217
+
218
+ <p align="right"><a href="#contents">Back to Top</a></p>
219
+
220
+ ## Examples
221
+
222
+ All examples are self-contained TypeScript files in the `examples/`
223
+ directory. Run any example with:
224
+
225
+ ```bash
226
+ npx ts-node examples/<name>.ts
227
+ ```
228
+
229
+ | Category | Example | Purpose |
230
+ | :--------- | :-------------------------------------- | :---------------------------------- |
231
+ | Keys | [keys.ts](examples/keys.ts) | Using deterministic test keys |
232
+ | Mocks | [mocks.ts](examples/mocks.ts) | Mocking crypto operations for speed |
233
+ | Fixtures | [fixtures.ts](examples/fixtures.ts) | Using pre-built test fixtures |
234
+ | Assertions | [assertions.ts](examples/assertions.ts) | Assertion helpers in tests |
235
+
236
+ <p align="right"><a href="#contents">Back to Top</a></p>
237
+
238
+ <p align="right"><a href="#contents">Back to Top</a></p>
239
+
240
+ ---
241
+
242
+ ## Development
243
+
244
+ ```bash
245
+ pnpm --filter @sebastienrousseau/crypto-testing run build
246
+ pnpm --filter @sebastienrousseau/crypto-testing run test
247
+ pnpm --filter @sebastienrousseau/crypto-testing run lint
248
+ pnpm --filter @sebastienrousseau/crypto-testing run format
249
+ ```
250
+
251
+ All 18 packages in the Crypto Service workspace maintain a **100% coverage floor** across statements, branches, functions, and lines.
252
+
253
+ <p align="right"><a href="#contents">Back to Top</a></p>
254
+
255
+ ---
256
+
257
+ ## Security
258
+
259
+ Report vulnerabilities privately via [GitHub Security Advisories](https://github.com/sebastienrousseau/crypto-service/security/advisories) or according to [`SECURITY.md`](../../SECURITY.md). Never report security issues publicly.
260
+
261
+ Cryptographic operations use the `@noble/*` libraries, Node.js `crypto` and OpenPGP.js. `@noble/post-quantum` has not been independently audited and does not guarantee constant-time execution, and no module in this suite is FIPS 140-3 validated. Key zeroization is limited: JavaScript strings and garbage-collected buffers cannot be reliably wiped. See [`SECURITY.md`](../../SECURITY.md).
262
+
263
+ <p align="right"><a href="#contents">Back to Top</a></p>
264
+
265
+ ---
266
+
267
+ ## Documentation
268
+
269
+ - [Full Suite Documentation](https://sebastienrousseau.github.io/crypto-service/)
270
+ - [API Reference (TypeDoc)](https://sebastienrousseau.github.io/crypto-service/)
271
+ - [Developer Guide](../../DEVELOPMENT.md)
272
+ - [Security Policy](../../SECURITY.md)
273
+ - [Architecture & Design](../../ARCHITECTURE.md)
274
+
275
+ <p align="right"><a href="#contents">Back to Top</a></p>
276
+
277
+ ---
278
+
279
+ ## Stability guarantees
280
+
281
+ Versions advance strictly one step at a time on the `0.0.x` line (`v0.0.1` → `v0.0.2` → `v0.0.3` ... → `v0.0.999` → `v0.1.0`). Work for every release iteration begins on a dedicated `feat/v<version>` branch.
282
+
283
+ All 18 packages in the workspace move in lockstep. Public API signatures, cipher output formats, and serialization schemas are strictly versioned. Breaking changes to serialized formats or algorithm defaults are considered major breaking changes. Minimum toolchain upgrades (e.g. Node.js LTS floor) are governed by [POLICIES.md](../../docs/POLICIES.md).
284
+
285
+ <p align="right"><a href="#contents">Back to Top</a></p>
286
+
287
+ ---
288
+
289
+ ## License
290
+
291
+ Dual-licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) or [MIT](https://opensource.org/licenses/MIT), at your option.
292
+
293
+ Copyright (c) 2022-2026 Sebastien Rousseau and The Crypto Service Suite contributors.
294
+
295
+ <p align="right"><a href="#contents">Back to Top</a></p>
@@ -0,0 +1,26 @@
1
+ import { type MockKeyPair, type MockHashPasswordResult } from "./mock";
2
+ export interface TestKeyring {
3
+ signing: MockKeyPair;
4
+ exchange: MockKeyPair;
5
+ ecdsa: MockKeyPair;
6
+ symmetric: string;
7
+ hmac: string;
8
+ }
9
+ export declare function createTestKeyring(): TestKeyring;
10
+ export interface TestEncryptedMessage {
11
+ key: string;
12
+ plaintext: string;
13
+ ciphertext: string;
14
+ algorithm: "mock-xor";
15
+ }
16
+ export declare function createTestEncryptedMessage(plaintext?: string, key?: string): TestEncryptedMessage;
17
+ export interface TestSignedMessage {
18
+ algorithm: string;
19
+ publicKey: string;
20
+ privateKey: string;
21
+ message: string;
22
+ signature: string;
23
+ }
24
+ export declare function createTestSignedMessage(algorithm?: string, message?: string): TestSignedMessage;
25
+ export declare function createTestPasswordHash(password?: string): MockHashPasswordResult;
26
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../src/fixtures.ts"],"names":[],"mappings":"AAYA,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAC5B,MAAM,QAAQ,CAAC;AAiBhB,MAAM,WAAW,WAAW;IAE1B,OAAO,EAAE,WAAW,CAAC;IAErB,QAAQ,EAAE,WAAW,CAAC;IAEtB,KAAK,EAAE,WAAW,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;CACd;AAcD,wBAAgB,iBAAiB,IAAI,WAAW,CAQ/C;AAiBD,MAAM,WAAW,oBAAoB;IAEnC,GAAG,EAAE,MAAM,CAAC;IAEZ,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,EAAE,UAAU,CAAC;CACvB;AAiBD,wBAAgB,0BAA0B,CACxC,SAAS,CAAC,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,GACX,oBAAoB,CAStB;AAiBD,MAAM,WAAW,iBAAiB;IAEhC,SAAS,EAAE,MAAM,CAAC;IAElB,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,EAAE,MAAM,CAAC;IAEnB,OAAO,EAAE,MAAM,CAAC;IAEhB,SAAS,EAAE,MAAM,CAAC;CACnB;AAgBD,wBAAgB,uBAAuB,CACrC,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACf,iBAAiB,CAWnB;AAmBD,wBAAgB,sBAAsB,CACpC,QAAQ,CAAC,EAAE,MAAM,GAChB,sBAAsB,CAExB"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createTestKeyring = createTestKeyring;
4
+ exports.createTestEncryptedMessage = createTestEncryptedMessage;
5
+ exports.createTestSignedMessage = createTestSignedMessage;
6
+ exports.createTestPasswordHash = createTestPasswordHash;
7
+ const keys_1 = require("./keys");
8
+ const mock_1 = require("./mock");
9
+ function createTestKeyring() {
10
+ return {
11
+ signing: (0, mock_1.mockGenerateKeyPair)("ed25519"),
12
+ exchange: (0, mock_1.mockGenerateKeyPair)("x25519"),
13
+ ecdsa: (0, mock_1.mockGenerateKeyPair)("p256"),
14
+ symmetric: keys_1.TEST_KEYS.aes256,
15
+ hmac: keys_1.TEST_KEYS.hmacKey,
16
+ };
17
+ }
18
+ function createTestEncryptedMessage(plaintext, key) {
19
+ const pt = plaintext ?? keys_1.TEST_VECTORS.plaintext;
20
+ const k = key ?? keys_1.TEST_KEYS.aes256;
21
+ return {
22
+ key: k,
23
+ plaintext: pt,
24
+ ciphertext: (0, mock_1.mockEncrypt)(k, pt),
25
+ algorithm: "mock-xor",
26
+ };
27
+ }
28
+ function createTestSignedMessage(algorithm, message) {
29
+ const alg = algorithm ?? "ed25519";
30
+ const msg = message ?? keys_1.TEST_VECTORS.plaintext;
31
+ const kp = (0, mock_1.mockGenerateKeyPair)(alg);
32
+ return {
33
+ algorithm: alg,
34
+ publicKey: kp.publicKey,
35
+ privateKey: kp.privateKey,
36
+ message: msg,
37
+ signature: (0, mock_1.mockSign)(kp.privateKey, msg),
38
+ };
39
+ }
40
+ function createTestPasswordHash(password) {
41
+ return (0, mock_1.mockHashPassword)(password ?? "test-password-123");
42
+ }
43
+ //# sourceMappingURL=fixtures.js.map
@@ -0,0 +1,12 @@
1
+ import { type SignAlgorithm } from "@sebastienrousseau/crypto-lib";
2
+ export declare function expectValidHex(value: string, length?: number): void;
3
+ export declare function expectValidBase64(value: string): void;
4
+ export interface MinimalKeyPair {
5
+ publicKey: string;
6
+ privateKey: string;
7
+ algorithm?: string;
8
+ }
9
+ export declare function expectKeyPair(kp: MinimalKeyPair): void;
10
+ export declare function expectEncryptDecryptRoundTrip(key: string, plaintext: string): void;
11
+ export declare function expectSignVerifyRoundTrip(algorithm: SignAlgorithm): void;
12
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAUA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAyB3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAsBnE;AAsBD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAYrD;AAgBD,MAAM,WAAW,cAAc;IAE7B,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAmBD,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI,CAStD;AAqBD,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAChB,IAAI,CASN;AAsBD,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,aAAa,GAAG,IAAI,CAgCxE"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expectValidHex = expectValidHex;
4
+ exports.expectValidBase64 = expectValidBase64;
5
+ exports.expectKeyPair = expectKeyPair;
6
+ exports.expectEncryptDecryptRoundTrip = expectEncryptDecryptRoundTrip;
7
+ exports.expectSignVerifyRoundTrip = expectSignVerifyRoundTrip;
8
+ const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
9
+ const HEX_RE = /^[0-9a-fA-F]+$/;
10
+ function expectValidHex(value, length) {
11
+ if (typeof value !== "string" || value.length === 0) {
12
+ throw new Error("Expected a non-empty hex string");
13
+ }
14
+ if (value.length % 2 !== 0) {
15
+ throw new Error(`Hex string has odd length (${value.length}); must be even`);
16
+ }
17
+ if (!HEX_RE.test(value)) {
18
+ throw new Error(`String is not valid hexadecimal: "${value.slice(0, 20)}..."`);
19
+ }
20
+ if (length !== undefined) {
21
+ const byteLen = value.length / 2;
22
+ if (byteLen !== length) {
23
+ throw new Error(`Expected ${length} bytes (${length * 2} hex chars), got ${byteLen} bytes (${value.length} hex chars)`);
24
+ }
25
+ }
26
+ }
27
+ const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
28
+ function expectValidBase64(value) {
29
+ if (typeof value !== "string" || value.length === 0) {
30
+ throw new Error("Expected a non-empty Base64 string");
31
+ }
32
+ if (!BASE64_RE.test(value)) {
33
+ throw new Error(`String is not valid Base64: "${value.slice(0, 20)}..."`);
34
+ }
35
+ const buf = Buffer.from(value, "base64");
36
+ if (buf.toString("base64") !== value) {
37
+ throw new Error("Base64 string does not survive round-trip decode/encode");
38
+ }
39
+ }
40
+ function expectKeyPair(kp) {
41
+ if (!kp || typeof kp !== "object") {
42
+ throw new Error("Expected a key pair object");
43
+ }
44
+ expectValidHex(kp.publicKey);
45
+ expectValidHex(kp.privateKey);
46
+ if (kp.publicKey === kp.privateKey) {
47
+ throw new Error("Public key and private key must not be identical");
48
+ }
49
+ }
50
+ function expectEncryptDecryptRoundTrip(key, plaintext) {
51
+ const sealed = crypto_lib_1.crypto.encrypt(key, plaintext);
52
+ const decrypted = crypto_lib_1.crypto.decrypt(key, sealed);
53
+ const result = Buffer.from(decrypted).toString("utf8");
54
+ if (result !== plaintext) {
55
+ throw new Error(`Round-trip failed: expected "${plaintext}", got "${result}"`);
56
+ }
57
+ }
58
+ function expectSignVerifyRoundTrip(algorithm) {
59
+ const keyAlgorithmMap = {
60
+ ed25519: "ed25519",
61
+ ed448: "ed448",
62
+ "ecdsa-p256": "p256",
63
+ "ecdsa-p384": "p384",
64
+ schnorr: "ed25519",
65
+ "ml-dsa-44": "ml-dsa-44",
66
+ "ml-dsa-65": "ml-dsa-65",
67
+ "ml-dsa-87": "ml-dsa-87",
68
+ };
69
+ const keyAlg = keyAlgorithmMap[algorithm];
70
+ if (!keyAlg) {
71
+ throw new Error(`No key algorithm mapping for sign algorithm "${algorithm}"`);
72
+ }
73
+ const kp = crypto_lib_1.crypto.generateKeyPair(keyAlg);
74
+ const message = "crypto-testing round-trip verification";
75
+ const signature = crypto_lib_1.crypto.sign(algorithm, kp.privateKey, message);
76
+ expectValidHex(signature);
77
+ const valid = crypto_lib_1.crypto.verify(algorithm, kp.publicKey, message, signature);
78
+ if (!valid) {
79
+ throw new Error(`Sign/verify round-trip failed for algorithm "${algorithm}"`);
80
+ }
81
+ }
82
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1,5 @@
1
+ export { TEST_KEYS, TEST_VECTORS } from "./keys";
2
+ export { mockHashPassword, mockGenerateKeyPair, mockEncrypt, mockDecrypt, mockSign, mockVerify, } from "./mock";
3
+ export { createTestKeyring, createTestEncryptedMessage, createTestSignedMessage, createTestPasswordHash, } from "./fixtures";
4
+ export { expectValidHex, expectValidBase64, expectKeyPair, expectEncryptDecryptRoundTrip, expectSignVerifyRoundTrip, } from "./helpers";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGjD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,QAAQ,EACR,UAAU,GACX,MAAM,QAAQ,CAAC;AAGhB,OAAO,EACL,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,6BAA6B,EAC7B,yBAAyB,GAC1B,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expectSignVerifyRoundTrip = exports.expectEncryptDecryptRoundTrip = exports.expectKeyPair = exports.expectValidBase64 = exports.expectValidHex = exports.createTestPasswordHash = exports.createTestSignedMessage = exports.createTestEncryptedMessage = exports.createTestKeyring = exports.mockVerify = exports.mockSign = exports.mockDecrypt = exports.mockEncrypt = exports.mockGenerateKeyPair = exports.mockHashPassword = exports.TEST_VECTORS = exports.TEST_KEYS = void 0;
4
+ var keys_1 = require("./keys");
5
+ Object.defineProperty(exports, "TEST_KEYS", { enumerable: true, get: function () { return keys_1.TEST_KEYS; } });
6
+ Object.defineProperty(exports, "TEST_VECTORS", { enumerable: true, get: function () { return keys_1.TEST_VECTORS; } });
7
+ var mock_1 = require("./mock");
8
+ Object.defineProperty(exports, "mockHashPassword", { enumerable: true, get: function () { return mock_1.mockHashPassword; } });
9
+ Object.defineProperty(exports, "mockGenerateKeyPair", { enumerable: true, get: function () { return mock_1.mockGenerateKeyPair; } });
10
+ Object.defineProperty(exports, "mockEncrypt", { enumerable: true, get: function () { return mock_1.mockEncrypt; } });
11
+ Object.defineProperty(exports, "mockDecrypt", { enumerable: true, get: function () { return mock_1.mockDecrypt; } });
12
+ Object.defineProperty(exports, "mockSign", { enumerable: true, get: function () { return mock_1.mockSign; } });
13
+ Object.defineProperty(exports, "mockVerify", { enumerable: true, get: function () { return mock_1.mockVerify; } });
14
+ var fixtures_1 = require("./fixtures");
15
+ Object.defineProperty(exports, "createTestKeyring", { enumerable: true, get: function () { return fixtures_1.createTestKeyring; } });
16
+ Object.defineProperty(exports, "createTestEncryptedMessage", { enumerable: true, get: function () { return fixtures_1.createTestEncryptedMessage; } });
17
+ Object.defineProperty(exports, "createTestSignedMessage", { enumerable: true, get: function () { return fixtures_1.createTestSignedMessage; } });
18
+ Object.defineProperty(exports, "createTestPasswordHash", { enumerable: true, get: function () { return fixtures_1.createTestPasswordHash; } });
19
+ var helpers_1 = require("./helpers");
20
+ Object.defineProperty(exports, "expectValidHex", { enumerable: true, get: function () { return helpers_1.expectValidHex; } });
21
+ Object.defineProperty(exports, "expectValidBase64", { enumerable: true, get: function () { return helpers_1.expectValidBase64; } });
22
+ Object.defineProperty(exports, "expectKeyPair", { enumerable: true, get: function () { return helpers_1.expectKeyPair; } });
23
+ Object.defineProperty(exports, "expectEncryptDecryptRoundTrip", { enumerable: true, get: function () { return helpers_1.expectEncryptDecryptRoundTrip; } });
24
+ Object.defineProperty(exports, "expectSignVerifyRoundTrip", { enumerable: true, get: function () { return helpers_1.expectSignVerifyRoundTrip; } });
25
+ //# sourceMappingURL=index.js.map
package/dist/keys.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ export declare const TEST_KEYS: {
2
+ readonly ed25519: {
3
+ readonly publicKey: "d75a980182b10ab7d54bfed3c964073a0ee172f3daa3f4a18446b7e8c38f1dd5";
4
+ readonly privateKey: "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60";
5
+ };
6
+ readonly x25519: {
7
+ readonly publicKey: "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
8
+ readonly privateKey: "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
9
+ };
10
+ readonly p256: {
11
+ readonly publicKey: "0437c1b9e44bcb3d97a78b8b5e71f5e3e5a4a0e7d6b8c2f1e0d9c8b7a6f5e4d3c2b1a09f8e7d6c5b4a3029f8e7d6c5b4a3029f8e7d6c5b4a3029f8e7d6c5b4a302";
12
+ readonly privateKey: "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721";
13
+ };
14
+ readonly aes256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
15
+ readonly hmacKey: "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe";
16
+ };
17
+ export declare const TEST_VECTORS: {
18
+ readonly plaintext: "The quick brown fox jumps over the lazy dog";
19
+ readonly sha256: "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592";
20
+ readonly sha3_256: "a80f839cd4f83f6c3dafc87feae470045e4eb0d366397d5c6ce34ba1739f734d";
21
+ readonly blake3: "8e7e26d7e72a04d2ed173f90a72ec2a3bf3bb38890c9a2f95f3ab0e18a1e1a4a";
22
+ };
23
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;CAgCZ,CAAC;AAaX,eAAO,MAAM,YAAY;;;;;CASf,CAAC"}
package/dist/keys.js ADDED
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TEST_VECTORS = exports.TEST_KEYS = void 0;
4
+ exports.TEST_KEYS = {
5
+ ed25519: {
6
+ publicKey: "d75a980182b10ab7d54bfed3c964073a0ee172f3daa3f4a18446b7e8c38f1dd5",
7
+ privateKey: "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
8
+ },
9
+ x25519: {
10
+ publicKey: "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
11
+ privateKey: "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
12
+ },
13
+ p256: {
14
+ publicKey: "0437c1b9e44bcb3d97a78b8b5e71f5e3e5a4a0e7d6b8c2f1e0d9c8b7a6f5e4d3c2b1a09f8e7d6c5b4a3029f8e7d6c5b4a3029f8e7d6c5b4a3029f8e7d6c5b4a302",
15
+ privateKey: "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
16
+ },
17
+ aes256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
18
+ hmacKey: "cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe",
19
+ };
20
+ exports.TEST_VECTORS = {
21
+ plaintext: "The quick brown fox jumps over the lazy dog",
22
+ sha256: "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592",
23
+ sha3_256: "a80f839cd4f83f6c3dafc87feae470045e4eb0d366397d5c6ce34ba1739f734d",
24
+ blake3: "8e7e26d7e72a04d2ed173f90a72ec2a3bf3bb38890c9a2f95f3ab0e18a1e1a4a",
25
+ };
26
+ //# sourceMappingURL=keys.js.map
package/dist/mock.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ export interface MockHashPasswordResult {
2
+ hash: string;
3
+ salt: string;
4
+ params: {
5
+ t: number;
6
+ m: number;
7
+ p: number;
8
+ };
9
+ algorithm: "mock-argon2id";
10
+ phc: string;
11
+ }
12
+ export declare function mockHashPassword(password: string | Uint8Array): MockHashPasswordResult;
13
+ export interface MockKeyPair {
14
+ publicKey: string;
15
+ privateKey: string;
16
+ algorithm: string;
17
+ kid: string;
18
+ }
19
+ export declare function mockGenerateKeyPair(algorithm?: string): MockKeyPair;
20
+ export declare function mockEncrypt(key: string, plaintext: string | Uint8Array): string;
21
+ export declare function mockDecrypt(key: string, ciphertext: string): Uint8Array;
22
+ export declare function mockSign(privateKey: string, message: string | Uint8Array): string;
23
+ export declare function mockVerify(_publicKey: string, message: string | Uint8Array, signature: string, privateKey: string): boolean;
24
+ //# sourceMappingURL=mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"AAwDA,MAAM,WAAW,sBAAsB;IAErC,IAAI,EAAE,MAAM,CAAC;IAEb,IAAI,EAAE,MAAM,CAAC;IAEb,MAAM,EAAE;QAEN,CAAC,EAAE,MAAM,CAAC;QAEV,CAAC,EAAE,MAAM,CAAC;QAEV,CAAC,EAAE,MAAM,CAAC;KACX,CAAC;IAEF,SAAS,EAAE,eAAe,CAAC;IAE3B,GAAG,EAAE,MAAM,CAAC;CACb;AAkBD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,UAAU,GAC5B,sBAAsB,CAaxB;AAiBD,MAAM,WAAW,WAAW;IAE1B,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;IAElB,GAAG,EAAE,MAAM,CAAC;CACb;AAiBD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,MAAkB,GAC5B,WAAW,CA+Bb;AAqBD,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAAG,UAAU,GAC7B,MAAM,CAIR;AAiBD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAIvE;AAuBD,wBAAgB,QAAQ,CACtB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,UAAU,GAC3B,MAAM,CAIR;AAsBD,wBAAgB,UAAU,CACxB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,UAAU,EAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAGT"}
package/dist/mock.js ADDED
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mockHashPassword = mockHashPassword;
4
+ exports.mockGenerateKeyPair = mockGenerateKeyPair;
5
+ exports.mockEncrypt = mockEncrypt;
6
+ exports.mockDecrypt = mockDecrypt;
7
+ exports.mockSign = mockSign;
8
+ exports.mockVerify = mockVerify;
9
+ const keys_1 = require("./keys");
10
+ function xorCipher(data, key) {
11
+ const out = new Uint8Array(data.length);
12
+ for (let i = 0; i < data.length; i++) {
13
+ out[i] = data[i] ^ key[i % key.length];
14
+ }
15
+ return out;
16
+ }
17
+ function toBytes(input) {
18
+ return input instanceof Uint8Array ? input : Buffer.from(input, "utf8");
19
+ }
20
+ function toHex(bytes) {
21
+ return Buffer.from(bytes).toString("hex");
22
+ }
23
+ function hexToBytes(hex) {
24
+ return Buffer.from(hex, "hex");
25
+ }
26
+ function mockHashPassword(password) {
27
+ const pw = toBytes(password);
28
+ const salt = hexToBytes("00".repeat(16));
29
+ const fakeHash = xorCipher(pw, salt);
30
+ const hashHex = toHex(fakeHash).padEnd(64, "0");
31
+ const saltHex = "00".repeat(16);
32
+ return {
33
+ hash: hashHex,
34
+ salt: saltHex,
35
+ params: { t: 1, m: 1024, p: 1 },
36
+ algorithm: "mock-argon2id",
37
+ phc: `$mock-argon2id$v=19$m=1024,t=1,p=1$${Buffer.from(salt).toString("base64")}$${Buffer.from(hexToBytes(hashHex)).toString("base64")}`,
38
+ };
39
+ }
40
+ function mockGenerateKeyPair(algorithm = "ed25519") {
41
+ switch (algorithm) {
42
+ case "ed25519":
43
+ return {
44
+ publicKey: keys_1.TEST_KEYS.ed25519.publicKey,
45
+ privateKey: keys_1.TEST_KEYS.ed25519.privateKey,
46
+ algorithm,
47
+ kid: "mock-ed25519-kid",
48
+ };
49
+ case "x25519":
50
+ return {
51
+ publicKey: keys_1.TEST_KEYS.x25519.publicKey,
52
+ privateKey: keys_1.TEST_KEYS.x25519.privateKey,
53
+ algorithm,
54
+ kid: "mock-x25519-kid",
55
+ };
56
+ case "p256":
57
+ return {
58
+ publicKey: keys_1.TEST_KEYS.p256.publicKey,
59
+ privateKey: keys_1.TEST_KEYS.p256.privateKey,
60
+ algorithm,
61
+ kid: "mock-p256-kid",
62
+ };
63
+ default:
64
+ return {
65
+ publicKey: "aa".repeat(32),
66
+ privateKey: "bb".repeat(32),
67
+ algorithm,
68
+ kid: `mock-${algorithm}-kid`,
69
+ };
70
+ }
71
+ }
72
+ function mockEncrypt(key, plaintext) {
73
+ const keyBytes = hexToBytes(key);
74
+ const ptBytes = toBytes(plaintext);
75
+ return toHex(xorCipher(ptBytes, keyBytes));
76
+ }
77
+ function mockDecrypt(key, ciphertext) {
78
+ const keyBytes = hexToBytes(key);
79
+ const ctBytes = hexToBytes(ciphertext);
80
+ return xorCipher(ctBytes, keyBytes);
81
+ }
82
+ function mockSign(privateKey, message) {
83
+ const keyBytes = hexToBytes(privateKey);
84
+ const msgBytes = toBytes(message);
85
+ return toHex(xorCipher(msgBytes, keyBytes));
86
+ }
87
+ function mockVerify(_publicKey, message, signature, privateKey) {
88
+ const expected = mockSign(privateKey, message);
89
+ return expected === signature;
90
+ }
91
+ //# sourceMappingURL=mock.js.map
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@sebastienrousseau/crypto-testing",
3
+ "version": "0.0.7",
4
+ "description": "Deterministic keys, fast mocks, and test fixtures for crypto-lib — make your CI/CD pipeline fast and reproducible.",
5
+ "author": "Sebastien Rousseau <sebastienrousseau@users.noreply.github.com>",
6
+ "license": "MIT OR Apache-2.0",
7
+ "homepage": "https://github.com/sebastienrousseau/crypto-service/tree/main/packages/crypto-testing",
8
+ "repository": {
9
+ "directory": "packages/crypto-testing",
10
+ "type": "git",
11
+ "url": "git@github.com:sebastienrousseau/crypto-service.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/sebastienrousseau/crypto-service/issues"
15
+ },
16
+ "engines": {
17
+ "node": ">=22.0.0"
18
+ },
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "sideEffects": false,
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "require": "./dist/index.js",
26
+ "import": "./dist/index.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist/**/*.js",
31
+ "dist/**/*.d.ts",
32
+ "dist/**/*.d.ts.map"
33
+ ],
34
+ "dependencies": {
35
+ "@sebastienrousseau/crypto-lib": "0.0.7"
36
+ },
37
+ "devDependencies": {
38
+ "@sebastienrousseau/mocha-config": "^0.0.5",
39
+ "@types/chai": "^4.3.7",
40
+ "@types/chai-as-promised": "^8.0.2",
41
+ "@types/mocha": "^10.0.2",
42
+ "@types/node": "^26.4.1",
43
+ "c8": "^12.0.0",
44
+ "chai": "^4.5.0",
45
+ "chai-as-promised": "^7.1.2",
46
+ "mocha": "^10.8.2",
47
+ "rimraf": "^6.1.3",
48
+ "ts-node": "^10.9.2",
49
+ "typedoc": "^0.28.20",
50
+ "typescript": "~5.9.3"
51
+ },
52
+ "keywords": [
53
+ "crypto-testing",
54
+ "test-fixtures",
55
+ "deterministic-keys",
56
+ "mock-crypto",
57
+ "crypto-lib"
58
+ ],
59
+ "publishConfig": {
60
+ "access": "public"
61
+ },
62
+ "private": false,
63
+ "scripts": {
64
+ "build": "tsc --build tsconfig.json",
65
+ "clean": "rimraf ./coverage ./dist ./docs",
66
+ "test": "c8 --check-coverage --lines 100 --branches 100 --functions 100 --reporter=lcov --reporter=text mocha --config=.mocharc.cjs",
67
+ "docs": "typedoc",
68
+ "lint": "eslint --ext .ts src __tests__",
69
+ "lint:fix": "eslint --cache --fix --ext .ts src __tests__",
70
+ "format": "prettier --write src/**/*.ts",
71
+ "format:check": "prettier --check \"src/**/*.ts\""
72
+ }
73
+ }