wative-core 1.2.2 → 2.2.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 (62) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/LICENSE +55 -0
  3. package/README.md +496 -0
  4. package/dist/artifacts/evm/index.cjs +1 -0
  5. package/dist/artifacts/evm/index.d.cts +6 -0
  6. package/dist/artifacts/evm/index.d.ts +6 -0
  7. package/dist/artifacts/evm/index.js +1 -0
  8. package/dist/artifacts/svm/index.cjs +1 -0
  9. package/dist/artifacts/svm/index.d.cts +9 -0
  10. package/dist/artifacts/svm/index.d.ts +9 -0
  11. package/dist/artifacts/svm/index.js +1 -0
  12. package/dist/container-provider-C59yqd5Z.d.ts +48 -0
  13. package/dist/container-provider-i614LDE1.d.cts +48 -0
  14. package/dist/evm-BLml33LX.d.cts +34 -0
  15. package/dist/evm-DTzqGlL6.d.ts +34 -0
  16. package/dist/file-sink-B73MuigK.d.cts +44 -0
  17. package/dist/file-sink-LS4hyXXi.d.ts +44 -0
  18. package/dist/index.cjs +1 -0
  19. package/dist/index.d.cts +194 -0
  20. package/dist/index.d.ts +194 -0
  21. package/dist/index.js +1 -0
  22. package/dist/index.node.cjs +1 -0
  23. package/dist/index.node.d.cts +6 -0
  24. package/dist/index.node.d.ts +6 -0
  25. package/dist/index.node.js +1 -0
  26. package/dist/node/index.cjs +1 -0
  27. package/dist/node/index.d.cts +7 -0
  28. package/dist/node/index.d.ts +7 -0
  29. package/dist/node/index.js +1 -0
  30. package/dist/svm-CCzqNAWM.d.cts +59 -0
  31. package/dist/svm-DieFbh9k.d.ts +59 -0
  32. package/dist/workspace-CamTHxso.d.cts +737 -0
  33. package/dist/workspace-CamTHxso.d.ts +737 -0
  34. package/package.json +85 -39
  35. package/tests/01-quick-start.test.cjs +37 -0
  36. package/tests/02-hd-account.test.cjs +71 -0
  37. package/tests/03-pk-account.test.cjs +59 -0
  38. package/tests/04-network-management.test.cjs +94 -0
  39. package/tests/05-asset-management.test.cjs +117 -0
  40. package/tests/06-address-signing.test.cjs +55 -0
  41. package/tests/07-custom-provider.test.cjs +164 -0
  42. package/tests/08-persistence.test.cjs +105 -0
  43. package/tests/09-workspace-filter.test.cjs +84 -0
  44. package/tests/10-default-network.test.cjs +70 -0
  45. package/tests/11-subpath-imports.test.cjs +21 -0
  46. package/tests/12-logger.test.cjs +44 -0
  47. package/tests/13-workspace-config.test.cjs +43 -0
  48. package/tests/14-esm-runtime.test.mjs +81 -0
  49. package/tests/15-package-resolution.test.cjs +76 -0
  50. package/tests/16-known-answer-vectors.test.cjs +141 -0
  51. package/tests/17-locked-state.test.cjs +162 -0
  52. package/tests/README.md +72 -0
  53. package/lib/account.d.ts +0 -69
  54. package/lib/index.d.ts +0 -6
  55. package/lib/index.esm.js +0 -1
  56. package/lib/index.umd.js +0 -1
  57. package/lib/mnemonic.d.ts +0 -3
  58. package/lib/utils.d.ts +0 -21
  59. package/src/account.ts +0 -749
  60. package/src/index.ts +0 -11
  61. package/src/mnemonic.ts +0 -2084
  62. package/src/utils.ts +0 -132
