wative-core 2.4.1 → 2.4.3

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 (53) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +59 -55
  3. package/dist/artifacts/evm/index.cjs +1 -1
  4. package/dist/artifacts/evm/index.d.cts +2 -2
  5. package/dist/artifacts/evm/index.d.ts +2 -2
  6. package/dist/artifacts/evm/index.js +1 -1
  7. package/dist/artifacts/svm/index.cjs +1 -1
  8. package/dist/artifacts/svm/index.d.cts +3 -3
  9. package/dist/artifacts/svm/index.d.ts +3 -3
  10. package/dist/artifacts/svm/index.js +1 -1
  11. package/dist/{container-provider--npoeR2N.d.ts → container-provider-B8M0ylrK.d.ts} +1 -1
  12. package/dist/{container-provider-Ca_5z1uQ.d.cts → container-provider-YxyMpkhG.d.cts} +1 -1
  13. package/dist/{evm-EHpaW_sK.d.ts → evm-Bo3Dy492.d.ts} +1 -1
  14. package/dist/{evm-hSJJOVG3.d.cts → evm-DA6Sh6jv.d.cts} +1 -1
  15. package/dist/{file-sink-Bk5zYHw-.d.ts → file-sink-Be5iLbuT.d.ts} +2 -2
  16. package/dist/{file-sink-7ZAutH45.d.cts → file-sink-KCCpKHRj.d.cts} +2 -2
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.d.cts +6 -6
  19. package/dist/index.d.ts +6 -6
  20. package/dist/index.js +1 -1
  21. package/dist/index.node.cjs +1 -1
  22. package/dist/index.node.d.cts +5 -5
  23. package/dist/index.node.d.ts +5 -5
  24. package/dist/index.node.js +1 -1
  25. package/dist/node/index.cjs +1 -1
  26. package/dist/node/index.d.cts +3 -3
  27. package/dist/node/index.d.ts +3 -3
  28. package/dist/node/index.js +1 -1
  29. package/dist/{svm-B5q8YaV9.d.ts → svm-B51NevS8.d.ts} +1 -1
  30. package/dist/{svm-CXFwmeMo.d.cts → svm-DswX6XRv.d.cts} +1 -1
  31. package/dist/{workspace-kq3QyAgm.d.cts → workspace-CBxKR80D.d.cts} +7 -43
  32. package/dist/{workspace-kq3QyAgm.d.ts → workspace-CBxKR80D.d.ts} +7 -43
  33. package/examples/01-quick-start.test.cjs +1 -2
  34. package/examples/02-hd-account.test.cjs +1 -2
  35. package/examples/03-pk-account.test.cjs +39 -1
  36. package/examples/04-network-management.test.cjs +1 -2
  37. package/examples/05-asset-management.test.cjs +1 -2
  38. package/examples/06-address-signing.test.cjs +1 -2
  39. package/examples/07-custom-provider.test.cjs +2 -8
  40. package/examples/08-persistence.test.cjs +1 -2
  41. package/examples/09-workspace-filter.test.cjs +1 -2
  42. package/examples/10-default-network.test.cjs +1 -3
  43. package/examples/11-subpath-imports.test.cjs +2 -6
  44. package/examples/12-logger.test.cjs +1 -3
  45. package/examples/13-workspace-config.test.cjs +1 -5
  46. package/examples/14-esm-runtime.test.mjs +0 -9
  47. package/examples/15-package-resolution.test.cjs +5 -83
  48. package/examples/16-known-answer-vectors.test.cjs +4 -22
  49. package/examples/17-locked-state.test.cjs +2 -23
  50. package/examples/18-envelope-v3.test.cjs +2 -21
  51. package/examples/19-simple-provider.test.cjs +1 -19
  52. package/examples/README.md +1 -1
  53. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { c as Address, G as SvmTxBuildParams, F as SvmTransaction } from './workspace-kq3QyAgm.js';
1
+ import { c as Address, G as SvmTxBuildParams, F as SvmTransaction } from './workspace-CBxKR80D.js';
2
2
 
