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.
- package/CHANGELOG.md +20 -0
- package/README.md +59 -55
- package/dist/artifacts/evm/index.cjs +1 -1
- package/dist/artifacts/evm/index.d.cts +2 -2
- package/dist/artifacts/evm/index.d.ts +2 -2
- package/dist/artifacts/evm/index.js +1 -1
- package/dist/artifacts/svm/index.cjs +1 -1
- package/dist/artifacts/svm/index.d.cts +3 -3
- package/dist/artifacts/svm/index.d.ts +3 -3
- package/dist/artifacts/svm/index.js +1 -1
- package/dist/{container-provider--npoeR2N.d.ts → container-provider-B8M0ylrK.d.ts} +1 -1
- package/dist/{container-provider-Ca_5z1uQ.d.cts → container-provider-YxyMpkhG.d.cts} +1 -1
- package/dist/{evm-EHpaW_sK.d.ts → evm-Bo3Dy492.d.ts} +1 -1
- package/dist/{evm-hSJJOVG3.d.cts → evm-DA6Sh6jv.d.cts} +1 -1
- package/dist/{file-sink-Bk5zYHw-.d.ts → file-sink-Be5iLbuT.d.ts} +2 -2
- package/dist/{file-sink-7ZAutH45.d.cts → file-sink-KCCpKHRj.d.cts} +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1 -1
- package/dist/index.node.cjs +1 -1
- package/dist/index.node.d.cts +5 -5
- package/dist/index.node.d.ts +5 -5
- package/dist/index.node.js +1 -1
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.cts +3 -3
- package/dist/node/index.d.ts +3 -3
- package/dist/node/index.js +1 -1
- package/dist/{svm-B5q8YaV9.d.ts → svm-B51NevS8.d.ts} +1 -1
- package/dist/{svm-CXFwmeMo.d.cts → svm-DswX6XRv.d.cts} +1 -1
- package/dist/{workspace-kq3QyAgm.d.cts → workspace-CBxKR80D.d.cts} +7 -43
- package/dist/{workspace-kq3QyAgm.d.ts → workspace-CBxKR80D.d.ts} +7 -43
- package/examples/01-quick-start.test.cjs +1 -2
- package/examples/02-hd-account.test.cjs +1 -2
- package/examples/03-pk-account.test.cjs +39 -1
- package/examples/04-network-management.test.cjs +1 -2
- package/examples/05-asset-management.test.cjs +1 -2
- package/examples/06-address-signing.test.cjs +1 -2
- package/examples/07-custom-provider.test.cjs +2 -8
- package/examples/08-persistence.test.cjs +1 -2
- package/examples/09-workspace-filter.test.cjs +1 -2
- package/examples/10-default-network.test.cjs +1 -3
- package/examples/11-subpath-imports.test.cjs +2 -6
- package/examples/12-logger.test.cjs +1 -3
- package/examples/13-workspace-config.test.cjs +1 -5
- package/examples/14-esm-runtime.test.mjs +0 -9
- package/examples/15-package-resolution.test.cjs +5 -83
- package/examples/16-known-answer-vectors.test.cjs +4 -22
- package/examples/17-locked-state.test.cjs +2 -23
- package/examples/18-envelope-v3.test.cjs +2 -21
- package/examples/19-simple-provider.test.cjs +1 -19
- package/examples/README.md +1 -1
- package/package.json +1 -1
|
@@ -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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 —
|
|
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 —
|
|
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,17 +1,11 @@
|
|
|
1
|
-
// 07 — Custom
|
|
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
|
|
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 —
|
|
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 —
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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 —
|
|
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"
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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}
|
|
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
|
|
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
|
|
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();
|