leviathan-crypto 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CLAUDE.md +265 -0
  2. package/LICENSE +21 -0
  3. package/README.md +322 -0
  4. package/SECURITY.md +174 -0
  5. package/dist/chacha.wasm +0 -0
  6. package/dist/chacha20/index.d.ts +49 -0
  7. package/dist/chacha20/index.js +177 -0
  8. package/dist/chacha20/ops.d.ts +16 -0
  9. package/dist/chacha20/ops.js +146 -0
  10. package/dist/chacha20/pool.d.ts +52 -0
  11. package/dist/chacha20/pool.js +188 -0
  12. package/dist/chacha20/pool.worker.d.ts +1 -0
  13. package/dist/chacha20/pool.worker.js +37 -0
  14. package/dist/chacha20/types.d.ts +30 -0
  15. package/dist/chacha20/types.js +1 -0
  16. package/dist/docs/architecture.md +795 -0
  17. package/dist/docs/argon2id.md +290 -0
  18. package/dist/docs/chacha20.md +602 -0
  19. package/dist/docs/chacha20_pool.md +306 -0
  20. package/dist/docs/fortuna.md +322 -0
  21. package/dist/docs/init.md +308 -0
  22. package/dist/docs/loader.md +206 -0
  23. package/dist/docs/serpent.md +914 -0
  24. package/dist/docs/sha2.md +620 -0
  25. package/dist/docs/sha3.md +509 -0
  26. package/dist/docs/types.md +198 -0
  27. package/dist/docs/utils.md +273 -0
  28. package/dist/docs/wasm.md +193 -0
  29. package/dist/embedded/chacha.d.ts +1 -0
  30. package/dist/embedded/chacha.js +2 -0
  31. package/dist/embedded/serpent.d.ts +1 -0
  32. package/dist/embedded/serpent.js +2 -0
  33. package/dist/embedded/sha2.d.ts +1 -0
  34. package/dist/embedded/sha2.js +2 -0
  35. package/dist/embedded/sha3.d.ts +1 -0
  36. package/dist/embedded/sha3.js +2 -0
  37. package/dist/fortuna.d.ts +72 -0
  38. package/dist/fortuna.js +445 -0
  39. package/dist/index.d.ts +13 -0
  40. package/dist/index.js +44 -0
  41. package/dist/init.d.ts +11 -0
  42. package/dist/init.js +49 -0
  43. package/dist/loader.d.ts +4 -0
  44. package/dist/loader.js +30 -0
  45. package/dist/serpent/index.d.ts +65 -0
  46. package/dist/serpent/index.js +242 -0
  47. package/dist/serpent/seal.d.ts +8 -0
  48. package/dist/serpent/seal.js +70 -0
  49. package/dist/serpent/stream-encoder.d.ts +20 -0
  50. package/dist/serpent/stream-encoder.js +167 -0
  51. package/dist/serpent/stream-pool.d.ts +48 -0
  52. package/dist/serpent/stream-pool.js +285 -0
  53. package/dist/serpent/stream-sealer.d.ts +34 -0
  54. package/dist/serpent/stream-sealer.js +223 -0
  55. package/dist/serpent/stream.d.ts +28 -0
  56. package/dist/serpent/stream.js +205 -0
  57. package/dist/serpent/stream.worker.d.ts +32 -0
  58. package/dist/serpent/stream.worker.js +117 -0
  59. package/dist/serpent/types.d.ts +5 -0
  60. package/dist/serpent/types.js +1 -0
  61. package/dist/serpent.wasm +0 -0
  62. package/dist/sha2/hkdf.d.ts +16 -0
  63. package/dist/sha2/hkdf.js +108 -0
  64. package/dist/sha2/index.d.ts +40 -0
  65. package/dist/sha2/index.js +190 -0
  66. package/dist/sha2/types.d.ts +5 -0
  67. package/dist/sha2/types.js +1 -0
  68. package/dist/sha2.wasm +0 -0
  69. package/dist/sha3/index.d.ts +55 -0
  70. package/dist/sha3/index.js +246 -0
  71. package/dist/sha3/types.d.ts +5 -0
  72. package/dist/sha3/types.js +1 -0
  73. package/dist/sha3.wasm +0 -0
  74. package/dist/types.d.ts +24 -0
  75. package/dist/types.js +26 -0
  76. package/dist/utils.d.ts +26 -0
  77. package/dist/utils.js +169 -0
  78. package/package.json +90 -0