3
3
  interface SvmInstruction {
4
4
  readonly programId: string;
@@ -1,4 +1,4 @@
1
- import { c as Address, G as SvmTxBuildParams, F as SvmTransaction } from './workspace-kq3QyAgm.cjs';
1
+ import { c as Address, G as SvmTxBuildParams, F as SvmTransaction } from './workspace-CBxKR80D.cjs';
2
2
 
3
3
  interface SvmInstruction {
4
4
  readonly programId: string;
@@ -128,11 +128,7 @@ interface RecordInit<T> {
128
128
  readonly password?: string;
129
129
  readonly decrypt?: (ciphertext: Uint8Array, password: string) => Uint8Array;
130
130
  readonly persist?: (value: T, password: string) => Promise<void>;
131
- /**
132
- * Throws if the session that issued this record has ended. Supplied by
133
- * providers that HAVE a session; a provider without one omits it and the
134
- * record behaves as before.
135
- */
131
+ /** Throws if the session that issued this record has ended. */
136
132
  readonly requireLiveSession?: () => void;
137
133
  }
138
134
  declare class Record$1<T> {
@@ -142,17 +138,7 @@ declare class Record$1<T> {
142
138
  constructor(slug: Slug, path: string, init?: RecordInit<T>);
143
139
  get locked(): boolean;
144
140
  get value(): T;
145
- /**
146
- * Pure CPU — decrypts the in-memory blob. Sync.
147
- *
148
- * BAD_PASSWORD means the password is wrong: an AES-GCM auth-tag mismatch on
149
- * the locked path, or a mismatch against the password that opened the record
150
- * on the unlocked one. DECRYPT_FAILED means the record cannot answer —
151
- * structural damage or corruption on the locked path, and on the unlocked
152
- * path a record carrying neither a password nor ciphertext, which has nothing
153
- * to verify against. Callers can still read the two apart as "wrong password"
154
- * versus "cannot be checked".
155
- */
141
+ /** Pure CPU — decrypts the in-memory blob. */
156
142
  unlock(password: string): this;
157
143
  /** Persists changes via the owning Provider. */
158
144
  save(): Promise<void>;
@@ -170,38 +156,15 @@ declare abstract class Provider {
170
156
  abstract unlockContainer(password: string): Promise<boolean>;
171
157
  abstract lockContainer(): Promise<void>;
172
158
  abstract isContainerUnlocked(): boolean;
173
- /**
174
- * Structural inspection of the container BEFORE unlock. `Workspace.open()`
175
- * uses it to auto-select create-vs-open without a `create` flag:
176
- * - `"empty"` — no container yet; safe to initialize a new workspace.
177
- * - `"workspace"` — an existing wative workspace; open it.
178
- * - `"foreign"` — non-empty storage that is NOT a wative workspace; `open()`
179
- * refuses rather than scaffolding a workspace into it.
180
- * The base default returns `"workspace"` (proceed as open-or-create, no guard),
181
- * so existing custom providers are unaffected. Providers that can cheaply
182
- * inspect their storage — e.g. `HybridProvider` on a filesystem — override this
183
- * to detect `"foreign"` and stop a mistyped path from creating a workspace
184
- * inside unrelated data.
185
- */
159
+ /** Structural inspection of the container BEFORE unlock. */
186
160
  inspectContainer(): Promise<ContainerState>;
187
161
  abstract loadRecord<T>(type: RecordType, slug?: Slug): Promise<Record$1<T>>;
188
162
  abstract loadRecords<T>(type: RecordType): Promise<ReadonlyArray<Record$1<T>>>;
189
163
  abstract writeRecord<T>(type: RecordType, slug: Slug, value: T): Promise<void>;
190
164
  abstract dropRecord(type: RecordType, slug: Slug): Promise<void>;
191
- /**
192
- * Initialize an empty container by writing the four root records
193
- * (CONFIG, NETWORK, ASSET, LOG) from the bundled template files at
194
- * `src/workspace_template/`. Idempotent: existing records are left
195
- * untouched. The ACCOUNTS namespace is left empty — per-account records
196
- * are created lazily by `accounts.create(...)`.
197
- *
198
- * Subclasses inherit this default; override only if a backend needs
199
- * non-record initialization (e.g. allocating a database schema).
200
- */
165
+ /** Initialize an empty container by writing the four root records (CONFIG, NETWORK, ASSET, LOG) from the bundled templat... */
201
166
  initialize(): Promise<void>;
202
- /** Any storage at all under the accounts area, however named. Defaults to
203
- * "nothing", so a provider that does not implement it can only fail to add a
204
- * refusal reason — never cause one. */
167
+ /** Any storage at all under the accounts area, however named. */
205
168
  protected _hasRawAccountData(): Promise<boolean>;
206
169
  abstract close(): Promise<void>;
207
170
  }
@@ -622,7 +585,8 @@ declare class Account {
622
585
  after: number;
623
586
  dropped: number;
624
587
  }>;
625
- importPrivateKey(pk: string, vm: ChainVM): Promise<Wallet>;
588
+
589
+ importPrivateKey(pk: string, vm?: ChainVM): Promise<Wallet>;
626
590
  drop(): Promise<void>;
627
591
 
628
592
  filterAddress(network: NetworkLike, addr?: string): Address | null;
@@ -128,11 +128,7 @@ interface RecordInit<T> {
128
128
  readonly password?: string;
129
129
  readonly decrypt?: (ciphertext: Uint8Array, password: string) => Uint8Array;
130
130
  readonly persist?: (value: T, password: string) => Promise<void>;
131
- /**
132
- * Throws if the session that issued this record has ended. Supplied by
133
- * providers that HAVE a session; a provider without one omits it and the
134
- * record behaves as before.
135
- */
131
+ /** Throws if the session that issued this record has ended. */
136
132
  readonly requireLiveSession?: () => void;
137
133
  }
138
134
  declare class Record$1<T> {
@@ -142,17 +138,7 @@ declare class Record$1<T> {
142
138
  constructor(slug: Slug, path: string, init?: RecordInit<T>);
143
139
  get locked(): boolean;
144
140
  get value(): T;
145
- /**
146
- * Pure CPU — decrypts the in-memory blob. Sync.
147
- *
148
- * BAD_PASSWORD means the password is wrong: an AES-GCM auth-tag mismatch on
149
- * the locked path, or a mismatch against the password that opened the record
150
- * on the unlocked one. DECRYPT_FAILED means the record cannot answer —
151
- * structural damage or corruption on the locked path, and on the unlocked
152
- * path a record carrying neither a password nor ciphertext, which has nothing
153
- * to verify against. Callers can still read the two apart as "wrong password"
154
- * versus "cannot be checked".
155
- */
141
+ /** Pure CPU — decrypts the in-memory blob. */
156
142
  unlock(password: string): this;
157
143
  /** Persists changes via the owning Provider. */
158
144
  save(): Promise<void>;
@@ -170,38 +156,15 @@ declare abstract class Provider {
170
156
  abstract unlockContainer(password: string): Promise<boolean>;
171
157
  abstract lockContainer(): Promise<void>;
172
158
  abstract isContainerUnlocked(): boolean;
173
- /**
174
- * Structural inspection of the container BEFORE unlock. `Workspace.open()`
175
- * uses it to auto-select create-vs-open without a `create` flag:
176
- * - `"empty"` — no container yet; safe to initialize a new workspace.
177
- * - `"workspace"` — an existing wative workspace; open it.
178
- * - `"foreign"` — non-empty storage that is NOT a wative workspace; `open()`
179
- * refuses rather than scaffolding a workspace into it.
180
- * The base default returns `"workspace"` (proceed as open-or-create, no guard),
181
- * so existing custom providers are unaffected. Providers that can cheaply
182
- * inspect their storage — e.g. `HybridProvider` on a filesystem — override this
183
- * to detect `"foreign"` and stop a mistyped path from creating a workspace
184
- * inside unrelated data.
185
- */
159
+ /** Structural inspection of the container BEFORE unlock. */
186
160
  inspectContainer(): Promise<ContainerState>;
187
161
  abstract loadRecord<T>(type: RecordType, slug?: Slug): Promise<Record$1<T>>;
188
162
  abstract loadRecords<T>(type: RecordType): Promise<ReadonlyArray<Record$1<T>>>;
189
163
  abstract writeRecord<T>(type: RecordType, slug: Slug, value: T): Promise<void>;
190
164
  abstract dropRecord(type: RecordType, slug: Slug): Promise<void>;
191
- /**
192
- * Initialize an empty container by writing the four root records
193
- * (CONFIG, NETWORK, ASSET, LOG) from the bundled template files at
194
- * `src/workspace_template/`. Idempotent: existing records are left
195
- * untouched. The ACCOUNTS namespace is left empty — per-account records
196
- * are created lazily by `accounts.create(...)`.
197
- *
198
- * Subclasses inherit this default; override only if a backend needs
199
- * non-record initialization (e.g. allocating a database schema).
200
- */
165
+ /** Initialize an empty container by writing the four root records (CONFIG, NETWORK, ASSET, LOG) from the bundled templat... */
201
166
  initialize(): Promise<void>;
202
- /** Any storage at all under the accounts area, however named. Defaults to
203
- * "nothing", so a provider that does not implement it can only fail to add a
204
- * refusal reason — never cause one. */
167
+ /** Any storage at all under the accounts area, however named. */
205
168
  protected _hasRawAccountData(): Promise<boolean>;
206
169
  abstract close(): Promise<void>;
207
170
  }
@@ -622,7 +585,8 @@ declare class Account {
622
585
  after: number;
623
586
  dropped: number;
624
587
  }>;
625
- importPrivateKey(pk: string, vm: ChainVM): Promise<Wallet>;
588
+
589
+ importPrivateKey(pk: string, vm?: ChainVM): Promise<Wallet>;
626
590
  drop(): Promise<void>;
627
591
 
628
592
  filterAddress(network: NetworkLike, addr?: string): Address | null;
@@ -1,5 +1,4 @@
1
- // 01 — Quick start. Open a workspace, create an HD account, derive wallets,
2
- // lock down. The shortest end-to-end demo.
1
+ // 01 — Quick start: open a workspace, create an HD account, derive wallets, lock.
3
2
 
4
3
  const test = require("node:test");
5
4
  const assert = require("node:assert");
@@ -1,5 +1,4 @@
1
- // 02 — HD account flows. derive / slice wallets, dump mnemonic, reset password,
2
- // reopen a locked workspace.
1
+ // 02 — HD accounts: derive/slice wallets, dump mnemonic, reset password, reopen.
3
2
 
4
3
  const test = require("node:test");
5
4
  const assert = require("node:assert");
@@ -1,4 +1,4 @@
1
- // 03 — PK account flows. Heterogeneous private keys, one wallet per import.
1
+ // 03 — PK accounts: vm inferred from the key, and one import yielding both chains.
2
2
 
3
3
  const test = require("node:test");
4
4
  const assert = require("node:assert");
@@ -10,6 +10,13 @@ const { Workspace } = require("wative-core");
10
10
 
11
11
  const EVM_PK = "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d";
12
12
  const SVM_PK = "xtGWcHvQsr5ue8zG2F5fm31bW8vyn597Y92gUWaZZg3S1Z6FeJMATL8KU3xJMGbLfALnokcct9y5wYtCRrwNXZW";
13
+ const SVM_PK_2 = "gaiC7Rnf9J6tV3SGwJyzvMDdz9RMmreSWZDyDK682MUKZHaWdZbbqgf35qD7FSwXka2Rf2o4MuhbWQTg82W9mbV";
14
+ const SVM_KEYGEN_BYTES = [
15
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
16
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
17
+ 23, 203, 121, 251, 43, 65, 32, 242, 177, 236, 101, 228, 25, 141, 110, 8,
18
+ 178, 142, 129, 63, 235, 1, 228, 164, 0, 131, 155, 133, 225, 128, 128, 206,
19
+ ];
13
20
 
14
21
  test("PK account — create from EVM key + import a Solana key", async () => {
15
22
  const root = mkdtempSync(join(tmpdir(), "wative-pk-"));
@@ -25,10 +32,41 @@ test("PK account — create from EVM key + import a Solana key", async () => {
25
32
  assert.strictEqual(acc.organizationType, "PK");
26
33
  assert.strictEqual(acc.wallets.length, 1);
27
34
  assert.strictEqual(acc.wallets[0].addresses[0].vm, "evm");
35
+ assert.strictEqual(acc.wallets[0].addresses[1].vm, "svm");
28
36
 
29
37
  await acc.importPrivateKey(SVM_PK, "svm");
30
38
  assert.strictEqual(acc.wallets.length, 2);
31
39
  assert.strictEqual(acc.wallets[1].addresses[0].vm, "svm");
40
+ assert.strictEqual(acc.wallets[1].addresses[1].vm, "evm");
41
+
42
+ await ws.lock();
43
+ });
44
+
45
+ test("PK account — the key formats importPrivateKey accepts", async () => {
46
+ const root = mkdtempSync(join(tmpdir(), "wative-pk-fmt-"));
47
+ const ws = await Workspace.open({ path: root, password: "wsp-pwd" });
48
+ const acc = await ws.accounts.create("Formats", "wsp-pwd", EVM_PK, undefined, { kind: "PK" });
49
+
50
+ const evm = await acc.importPrivateKey(
51
+ "0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1",
52
+ );
53
+ assert.strictEqual(evm.addresses[0].vm, "evm");
54
+
55
+ const svm = await acc.importPrivateKey(SVM_PK_2);
56
+ assert.strictEqual(svm.addresses[0].vm, "svm");
57
+
58
+ const fromKeygen = await acc.importPrivateKey(JSON.stringify(SVM_KEYGEN_BYTES));
59
+ assert.strictEqual(fromKeygen.addresses[0].vm, "svm");
60
+
61
+ await assert.rejects(
62
+ acc.importPrivateKey("definitely-not-a-key"),
63
+ (err) => err.code === "INVALID_PRIVATE_KEY",
64
+ );
65
+
66
+ await assert.rejects(
67
+ acc.importPrivateKey("0x" + "0".repeat(64), "evm"),
68
+ (err) => err.code === "INVALID_PRIVATE_KEY",
69
+ );
32
70
 
33
71
  await ws.lock();
34
72
  });
@@ -1,5 +1,4 @@
1
- // 04 — Network management. Pre-loaded networks, add custom networks,
2
- // override an existing network's RPC URL, drop user networks.
1
+ // 04 — Networks: the pre-loaded set, adding, overriding an RPC URL, dropping.
3
2
 
4
3
  const test = require("node:test");
5
4
  const assert = require("node:assert");
@@ -1,5 +1,4 @@
1
- // 05 — Asset management. Pre-loaded tokens, add custom tokens,
2
- // drop user tokens, search via filter.
1
+ // 05 — Assets: the pre-loaded tokens, adding, dropping, and searching via filter.
3
2
 
4
3
  const test = require("node:test");
5
4
  const assert = require("node:assert");
@@ -1,5 +1,4 @@
1
- // 06 — Address signing. Sign personal messages and build transactions
2
- // fully offline.
1
+ // 06 — Address signing: personal messages and transaction building, fully offline.
3
2
 
4
3
  const test = require("node:test");
5
4
  const assert = require("node:assert");
@@ -1,17 +1,11 @@
1
- // 07 — Custom storage backend. Extend the abstract `Provider` class to plug
2
- // `Workspace.open()` into any storage system. This example uses an in-memory
3
- // key-value store with a trivial XOR cipher; real custom Providers should use
4
- // an authenticated cipher (AES-GCM, ChaCha20-Poly1305).
1
+ // 07 — Custom backend: extend Provider itself; the XOR cipher here is illustrative only.
5
2
 
6
3
  const test = require("node:test");
7
4
  const assert = require("node:assert");
8
5
 
9
6
  const { Workspace, Provider, Record, WativeError } = require("wative-core");
10
7
 
11
- /**
12
- * In-memory `Provider` implementation. Stores records in a Map under
13
- * filesystem-style paths, encrypted with a single-byte-header XOR scheme.
14
- */
8
+ /** In-memory Provider: records in a Map under path-like keys, XOR-"encrypted". */
15
9
  class InMemoryProvider extends Provider {
16
10
  #store = new Map();
17
11
  #dirs = new Set();
@@ -1,5 +1,4 @@
1
- // 08 — Persistence. Verify that accounts, networks, custom assets, and the
2
- // logger configuration all round-trip correctly across `lock()` + reopen.
1
+ // 08 — Persistence: accounts, networks, assets and logger config across lock/reopen.
3
2
 
4
3
  const test = require("node:test");
5
4
  const assert = require("node:assert");
@@ -1,5 +1,4 @@
1
- // 09 — Workspace search. `filter(query, objective)` returns the live object
2
- // matching `query` for the given objective: Wallet, Address, Account, or Asset.
1
+ // 09 — Search: filter(query, objective) over Wallet, Address, Account and Asset.
3
2
 
4
3
  const test = require("node:test");
5
4
  const assert = require("node:assert");
@@ -1,6 +1,4 @@
1
- // 10 — Account default network. `account.setDefaultNetwork(value)` accepts
2
- // a slug, a chain id, a hex chain id, or a Network instance — and the choice
3
- // persists across reopen.
1
+ // 10 — setDefaultNetwork accepts a slug, chain id, hex chain id or Network, and persists.
4
2
 
5
3
  const test = require("node:test");
6
4
  const assert = require("node:assert");
@@ -1,13 +1,9 @@
1
- // 11 — Subpath imports. Heavier chain helpers ship under `wative-core/artifacts/evm`
2
- // and `wative-core/artifacts/svm` so they're only pulled in when used. In your
3
- // own project: require("wative-core/artifacts/evm") / require("wative-core/artifacts/svm").
1
+ // 11 — Subpath imports: the heavier chain helpers under wative-core/artifacts/{evm,svm}.
4
2
 
5
3
  const test = require("node:test");
6
4
  const assert = require("node:assert");
7
5
 
8
- // Imported by SUBPATH, which is the whole point of this example — and what a
9
- // consumer writes. A relative path into dist/ would resolve here but nowhere
10
- // else, so it would pass in-repo while failing for every real installation.
6
+ // By SUBPATH, as a consumer writes it — a relative dist/ path would pass only in-repo.
11
7
  const evmArtifacts = require("wative-core/artifacts/evm");
12
8
  const svmArtifacts = require("wative-core/artifacts/svm");
13
9
 
@@ -1,6 +1,4 @@
1
- // 12 — Logger. Workspace exposes `ws.logger` after unlock. Write structured
2
- // logs at any level, change the level via `ws.logger.setLevel(...)`, and the
3
- // configuration persists across reopen.
1
+ // 12 — Logger: ws.logger after unlock, six levels, setLevel, and config persistence.
4
2
 
5
3
  const test = require("node:test");
6
4
  const assert = require("node:assert");
@@ -1,5 +1,4 @@
1
- // 13 — Workspace config public API. Consumers should never need Provider
2
- // record access to update business-wide settings.
1
+ // 13 — Workspace config: business-wide settings without reaching for the Provider.
3
2
 
4
3
  const test = require("node:test");
5
4
  const assert = require("node:assert");
@@ -25,9 +24,6 @@ test("workspace config — setBusinessTimezone/getConfig work from release dist"
25
24
  && !err.message.includes("Not/AZone")
26
25
  ));
27
26
 
28
- // `config` hands back a frozen snapshot, not the live object. Assigning to it
29
- // does not change the workspace — read config through `config` / `getConfig()`
30
- // and change it through the setters.
31
27
  const snapshot = ws.config;
32
28
  assert.strictEqual(Object.isFrozen(snapshot), true);
33
29
  snapshot.settlementWindow = "ny-close";
@@ -8,12 +8,6 @@ import { fileURLToPath } from "node:url";
8
8
  const here = path.dirname(fileURLToPath(import.meta.url));
9
9
 
10
10
  async function importDist() {
11
- // Import by PACKAGE SPECIFIER, not by file path. The root entry is
12
- // condition-split: a raw path to dist/index.js bypasses the exports map and
13
- // lands on the browser build, which deliberately has no filesystem backend.
14
- // "wative-core" resolves through the node condition, which is what an ESM
15
- // consumer actually gets — and still loads real ESM, so this keeps testing
16
- // that the dist needs no CJS require shims.
17
11
  return import("wative-core");
18
12
  }
19
13
 
@@ -58,9 +52,6 @@ test("ESM dist consumer signs EVM transactions without global require shims", as
58
52
  });
59
53
 
60
54
  test("ESM dist consumer signs SVM transactions without global require shims", async () => {
61
- // Regression: the SVM signing path used require() for @solana/web3.js, bs58
62
- // and tweetnacl, which throws "Dynamic require ... is not supported" under
63
- // native ESM. These are now static imports like web3 on the EVM side.
64
55
  const { Workspace } = await importDist();
65
56
  assert.equal(globalThis.require, undefined);
66
57
  const root = await mkdtemp(path.join(tmpdir(), "wative-core-esm-svm-sign-"));
@@ -1,20 +1,9 @@
1
- // 15 — Package resolution. Every example now resolves the package by its bare
2
- // name, so they all exercise the `exports` map; this file is the one that tests
3
- // the map ITSELF rather than using it incidentally — every declared subpath,
4
- // under both conditions. It therefore covers the export conditions (`import` vs
5
- // `require`), the declared subpaths, and the parity between the two builds.
6
- //
7
- // It works without installing anything because Node resolves a package's own
8
- // name from inside that package ("self-reference"), which requires `name` and
9
- // `exports` to be present — so a broken exports map fails this file outright.
1
+ // 15 — The exports map itself: every subpath, both conditions, and build parity.
10
2
 
11
3
  const test = require("node:test");
12
4
  const assert = require("node:assert");
13
5
 
14
- // "wative-core/node" is where HybridProviderV3 lives after the breaking
15
- // refactor that folded NodeHybridProvider into it. It was missing here, so the
16
- // subpath carrying the release's headline feature was the one subpath nothing
17
- // resolved.
6
+ // "wative-core/node" carries HybridProviderV3 and was once missing from this list.
18
7
  const SUBPATHS = [
19
8
  "wative-core",
20
9
  "wative-core/node",
@@ -34,37 +23,7 @@ test("every declared subpath resolves by bare name under require(), to the CJS b
34
23
  }
35
24
  });
36
25
 
37
- // A type used in a public signature but not exported is invisible to a
38
- // TypeScript consumer, who then cannot name what the method returns. That is not
39
- // visible from JavaScript, so it is asserted against the emitted declarations.
40
- //
41
- // Two things this used to get wrong, both of which let a real regression ship:
42
- //
43
- // 1. It opened ONLY `dist/index.node.d.cts` — the node+require artifact. The
44
- // root subpath emits THREE declaration files (browser/default, node+import,
45
- // node+require), and `ContainerState` is the return type of the universal
46
- // `Provider.inspectContainer()`. Deleting its export from the browser
47
- // artifact alone left this green while every bundler consumer got TS2305.
48
- // The artifact list is now derived from the `exports` map itself, so a new
49
- // subpath or condition is covered the day it is added rather than the day
50
- // someone remembers.
51
- //
52
- // 2. It asserted `\bName\b` — bare presence anywhere in the file. A name also
53
- // appears in an import specifier, in another type's signature, or in a
54
- // comment, so the assertion held whether or not the name was EXPORTED. It
55
- // now parses the export clauses and checks membership in the exported set.
56
- // Keyed by ARTIFACT, not by subpath: the browser and node root builds export
57
- // legitimately different sets (the node one adds the filesystem provider), so a
58
- // per-subpath map cannot express "required here, absent there" and would either
59
- // under-check the node build or falsely fail the browser one.
60
- //
61
- // ⚠️ This list is HAND-MAINTAINED, and that is the remaining weakness. The
62
- // checks below make the ORACLE strong — every declared artifact, and exported
63
- // rather than merely mentioned — but the INPUT is still a list someone has to
64
- // remember to extend. `OpenOptions` and `PasswordCheckContext` were both TS2305
65
- // in a shipped release precisely because nothing derived this list from the
66
- // public signatures. Deriving it (a referenced-but-not-exported set difference
67
- // over the emitted declarations) is the real fix and is not done here.
26
+ // Keyed by ARTIFACT, since the browser and node root builds export different sets.
68
27
  const EXPECTED_EXPORTS = {
69
28
  "./dist/index.d.ts": ["ContainerState", "Argon2BackendInfo", "OpenOptions", "PasswordCheckContext"],
70
29
  "./dist/index.node.d.ts": [
@@ -72,9 +31,6 @@ const EXPECTED_EXPORTS = {
72
31
  "Argon2BackendInfo",
73
32
  "OpenOptions",
74
33
  "PasswordCheckContext",
75
- // The node root re-exports the filesystem provider surface. `exports.test.ts`
76
- // pins this as a deliberate contract, and `18-envelope-v3.test.cjs` reaches
77
- // `HybridProviderV3` through the ROOT specifier, not the ./node subpath.
78
34
  "HybridProviderV3",
79
35
  "HybridProvider",
80
36
  "FileSink",
@@ -94,27 +50,11 @@ const EXPECTED_EXPORTS = {
94
50
  "./dist/node/index.d.cts": ["HybridProviderV3", "HybridProvider", "FileSink", "FileSinkOptions"],
95
51
  };
96
52
 
97
- // The `exports` map declares 10 `types` entries across 9 distinct files
98
- // (`./dist/index.d.ts` is reached by both the `browser` and `default`
99
- // conditions). Both totals are asserted EXACTLY rather than as floors: a floor
100
- // tolerates a gap, so deleting `exports["./node"].require.types` would drop the
101
- // CJS declarations for the subpath carrying the headline feature and still pass.
102
- // 32 = 4 (index.d.ts, counted twice — browser and default both resolve to it)
103
- // + 8 + 8 (the two node root artifacts) + 4 + 4 (the ./node subpath pair).
104
- // The artifacts/{evm,svm} pairs contribute none; they are checked for existence
105
- // and non-emptiness only.
53
+ // Both totals are EXACT, not floors: a floor tolerates the gap this exists to catch.
106
54
  const EXPECTED_ARTIFACTS = 10;
107
55
  const EXPECTED_ASSERTIONS = 32;
108
56
 
109
- // tsup re-exports rather than re-declares: `export { k as ContainerState } from
110
- // './chunk.cjs'`. The public name is what follows `as`, or the whole entry when
111
- // there is no alias. Direct `export declare class X` forms are collected too.
112
- //
113
- // Two shapes the two builds do NOT agree on, both of which must be handled or
114
- // the check reports a false regression: the browser artifact writes inline type
115
- // modifiers (`type Argon2BackendInfo`) where the node artifact writes the bare
116
- // name, and a name colliding with a lib type is emitted aliased (`Record$1 as
117
- // Record`). Strip the modifier first, then resolve the alias.
57
+ // tsup re-exports rather than re-declares, so strip any inline modifier then the alias.
118
58
  function exportedNames(dts) {
119
59
  const names = new Set();
120
60
  for (const m of dts.matchAll(/export\s*(?:type\s*)?\{([^}]*)\}/g)) {
@@ -153,20 +93,10 @@ function declaredTypeArtifacts(exportsMap) {
153
93
  test("every type named by a public signature is exported from every artifact that declares it", () => {
154
94
  const fs = require("node:fs");
155
95
  const path = require("node:path");
156
- // Resolve the package under test by NAME, not by `__dirname/..`. This file
157
- // runs in two layouts: in the release checkout (examples/ beside dist/, where
158
- // `..` is the package root) and copied FLAT into a scratch consumer dir that
159
- // installed the tarball (where `..` is the scratch dir's parent and holds no
160
- // package.json at all). The second layout is what CI's "published" job uses,
161
- // and `__dirname/..` made this test throw ENOENT there — so that job has
162
- // never been able to pass. The exports map declares "./package.json", so this
163
- // resolves in both.
164
96
  const root = path.dirname(require.resolve("wative-core/package.json"));
165
97
  const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
166
98
  const artifacts = declaredTypeArtifacts(pkg.exports);
167
99
 
168
- // If the exports map stops declaring types, this test would otherwise pass by
169
- // checking nothing at all — the same vacuity it exists to prevent.
170
100
  assert.strictEqual(
171
101
  artifacts.length,
172
102
  EXPECTED_ARTIFACTS,
@@ -175,11 +105,6 @@ test("every type named by a public signature is exported from every artifact tha
175
105
  `or a subpath was added and needs an entry in EXPECTED_EXPORTS.`,
176
106
  );
177
107
 
178
- // Counting `types` entries alone cannot see a subpath that declares NONE: the
179
- // total is unchanged, so the exact-count assertion above passes while the new
180
- // subpath ships with no declarations at all and its consumers get "could not
181
- // find a declaration file". Every subpath with conditions must produce at
182
- // least one.
183
108
  for (const [subpath, node] of Object.entries(pkg.exports)) {
184
109
  if (typeof node === "string") continue; // "./package.json" — a file, not a type surface
185
110
  assert.ok(
@@ -202,8 +127,6 @@ test("every type named by a public signature is exported from every artifact tha
202
127
  checked++;
203
128
  }
204
129
  }
205
- // Exact, for the same reason as the artifact count: a floor here would let a
206
- // whole artifact's expectations be deleted without the suite noticing.
207
130
  assert.strictEqual(
208
131
  checked,
209
132
  EXPECTED_ASSERTIONS,
@@ -234,7 +157,6 @@ test("the CJS and ESM builds expose an identical export surface", async () => {
234
157
  const esm = await import("wative-core");
235
158
 
236
159
  const cjsKeys = Object.keys(cjs).sort();
237
- // The ESM namespace carries a `default` interop key that the CJS object does not.
238
160
  const esmKeys = Object.keys(esm).filter((k) => k !== "default").sort();
239
161
 
240
162
  assert.deepStrictEqual(
@@ -1,13 +1,4 @@
1
- // 16 — Known-answer vectors. The other examples check that operations succeed
2
- // and that results look plausible ("is a string", "starts with 0x"). That kind
3
- // of assertion still passes if a key is derived from the wrong path, or if a
4
- // signature is produced by the wrong key — the two failures most likely to lose
5
- // someone's funds.
6
- //
7
- // This file pins exact expected values instead. Every EVM value below was taken
8
- // from the published BIP-39 / BIP-44 test vectors for the canonical "abandon …
9
- // about" mnemonic, not from this library's own output, so the assertions are
10
- // independent ground truth rather than a recording of current behaviour.
1
+ // 16 — Known-answer vectors: exact published BIP-44 values, not "looks plausible".
11
2
 
12
3
  const test = require("node:test");
13
4
  const assert = require("node:assert");
@@ -21,27 +12,21 @@ const { Workspace } = require("wative-core");
21
12
  const MNEMONIC =
22
13
  "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
23
14
 
24
- // m/44'/60'/0'/0/{0,1,2} for the mnemonic above. These are the standard vectors
25
- // reproduced by every BIP-44 implementation; a mismatch means the derivation
26
- // path or the seed derivation changed.
15
+ // m/44'/60'/0'/0/{0,1,2}: standard vectors; a mismatch means the derivation moved.
27
16
  const EVM_ADDRESSES = [
28
17
  "0x9858EfFD232B4033E47d90003D41EC34EcaEda94",
29
18
  "0x6Fac4D18c912343BF86fa7049364Dd4E424Ab9C0",
30
19
  "0xb6716976A3ebe8D39aCEB04372f22Ff8e6802D7A",
31
20
  ];
32
21
 
33
- // Solana derivation for the same mnemonic. Pinned as a regression vector: these
34
- // are this library's values, so they guard against silent drift in the SVM
35
- // derivation path rather than asserting an external standard.
22
+ // Solana, same mnemonic — this library's own values, pinned against silent drift.
36
23
  const SVM_ADDRESSES = [
37
24
  "HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk",
38
25
  "Hh8QwFUA6MtVu1qAoq12ucvFHNwCcVTV7hpWjeY1Hztb",
39
26
  "7WktogJEd2wQ9eH2oWusmcoFTgeYi6rS632UviTBJ2jm",
40
27
  ];
41
28
 
42
- // EIP-191 personal_sign of "hello" by the first EVM address. ECDSA here is
43
- // deterministic (RFC 6979), so the signature is a fixed value, and it verifies
44
- // externally as recovering to EVM_ADDRESSES[0].
29
+ // EIP-191 "hello" by the first address; RFC 6979 makes it a fixed value.
45
30
  const EVM_SIGNATURE_HELLO =
46
31
  "0x22f6b9cd7ff4f321e11181c4fe64adeea9469908fb514fbb6001fe022002dfda" +
47
32
  "1f4ec9ea436bad14a7823806487d3aeb39b22e2556590922d6a8308971a17e991c";
@@ -130,11 +115,8 @@ test("signatures are bound to the message and to the signing key", async () => {
130
115
  const first = addressFor(acc.wallets[0], "evm");
131
116
  const second = addressFor(acc.wallets[1], "evm");
132
117
 
133
- // A different message under the same key must not reuse the signature.
134
118
  assert.notStrictEqual(first.signMessage("hello!"), EVM_SIGNATURE_HELLO);
135
- // The same message under a different key must not either.
136
119
  assert.notStrictEqual(second.signMessage("hello"), EVM_SIGNATURE_HELLO);
137
- // And signing is repeatable for a given key and message.
138
120
  assert.strictEqual(first.signMessage("hello"), EVM_SIGNATURE_HELLO);
139
121
 
140
122
  await ws.lock();