package/README.md ADDED
@@ -0,0 +1,496 @@
1
+ # Wative Core 2
2
+
3
+ Hyperscale crypto wallet management library — multi-workspace, multi-chain, originally designed for on-chain market makers.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add wative-core
9
+ # or
10
+ npm i wative-core
11
+ ```
12
+
13
+ Node.js 22.12+ for the filesystem backend. The browser build has no Node requirement.
14
+
15
+ <img width="1280" height="720" alt="wative-hierarchy" src="https://github.com/user-attachments/assets/eea4e0de-e19f-43ff-8a2b-ea5193998166" />
16
+
17
+ ## Quick start
18
+
19
+ The API is the same in both environments. Only storage differs: Node writes
20
+ encrypted files to disk, the browser writes to IndexedDB.
21
+
22
+ ### Node
23
+
24
+ ```ts
25
+ import { Workspace } from "wative-core";
26
+ import "wative-core/node"; // installs the filesystem backend
27
+
28
+ const ws = await Workspace.open({ password: "your-workspace-password" });
29
+
30
+ const account = await ws.accounts.create(
31
+ "Trading Desk",
32
+ "your-workspace-password",
33
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
34
+ );
35
+
36
+ await account.deriveWallets(5);
37
+
38
+ const wallet = account.wallets[0];
39
+ const evmAddress = wallet.addresses.find((a) => a.vm === "evm")!;
40
+ const svmAddress = wallet.addresses.find((a) => a.vm === "svm")!;
41
+
42
+ await ws.lock();
43
+ ```
44
+
45
+ ### Browser
46
+
47
+ ```ts
48
+ import { Workspace, IdbProvider } from "wative-core";
49
+
50
+ const provider = await IdbProvider.create("my-dapp");
51
+ const ws = await Workspace.open({ provider, password: "your-workspace-password" });
52
+
53
+ // everything from here is identical to the Node example
54
+ const account = await ws.accounts.create(
55
+ "Trading Desk",
56
+ "your-workspace-password",
57
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
58
+ );
59
+
60
+ await account.deriveWallets(5);
61
+ await ws.lock();
62
+ ```
63
+
64
+ ### What differs
65
+
66
+ | | Node | Browser |
67
+ | -------------- | -------------------------------------- | -------------------------------- |
68
+ | Extra import | `import "wative-core/node"` | none |
69
+ | Storage | encrypted files on disk | IndexedDB |
70
+ | Where it lives | `Workspace.open({ path })`, or default | `IdbProvider.create(name)` |
71
+ | Extra setup | none | `Buffer` polyfill |
72
+ | Keys can vanish| no | yes — storage is evictable |
73
+
74
+ `HybridProvider` and `FileSink` are also re-exported from the package root in
75
+ Node, so existing imports keep working. New code should take them from
76
+ `wative-core/node`.
77
+
78
+ ### Browser: the Buffer polyfill
79
+
80
+ The Solana libraries read a global `Buffer`, which browsers do not provide.
81
+
82
+ ```bash
83
+ pnpm add buffer
84
+ ```
85
+
86
+ ```ts
87
+ // once, before importing wative-core
88
+ import { Buffer } from "buffer";
89
+ globalThis.Buffer = Buffer;
90
+ ```
91
+
92
+ ### Browser: keeping keys from disappearing
93
+
94
+ Browser storage is not permanent. A browser may clear IndexedDB when disk runs
95
+ low, and Safari clears script-writable storage after about seven days without
96
+ user interaction. Losing it means losing the keys.
97
+
98
+ `IdbProvider.create()` asks the browser for persistent storage and reports the
99
+ answer:
100
+
101
+ ```ts
102
+ const provider = await IdbProvider.create("my-dapp");
103
+ provider.durability; // "persistent" | "best-effort"
104
+ ```
105
+
106
+ Creating a **new** workspace in non-persistent storage is refused, because keys
107
+ written there can disappear without warning:
108
+
109
+ ```ts
110
+ // throws STORAGE_NOT_DURABLE when the browser refused persistence
111
+ await Workspace.open({ provider, password });
112
+
113
+ // proceed anyway — only if the keys are backed up elsewhere
114
+ await IdbProvider.create("my-dapp", { acknowledgeEvictionRisk: true });
115
+ ```
116
+
117
+ Opening a workspace that already exists is never blocked.
118
+
119
+ Browsers are far more likely to grant persistence when the request comes from a
120
+ user action, so call `IdbProvider.create()` from a click rather than on page
121
+ load. Whatever the answer, give users a backup — `exportContainer()` returns the
122
+ encrypted records, and they import into a Node workspace unchanged:
123
+
124
+ ```ts
125
+ const backup = await provider.exportContainer(); // still encrypted
126
+ await otherProvider.importContainer(backup); // same password opens it
127
+ ```
128
+
129
+ ## Runnable examples
130
+
131
+ A `tests/` folder ships with the package — runnable JavaScript files demonstrating every domain's use cases. Read them top-to-bottom as walkthroughs, or run them as integration tests via Node's built-in test runner.
132
+
133
+ Copy them out of `node_modules` first, then run the whole set:
134
+
135
+ ```bash
136
+ cp -R node_modules/wative-core/tests wative-examples
137
+ node --test wative-examples/
138
+ ```
139
+
140
+ The copy is required: Node's test runner skips anything under `node_modules`, so pointing `--test` there finds no files. To run a single example without copying, execute it directly — a `node:test` file runs itself:
141
+
142
+ ```bash
143
+ node node_modules/wative-core/tests/01-quick-start.test.cjs
144
+ ```
145
+
146
+ Files cover: quick start, HD vs PK accounts, network management, asset management, address signing, custom storage backends, persistence, workspace search, default-network selection, subpath imports, the workspace logger, workspace config, the ESM entry, and package resolution. See [tests/README.md](./tests/README.md) for the index.
147
+
148
+ ## What you can do
149
+
150
+ The library is organized around seven things you'll work with:
151
+
152
+ - **`Workspace`** — your top-level container. Holds the password, your accounts, your network and asset list, and a built-in logger.
153
+ - **`Account`** — either an HD account (one BIP-39 mnemonic, can derive many wallets) or a PK account (raw private keys you import one at a time). Each account can have its own password or share the workspace password.
154
+ - **`Wallet`** — a unit inside an account. HD wallets carry one EVM and one Solana address; PK wallets carry one address.
155
+ - **`Address`** — an on-chain identity. Sign messages, build transactions, send them.
156
+ - **`Network`** — chain metadata. 10 networks ship pre-loaded.
157
+ - **`Asset`** — token metadata. 25 tokens ship pre-loaded.
158
+ - **`Transaction`** — `EvmTransaction` or `SvmTransaction`. Subscribe to lifecycle events (`change` / `confirmed` / `failed`) or await terminal states (`whenSubmitted` / `whenMined` / `whenConfirmed` / `whenFinalized`).
159
+
160
+ Everything is encrypted on disk under your workspace password.
161
+
162
+ ### Pre-loaded networks and tokens
163
+
164
+ | Networks (10) | Tokens (25) |
165
+ |---|---|
166
+ | ethereum, base, bnbchain, arbitrum, optimism | native gas + USDC + USDT on each EVM mainnet (BSC versions are 18-decimal Binance-Peg); plus WETH on ethereum |
167
+ | sepolia, arbitrum-sepolia | native ETH on both testnets; plus USDC on arbitrum-sepolia |
168
+ | solana, solana-testnet, solana-devnet | native SOL + USDC + USDT + WSOL on solana mainnet; native-only on testnets |
169
+
170
+ ## Examples by domain
171
+
172
+ ### Workspace — open / unlock / lock
173
+
174
+ ```ts
175
+ import { Workspace } from "wative-core";
176
+
177
+ const ws = await Workspace.open({ password: "wsp-pwd" });
178
+
179
+ const ws2 = await Workspace.open({ path: "/var/lib/wative-prod", password: "wsp-pwd" });
180
+
181
+ const locked = await Workspace.open({ path: "/var/lib/wative-prod" });
182
+ console.log(locked.locked);
183
+ await locked.unlock("wsp-pwd");
184
+
185
+ ws.logger.info("trading session opened", { desk: "alpha" });
186
+ await ws.logger.setLevel("debug");
187
+
188
+ await ws.lock();
189
+ ```
190
+
191
+ #### Creating vs. opening — it's automatic
192
+
193
+ You don't tell `open()` whether to create or open. It decides from what's already at the location:
194
+
195
+ - **Nothing there (or an empty folder)** → a new workspace is created.
196
+ - **A wative workspace already there** → it's opened, and your password is checked.
197
+ - **Something else there** (a folder with unrelated files) → the call is **refused** with a `PARAMETER_ERROR`, so a mistyped path never turns one of your other folders into a workspace.
198
+
199
+ Pass the password to get an unlocked workspace, or omit it to get a locked one you unlock later:
200
+
201
+ ```ts
202
+ const ws = await Workspace.open({ path: "/var/lib/wative-prod", password: "wsp-pwd" }); // unlocked
203
+ const later = await Workspace.open({ path: "/var/lib/wative-prod" }); // locked
204
+ await later.unlock("wsp-pwd");
205
+ ```
206
+
207
+ `open()` also accepts the older positional form — `Workspace.open(pathOrProvider, password)` — which works exactly the same way.
208
+
209
+ #### Where the workspace lives on disk
210
+
211
+ Most apps pass an explicit path as the first argument — that path is used verbatim (with `~` expansion). When you omit the path, the library resolves it through a **3-tier strategy**, in order:
212
+
213
+ 1. **`WATIVE_WORKSPACE_PATH` environment variable** — if set and non-empty, this wins. Use it for ops/CI overrides without touching code:
214
+
215
+ ```bash
216
+ WATIVE_WORKSPACE_PATH=/var/lib/wative-prod node app.js
217
+ WATIVE_WORKSPACE_PATH=~/.wative2 node app.js # ~ is expanded
218
+ ```
219
+
220
+ 2. **`<process.cwd()>/.wative2`** — used when the env var is unset *and* this directory already exists on disk. Lets a project pin its workspace by simply having a `.wative2/` folder at the repo root.
221
+
222
+ 3. **`<os.homedir()>/.wative2`** — last-resort fallback. Where `Workspace.open({ password })` lands on a fresh machine when no env var is set and there's no project-local `.wative2/`. Acts as the user-wide default workspace.
223
+
224
+ So a typical lifecycle looks like:
225
+
226
+ ```ts
227
+ // First run on a fresh machine — env unset, no <cwd>/.wative2 → creates ~/.wative2
228
+ await Workspace.open({ password: "wsp-pwd" });
229
+
230
+ // Later, opt the project into a local workspace
231
+ // $ mkdir .wative2
232
+ // Now <cwd>/.wative2 exists → tier 2 wins
233
+ await Workspace.open({ password: "wsp-pwd" });
234
+
235
+ // Or override via env in CI/staging
236
+ // $ WATIVE_WORKSPACE_PATH=/srv/wative-staging node app.js
237
+ await Workspace.open({ password: "wsp-pwd" });
238
+ ```
239
+
240
+ If you'd rather not rely on the resolver, just pass the path explicitly — it short-circuits all three tiers.
241
+
242
+ ### Account — HD vs PK, derive wallets, import keys
243
+
244
+ ```ts
245
+ const hd = await ws.accounts.create(
246
+ "Trading Desk",
247
+ "wsp-pwd",
248
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
249
+ );
250
+ await hd.deriveWallets(10);
251
+ await hd.sliceWallets(6);
252
+
253
+ const pk = await ws.accounts.create(
254
+ "External Hot Wallet",
255
+ "wsp-pwd",
256
+ "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
257
+ undefined,
258
+ { kind: "PK" },
259
+ );
260
+ await pk.importPrivateKey(
261
+ "xtGWcHvQsr5ue8zG2F5fm31bW8vyn597Y92gUWaZZg3S1Z6FeJMATL8KU3xJMGbLfALnokcct9y5wYtCRrwNXZW",
262
+ "svm",
263
+ );
264
+
265
+ const sub = await ws.accounts.create(
266
+ "Sub Account",
267
+ "sub-pwd",
268
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
269
+ undefined,
270
+ { hasOwnPassword: true },
271
+ );
272
+ await sub.tryUnlock("sub-pwd");
273
+ await sub.resetPassword("sub-pwd", "new-sub-pwd");
274
+
275
+ console.log(hd.dumpMnemonic());
276
+
277
+ hd.lock();
278
+ await pk.drop();
279
+ ```
280
+
281
+ ### Wallet — derivation slot, tags, drop
282
+
283
+ ```ts
284
+ const wallet = hd.wallets[2];
285
+ console.log(wallet.id);
286
+ console.log(wallet.addresses.length);
287
+
288
+ await wallet.addTag("hot");
289
+ await wallet.addTag("alpha-strategy");
290
+
291
+ await hd.wallets.drop(hd.wallets[10]);
292
+ ```
293
+
294
+ ### Address — sign messages, build transactions
295
+
296
+ ```ts
297
+ const evmAddr = hd.wallets[0].addresses.find((a) => a.vm === "evm")!;
298
+ const svmAddr = hd.wallets[0].addresses.find((a) => a.vm === "svm")!;
299
+
300
+ const sig = evmAddr.signMessage("hello hedgue");
301
+
302
+ import { Network } from "wative-core";
303
+ const tx = await evmAddr.buildTransaction({
304
+ to: "0x1234567890123456789012345678901234567890",
305
+ value: 1_000_000_000_000_000n,
306
+ network: Network.Ethereum,
307
+ });
308
+ await evmAddr.signTransaction(tx);
309
+ const tracker = await evmAddr.sendTransaction(tx);
310
+
311
+ tracker.on("change", (state) => console.log("state →", state.status));
312
+ tracker.on("confirmed", (receipt) => console.log("confirmed", receipt));
313
+ tracker.on("failed", (err) => console.error("failed", err));
314
+
315
+ const hash = await tracker.whenSubmitted();
316
+ const receipt = await tracker.whenConfirmed(3);
317
+
318
+ const sim = await evmAddr.simulateTransaction(tx);
319
+ console.log(sim.gasUsed);
320
+ ```
321
+
322
+ ### Network — pre-loaded networks + your own
323
+
324
+ ```ts
325
+ import { Network, type ChainId, type Slug } from "wative-core";
326
+
327
+ console.log(Network.Ethereum.chainId);
328
+ console.log(Network.Solana.vm);
329
+
330
+ const eth = ws.networks.bySlug("ethereum" as Slug)!;
331
+ const arb = ws.networks.byChainId(42161 as ChainId)!;
332
+
333
+ const monad = new Network({
334
+ slug: "monad-testnet" as Slug,
335
+ name: "Monad Testnet",
336
+ chainId: 10143 as ChainId,
337
+ rpcUrl: "https://testnet-rpc.monad.xyz",
338
+ nativeCurrency: { name: "Monad", symbol: "MON", decimals: 18 },
339
+ vm: "evm",
340
+ });
341
+ await ws.networks.add(monad);
342
+
343
+ const customEth = new Network({
344
+ ...eth,
345
+ rpcUrl: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
346
+ });
347
+ await ws.networks.update(customEth);
348
+
349
+ await ws.networks.drop(monad);
350
+
351
+ await hd.setDefaultNetwork("base");
352
+ await hd.setDefaultNetwork(8453 as ChainId);
353
+ await hd.setDefaultNetwork("0x2105");
354
+ await hd.setDefaultNetwork(Network.Base);
355
+ ```
356
+
357
+ ### Asset — pre-loaded tokens + custom tokens
358
+
359
+ ```ts
360
+ import { Asset, Network, type AssetId } from "wative-core";
361
+
362
+ const ethAssets = await ws.assets(Network.Ethereum);
363
+ console.log(ethAssets.map((a) => a.symbol));
364
+
365
+ const myToken = new Asset({
366
+ id: 200 as AssetId,
367
+ symbol: "PEPE",
368
+ name: "Pepe",
369
+ decimals: 18,
370
+ contractAddress: "0x6982508145454Ce325dDbE47a25d4ec3d2311933",
371
+ network: Network.Ethereum,
372
+ });
373
+ await ws.addAsset(myToken);
374
+
375
+ await ws.dropAsset(200 as AssetId);
376
+
377
+ const found = await ws.filter("USDC", "Asset");
378
+ const byAddr = await ws.filter("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "Asset");
379
+ ```
380
+
381
+ ### Transaction — build, sign, simulate, send
382
+
383
+ ```ts
384
+ import { EvmTransaction, Network, type EvmAddress } from "wative-core";
385
+
386
+ const evmTx = new EvmTransaction({
387
+ from: evmAddr.publicKey as EvmAddress,
388
+ to: "0x1234567890123456789012345678901234567890" as EvmAddress,
389
+ value: 10_000_000n,
390
+ network: Network.Base,
391
+ gasLimit: 21000n,
392
+ maxFeePerGas: 50_000_000_000n,
393
+ maxPriorityFeePerGas: 1_000_000_000n,
394
+ });
395
+ await evmAddr.signTransaction(evmTx);
396
+ const evmTracker = await evmAddr.sendTransaction(evmTx);
397
+
398
+ const receipt = await evmTracker.whenConfirmed(3);
399
+ ```
400
+
401
+ For Solana transactions, build instructions with `@solana/web3.js` (or your preferred Solana toolkit) and pass them to `new SvmTransaction({ from, instructions, network })`. The signing, sending, and tracker lifecycle then mirror the EVM flow.
402
+
403
+ #### Hand off to an external signer / RPC client
404
+
405
+ Both `EvmTransaction` and `SvmTransaction` expose `toRawTx()` for routing the transaction through any chain-native tool you already use:
406
+
407
+ ```ts
408
+ // EVM — synchronous; returns a plain web3.js / ethers / viem TransactionRequest
409
+ const evmTx = evmAddr.buildTransaction({
410
+ to: "0x1234567890123456789012345678901234567890" as EvmAddress,
411
+ value: 10_000_000n,
412
+ gasLimit: 21000n,
413
+ });
414
+ const rawEvm = evmTx.toRawTx();
415
+ // { from, to, value, data, type, chainId, gasLimit, ... }
416
+ // pass `rawEvm` to web3.eth.sendTransaction, ethers Signer, viem walletClient, etc.
417
+ ```
418
+
419
+ ```ts
420
+ // Solana — async; returns the underlying @solana/web3.js Transaction
421
+ const svmTx = svmAddr.buildTransaction({
422
+ recipient: "11111111111111111111111111111112",
423
+ amount: 1_000_000n,
424
+ });
425
+ const rawSvm = await svmTx.toRawTx();
426
+ // rawSvm.add(extraIx);
427
+ // rawSvm.sign(theirKeypair);
428
+ // connection.sendRawTransaction(rawSvm.serialize());
429
+ ```
430
+
431
+ For SVM, if you supply `recentBlockhash` at construction time, `toRawTx()` builds entirely offline — no Address binding required.
432
+
433
+ ### Workspace search
434
+
435
+ ```ts
436
+ const account = await ws.filter("Trading Desk", "Account");
437
+ const wallet = await ws.filter("alpha-strategy", "Wallet");
438
+ const address = await ws.filter("0xab12345678…cdef", "Address");
439
+ const asset = await ws.filter("USDC", "Asset");
440
+ ```
441
+
442
+ ## Custom storage backends
443
+
444
+ By default, `Workspace.open(...)` stores everything as encrypted files under the path you pass it. If you want to store the workspace somewhere else — a database, an object store, a browser's IndexedDB — extend the `Provider` base class and pass an instance to `Workspace.open()`:
445
+
446
+ ```ts
447
+ import { Workspace, Provider, Record, type RecordInit, type RecordType } from "wative-core";
448
+ import type { Slug } from "wative-core";
449
+
450
+ class MyDatabaseProvider extends Provider {
451
+ constructor(connectionString: string) {
452
+ super(connectionString);
453
+ }
454
+
455
+ protected async _exist(path: string) { return false; }
456
+ protected async _read(path: string) { return new Uint8Array(); }
457
+ protected async _write(path: string, b: Uint8Array) {}
458
+ protected async _remove(path: string) {}
459
+ protected async _ensureDir(path: string) {}
460
+ protected async _listItems(path: string) { return []; }
461
+
462
+ async unlockContainer(password: string): Promise<boolean> { return true; }
463
+ async lockContainer(): Promise<void> {}
464
+ isContainerUnlocked(): boolean { return true; }
465
+
466
+ async loadRecord<T>(type: RecordType, slug?: Slug): Promise<Record<T>> { throw new Error(); }
467
+ async loadRecords<T>(type: RecordType): Promise<readonly Record<T>[]> { return []; }
468
+ async writeRecord<T>(type: RecordType, slug: Slug, value: T): Promise<void> {}
469
+ async dropRecord(type: RecordType, slug: Slug): Promise<void> {}
470
+
471
+ async close(): Promise<void> {}
472
+ }
473
+
474
+ const ws = await Workspace.open({ provider: new MyDatabaseProvider("postgres://..."), password: "wsp-pwd" });
475
+ ```
476
+
477
+ `Workspace.open()` accepts any `Provider` subclass — the rest of the library is identical regardless of where state lives.
478
+
479
+ ## Imports from sub-paths
480
+
481
+ Heavier chain helpers are kept on separate import paths so you only pull them in if you need them:
482
+
483
+ ```ts
484
+ import { ERC20 } from "wative-core/artifacts/evm";
485
+ import { TokenProgram, Token2022Program } from "wative-core/artifacts/svm";
486
+ ```
487
+
488
+ ## Compatibility notes
489
+
490
+ - **Runtime**: Node.js 22.12+. Deno / Bun / browser support is partial. Some chain libraries that ship as compiled dependencies do not have universal builds yet.
491
+ - **Platform builds**: a few compiled dependencies lack pre-built binaries for Windows-ARM64 and Alpine-musl. Most installs on macOS / Linux x64 / Linux ARM64 won't notice.
492
+ - **One process at a time**: opening the same workspace from two Node processes simultaneously isn't supported.
493
+
494
+ ## License
495
+
496
+ [BUSL-1.1](./LICENSE) — Business Source License 1.1.