@@ -0,0 +1,308 @@
1
+ # Module Initialization and WASM Loading
2
+
3
+ > [!NOTE]
4
+ > The `init()` function is the entry point for leviathan-crypto. You must call it
5
+ > before using any cryptographic class. It loads the WebAssembly modules that
6
+ > perform the actual cryptographic work, caches them in memory, and makes them
7
+ > available to all wrapper classes.
8
+
9
+ ## Overview
10
+
11
+ leviathan-crypto runs all cryptographic computation inside WebAssembly modules.
12
+ These modules are not available automatically, they need to be loaded and
13
+ compiled before any cryptographic class (`Serpent`, `SHA256`, `ChaCha20`, etc.)
14
+ can be used.
15
+
16
+ `init()` handles this process for you. You tell it which modules you need, and
17
+ it loads them. After that, every class backed by those modules is ready to use.
18
+
19
+ Key properties:
20
+
21
+ - **Required before use.** If you try to create a cryptographic class before
22
+ calling `init()`, you will get a clear error telling you what to do.
23
+ - **Three loading modes.** Embedded (default, zero-config), streaming
24
+ (better performance for large apps), and manual (full control over how
25
+ binaries are provided).
26
+ - **Idempotent.** Calling `init()` multiple times with the same module is
27
+ safe, it skips modules that are already loaded. This means you can call
28
+ `init()` in multiple places in your application without worrying about
29
+ redundant work.
30
+ - **Async.** `init()` returns a Promise. Use `await` or `.then()` before
31
+ proceeding.
32
+
33
+ ---
34
+
35
+ ## Security Notes
36
+
37
+ - **WASM runs outside the JavaScript JIT.** Cryptographic code executes in
38
+ WebAssembly, which provides more predictable execution timing compared to
39
+ optimized JavaScript. This reduces the risk of timing side-channels
40
+ introduced by the JIT compiler.
41
+ - **Independent memory per module.** Each WASM module gets its own linear
42
+ memory (3 pages, 192 KB). Key material loaded into one module's memory
43
+ cannot be read by another module. There is no shared memory between modules.
44
+ - **No silent auto-initialization.** Every wrapper class checks that its
45
+ backing module has been initialized. If it hasn't, the class throws an
46
+ error immediately rather than silently loading the module in the
47
+ background. This makes initialization explicit and auditable.
48
+
49
+ ---
50
+
51
+ ## API Reference
52
+
53
+ ### Types
54
+
55
+ ```typescript
56
+ type Module = 'serpent' | 'chacha20' | 'sha2' | 'sha3'
57
+ ```
58
+
59
+ The four WASM module families. Each one backs a group of related classes:
60
+
61
+ | Module | Classes it enables |
62
+ |-------------|----------------------------------------------------------------|
63
+ | `'serpent'` | `Serpent`, `SerpentCbc`, `SerpentCtr`, `SerpentSeal`, `SerpentStream`, `SerpentStreamPool`, `Fortuna` |
64
+ | `'chacha20'`| `ChaCha20`, `XChaCha20Poly1305` |
65
+ | `'sha2'` | `SHA256`, `SHA384`, `SHA512`, `HMAC` (SHA-2 based), `Fortuna` |
66
+ | `'sha3'` | `SHA3`, `SHAKE128`, `SHAKE256` |
67
+
68
+ ```typescript
69
+ type Mode = 'embedded' | 'streaming' | 'manual'
70
+ ```
71
+
72
+ How the WASM binary is loaded. See the [Usage Examples](#usage-examples) section
73
+ for when to use each mode.
74
+
75
+ ```typescript
76
+ interface InitOpts {
77
+ wasmUrl?: URL | string
78
+ wasmBinary?: Partial<Record<Module, Uint8Array | ArrayBuffer>>
79
+ }
80
+ ```
81
+
82
+ Optional configuration object. Which fields are required depends on the mode:
83
+
84
+ | Mode | Required fields | Description |
85
+ |-------------|--------------------|-------------------------------------------------|
86
+ | `embedded` | (none) | Binaries are bundled in the package |
87
+ | `streaming` | `wasmUrl` | Base URL where `.wasm` files are served |
88
+ | `manual` | `wasmBinary` | A map of module names to raw WASM binary data |
89
+
90
+ ---
91
+
92
+ ### Functions
93
+
94
+ #### `init(modules, mode?, opts?)` — public API (exported from root barrel)
95
+
96
+ > [!NOTE]
97
+ > `init()` is no longer exported from `init.ts`. It is defined in the
98
+ > root barrel (`src/ts/index.ts`) and dispatches to each module's own init
99
+ > function (`serpentInit`, `chacha20Init`, `sha2Init`, `sha3Init`).
100
+ > See [README.md](./README.md) for details.
101
+
102
+ The public `init()` signature is unchanged:
103
+
104
+ ```typescript
105
+ async function init(
106
+ modules: Module | Module[],
107
+ mode?: Mode, // default: 'embedded'
108
+ opts?: InitOpts,
109
+ ): Promise<void>
110
+ ```
111
+
112
+ ---
113
+
114
+ #### `initModule(mod, embeddedThunk, mode?, opts?)` — internal
115
+
116
+ ```typescript
117
+ async function initModule(
118
+ mod: Module,
119
+ embeddedThunk: () => Promise<string>,
120
+ mode?: Mode, // default: 'embedded'
121
+ opts?: InitOpts,
122
+ ): Promise<void>
123
+ ```
124
+
125
+ Internal initialization function. Called by each module's own init function
126
+ (`serpentInit`, `chacha20Init`, `sha2Init`, `sha3Init`), not by consumers
127
+ directly. Each module passes its own embedded thunk so the
128
+ dependency graph stays isolated per module, enabling tree-shaking.
129
+
130
+ **Parameters:**
131
+
132
+ - `mod`: The module name to initialize.
133
+ - `embeddedThunk`: A function that returns a Promise resolving to the
134
+ base64-encoded WASM binary. Each module defines its own thunk pointing to
135
+ its own embedded file.
136
+ - `mode`: The loading strategy. Defaults to `'embedded'`.
137
+ - `opts`: Configuration for `'streaming'` and `'manual'` modes.
138
+
139
+ **Returns:** A Promise that resolves when the module is loaded and cached.
140
+
141
+ **Idempotent:** If the module is already initialized, returns immediately.
142
+
143
+ **Throws:**
144
+
145
+ - `'leviathan-crypto: streaming mode requires wasmUrl'` if mode is
146
+ `'streaming'` and `opts.wasmUrl` is not provided.
147
+ - `'leviathan-crypto: manual mode requires wasmBinary['<mod>']'` if mode
148
+ is `'manual'` and the binary for the requested module is missing.
149
+ - `'leviathan-crypto: unknown mode '<mode>''` if an invalid mode string
150
+ is passed.
151
+
152
+ > [!NOTE]
153
+ > The previous design exported `init()` from `init.ts`,
154
+ > which contained a central `embeddedLoaders` record mapping every module name
155
+ > to its embedded import. This meant any consumer importing `init()`,
156
+ > even through a subpath like `leviathan-crypto/serpent`, pulled all four
157
+ > embedded binaries into the bundle. Moving `init()` to the root barrel and
158
+ > giving each module its own thunk isolates the dependency graph so bundlers
159
+ > can tree-shake unused modules, optimizing build size.
160
+
161
+ ---
162
+
163
+ #### `getInstance(mod)`
164
+
165
+ ```typescript
166
+ function getInstance(mod: Module): WebAssembly.Instance
167
+ ```
168
+
169
+ Returns the cached WebAssembly instance for a module. This is used internally
170
+ by wrapper classes, you do not normally need to call it yourself.
171
+
172
+ **Throws:**
173
+ `'leviathan-crypto: call init(['<mod>']) before using this class'` if the
174
+ module has not been initialized.
175
+
176
+ ---
177
+
178
+ #### `isInitialized(mod)`
179
+
180
+ ```typescript
181
+ function isInitialized(mod: Module): boolean
182
+ ```
183
+
184
+ Returns `true` if the given module has been loaded and cached (read-only).
185
+ Exported from both `init.ts` and the root barrel (`src/ts/index.ts`).
186
+
187
+ > [!NOTE]
188
+ > `isInitialized` is a diagnostic indicator only — not a control mechanism.
189
+ > Use `init()` to initialize modules; do not guard calls on this value.
190
+
191
+ ---
192
+
193
+ #### `_resetForTesting()`
194
+
195
+ ```typescript
196
+ function _resetForTesting(): void
197
+ ```
198
+
199
+ Clears all cached WASM instances. This is a testing utility, it allows test
200
+ suites to reset the initialization state between test runs. Do not use this in
201
+ production code.
202
+
203
+ ---
204
+
205
+ ## Usage Examples
206
+
207
+ ### Embedded mode (default: simplest)
208
+
209
+ This is the recommended mode for most applications. The WASM binaries are
210
+ bundled inside the package as base64-encoded strings, so there are no extra
211
+ files to serve or fetch.
212
+
213
+ ```typescript
214
+ import { init, SHA256 } from 'leviathan-crypto'
215
+
216
+ await init('sha2')
217
+
218
+ const hash = new SHA256()
219
+ const digest = hash.hash(myData)
220
+ ```
221
+
222
+ ### Initializing multiple modules at once
223
+
224
+ Pass an array to load several modules in a single call:
225
+
226
+ ```typescript
227
+ import { init, Serpent, SHA256 } from 'leviathan-crypto'
228
+
229
+ await init(['serpent', 'sha2'])
230
+
231
+ const cipher = new Serpent(key)
232
+ const hash = new SHA256()
233
+ ```
234
+
235
+ ### Streaming mode (better performance for large apps)
236
+
237
+ Streaming mode fetches `.wasm` files from a URL and uses the browser's
238
+ streaming compilation (`WebAssembly.instantiateStreaming`). This can be
239
+ faster than embedded mode because the browser can begin compiling the WASM
240
+ binary while it is still downloading.
241
+
242
+ You must serve the `.wasm` files from your web server and provide the base
243
+ URL where they are hosted. The files must be served with the
244
+ `Content-Type: application/wasm` header.
245
+
246
+ ```typescript
247
+ import { init, Serpent } from 'leviathan-crypto'
248
+
249
+ await init('serpent', 'streaming', {
250
+ wasmUrl: '/static/wasm/'
251
+ })
252
+
253
+ const cipher = new Serpent(key)
254
+ ```
255
+
256
+ The library knows the filename for each module (e.g. `serpent.wasm`,
257
+ `sha2.wasm`). You only need to provide the directory URL.
258
+
259
+ ### Manual mode (full control)
260
+
261
+ Manual mode lets you provide the raw WASM binary yourself. This is useful if
262
+ you have a custom build pipeline, want to load binaries from a non-standard
263
+ source, or need to verify the binary before passing it to the library.
264
+
265
+ ```typescript
266
+ import { init, SHA256 } from 'leviathan-crypto'
267
+
268
+ // Load the binary however you like
269
+ const wasmBinary = await fetch('/my-custom-path/sha2.wasm')
270
+ .then(r => r.arrayBuffer())
271
+
272
+ await init('sha2', 'manual', {
273
+ wasmBinary: { sha2: new Uint8Array(wasmBinary) }
274
+ })
275
+
276
+ const hash = new SHA256()
277
+ ```
278
+
279
+ ### Checking if a module is initialized
280
+
281
+ ```typescript
282
+ import { isInitialized } from 'leviathan-crypto'
283
+
284
+ if (!isInitialized('sha2')) {
285
+ await init('sha2')
286
+ }
287
+ ```
288
+
289
+ ---
290
+
291
+ ## Error Conditions
292
+
293
+ | Situation | What happens |
294
+ |-------------------------------------------|----------------------------------------------------------------------------------------------|
295
+ | Using a class before calling `init()` | Throws: `"leviathan-crypto: call init(['<mod>']) before using this class"` |
296
+ | Streaming mode without `wasmUrl` | Throws: `"leviathan-crypto: streaming mode requires wasmUrl"` |
297
+ | Manual mode without the needed binary | Throws: `"leviathan-crypto: manual mode requires wasmBinary['<mod>']"` |
298
+ | Unknown mode string | Throws: `"leviathan-crypto: unknown mode '<mode>'"` |
299
+ | Calling `init()` for an already-loaded module | No error. Module is silently skipped (idempotent behavior) |
300
+
301
+ ---
302
+
303
+ ## Cross-References
304
+
305
+ - [README.md](./README.md) — package overview and quick-start guide
306
+ - [loader.md](./loader.md) — WASM binary loading strategies (internal details)
307
+ - [architecture.md](./architecture.md) — architecture overview, module structure, and build pipeline
308
+ - [wasm.md](./wasm.md) — WebAssembly primer: modules, instances, memory, and the init gate
@@ -0,0 +1,206 @@
1
+ # WASM Binary Loading Strategies
2
+
3
+ > [!NOTE]
4
+ > Internal module used by `init()` that handles the actual loading and
5
+ > instantiation of WebAssembly binaries. You normally do not interact
6
+ > with this module directly.
7
+
8
+ ## Overview
9
+
10
+ When you call `init()`, it delegates the work of obtaining and compiling the
11
+ WASM binary to the loader. The loader supports three strategies:
12
+
13
+ - **Embedded** -- The WASM binary is already bundled in the package as a
14
+ base64-encoded string. The loader decodes it and instantiates the module.
15
+ No network requests are made. This is the default and simplest option.
16
+ - **Streaming** -- The loader fetches the `.wasm` file from a URL you provide
17
+ and uses the browser's streaming compilation API. The browser can start
18
+ compiling the binary while it is still downloading, which can improve
19
+ load times for larger modules.
20
+ - **Manual** -- You provide the raw binary data (as a `Uint8Array` or
21
+ `ArrayBuffer`) and the loader instantiates it directly. This gives you
22
+ full control over how the binary is obtained.
23
+
24
+ All three strategies produce the same result: a `WebAssembly.Instance` that
25
+ the wrapper classes use to perform cryptographic operations.
26
+
27
+ ---
28
+
29
+ ## Security Notes
30
+
31
+ - **Embedded mode requires no network access.** The WASM binary is part of
32
+ the installed package. This eliminates the risk of a compromised CDN or
33
+ man-in-the-middle attack altering the binary at load time.
34
+ - **Streaming mode requires correct MIME type.** The `.wasm` files must be
35
+ served with `Content-Type: application/wasm`. This is a browser requirement
36
+ for `WebAssembly.instantiateStreaming`. If the header is missing or wrong,
37
+ the browser will reject the response.
38
+ - **Manual mode places integrity responsibility on you.** The loader
39
+ instantiates whatever binary you provide. If you use manual mode, you are
40
+ responsible for verifying that the binary is authentic and unmodified.
41
+ - **Each module gets its own memory.** Every instantiation creates a fresh
42
+ `WebAssembly.Memory` with 3 pages (192 KB). Modules cannot share or
43
+ access each other's memory. This means key material loaded into one
44
+ module's memory space is isolated from all other modules.
45
+
46
+ ---
47
+
48
+ ## API Reference
49
+
50
+ These functions are exported from `loader.ts` and called by `init.ts`. They
51
+ are not part of the public API -- they are documented here for completeness
52
+ and for contributors working on the internals.
53
+
54
+ ### `loadEmbedded(thunk)`
55
+
56
+ ```typescript
57
+ async function loadEmbedded(
58
+ thunk: () => Promise<string>,
59
+ ): Promise<WebAssembly.Instance>
60
+ ```
61
+
62
+ Loads a WASM module from an embedded base64-encoded string obtained by calling
63
+ the provided thunk.
64
+
65
+ **How it works:**
66
+
67
+ 1. Calls the thunk, which dynamically imports the embedded binary file and
68
+ returns the base64-encoded WASM string.
69
+ 2. Decodes the base64 string to raw bytes.
70
+ 3. Instantiates the WASM module with a fresh 3-page `WebAssembly.Memory`.
71
+
72
+ The thunk is provided by each module's own `init()` function (e.g.
73
+ `serpent/index.ts` passes `() => import('../embedded/serpent.js').then(m => m.WASM_BASE64)`).
74
+ This design means `loader.ts` has no knowledge of module names or embedded file
75
+ paths -- each module owns its own embedded import, enabling tree-shaking.
76
+
77
+ **Parameters:**
78
+
79
+ - `thunk` -- A function that returns a Promise resolving to a base64-encoded
80
+ WASM binary string. Each module's `index.ts` defines its own thunk pointing
81
+ to its own embedded file.
82
+
83
+ **Returns:** A Promise that resolves to a `WebAssembly.Instance`.
84
+
85
+ **Error conditions:**
86
+
87
+ - If the embedded binary file does not exist, this means the build step
88
+ (`scripts/embed-wasm.ts`) has not been run. Run `npm run build` to
89
+ generate the embedded files.
90
+
91
+ ---
92
+
93
+ ### `loadStreaming(mod, baseUrl, filename)`
94
+
95
+ ```typescript
96
+ async function loadStreaming(
97
+ _mod: Module,
98
+ baseUrl: URL | string,
99
+ filename: string,
100
+ ): Promise<WebAssembly.Instance>
101
+ ```
102
+
103
+ Loads a WASM module by fetching it from a URL and using streaming compilation.
104
+
105
+ **How it works:**
106
+
107
+ 1. Constructs the full URL by combining `baseUrl` and `filename`
108
+ (e.g. `https://example.com/wasm/` + `serpent.wasm`).
109
+ 2. Calls `WebAssembly.instantiateStreaming(fetch(url), imports)`, which
110
+ allows the browser to compile the module while it downloads.
111
+ 3. Creates a fresh 3-page `WebAssembly.Memory` for the instance.
112
+
113
+ **Parameters:**
114
+
115
+ - `_mod` -- The module name (currently unused in the function body, but
116
+ passed for consistency).
117
+ - `baseUrl` -- The base URL where `.wasm` files are hosted. Can be a
118
+ `URL` object or a string.
119
+ - `filename` -- The `.wasm` filename (e.g. `'serpent.wasm'`). This is
120
+ determined by `init.ts` using its internal filename mapping.
121
+
122
+ **Returns:** A Promise that resolves to a `WebAssembly.Instance`.
123
+
124
+ **Error conditions:**
125
+
126
+ - Network failure (server unreachable, 404, etc.) will cause the Promise
127
+ to reject.
128
+ - If the server does not respond with `Content-Type: application/wasm`,
129
+ the browser will reject the streaming compilation. This is a common
130
+ issue with misconfigured web servers -- ensure your server is configured
131
+ to serve `.wasm` files with the correct MIME type.
132
+
133
+ ---
134
+
135
+ ### `loadManual(binary)`
136
+
137
+ ```typescript
138
+ async function loadManual(
139
+ binary: Uint8Array | ArrayBuffer,
140
+ ): Promise<WebAssembly.Instance>
141
+ ```
142
+
143
+ Loads a WASM module from a raw binary you provide directly.
144
+
145
+ **How it works:**
146
+
147
+ 1. Converts `ArrayBuffer` to `Uint8Array` if needed.
148
+ 2. Instantiates the WASM module with a fresh 3-page `WebAssembly.Memory`.
149
+
150
+ **Parameters:**
151
+
152
+ - `binary` -- The raw WASM binary as a `Uint8Array` or `ArrayBuffer`.
153
+
154
+ **Returns:** A Promise that resolves to a `WebAssembly.Instance`.
155
+
156
+ **Error conditions:**
157
+
158
+ - If the binary is not a valid WASM module, `WebAssembly.instantiate` will
159
+ throw. The error message will come from the browser's WASM engine and
160
+ will typically mention a validation or compilation failure.
161
+
162
+ ---
163
+
164
+ ## Internal Details
165
+
166
+ ### Embedded binary ownership
167
+
168
+ Each module's `index.ts` owns the dynamic import to its own embedded binary
169
+ file. The loader has no knowledge of module names or file paths -- it receives
170
+ a thunk from `initModule()` and calls it. This means `loader.ts` has no
171
+ dependency on any embedded file, which enables bundlers to tree-shake unused
172
+ modules.
173
+
174
+ | Module | Thunk defined in | Embedded file path |
175
+ |-------------|-------------------------------|----------------------------|
176
+ | `serpent` | `serpent/index.ts` | `./embedded/serpent.js` |
177
+ | `chacha20` | `chacha20/index.ts` | `./embedded/chacha.js` |
178
+ | `sha2` | `sha2/index.ts` | `./embedded/sha2.js` |
179
+ | `sha3` | `sha3/index.ts` | `./embedded/sha3.js` |
180
+
181
+ The embedded `.js` files are generated by the build script
182
+ (`scripts/embed-wasm.ts`) and are gitignored. They are not meant to be
183
+ created or edited by hand.
184
+
185
+ ### Base64 decoding
186
+
187
+ The loader handles base64 decoding in both browser and Node.js environments:
188
+
189
+ - **Browser:** Uses the built-in `atob()` function.
190
+ - **Node.js:** Falls back to `Buffer.from(b64, 'base64')`.
191
+
192
+ ### Memory allocation
193
+
194
+ Every WASM instance receives a `WebAssembly.Memory` with exactly 3 pages
195
+ (192 KB total). The memory size is fixed -- modules do not grow their memory
196
+ at runtime. This is a deliberate design choice: fixed memory prevents
197
+ unexpected allocations and makes the memory layout predictable and auditable.
198
+
199
+ ---
200
+
201
+ ## Cross-References
202
+
203
+ - [README.md](./README.md) — package overview and quick-start guide
204
+ - [architecture.md](./architecture.md) — architecture overview and build pipeline
205
+ - [init.md](./init.md) — the public `init()` API that uses this loader
206
+ - [wasm.md](./wasm.md) — WebAssembly primer: modules, instances, and memory model