@balq/node 0.1.1

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/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # @balq/node
2
+
3
+ Node.js bindings for [balq](../../README.md): a local, verified archive of
4
+ contract storage built from EIP-7928 Block-Level Access Lists. In-process
5
+ (napi-rs), no HTTP hop; reads stay available while `sync()` runs.
6
+
7
+ ```js
8
+ const { Archive, Layout, NotAvailableError } = require("@balq/node");
9
+
10
+ const ar = Archive.open("./balq.redb", { proofWindow: 0 });
11
+ ar.watch("0x3582…53ce", 114563); // from >= head + 1
12
+ await ar.sync("http://localhost:8545"); // fetch → verify → apply; returns a SyncReport
13
+
14
+ const v = ar.storageAt("0x3582…53ce", "0", 114591);
15
+ // { value: "0x…0c", provenance: "bal", setAt: 114590, index: 125 }
16
+
17
+ const layout = Layout.fromFile("./out/Playground.sol/Playground.json"); // forge artifact or bare storageLayout
18
+ const loc = layout.locate("balances[0x61Cc…ca80]");
19
+ layout.decode(loc, ar.storageAt("0x3582…53ce", loc.slot, 114591).value); // "37585"
20
+
21
+ try {
22
+ ar.storageAt("0x3582…53ce", "0", 114500);
23
+ } catch (e) {
24
+ if (e instanceof NotAvailableError) console.log(e.code); // "BeforeStart" — never null
25
+ }
26
+ ```
27
+
28
+ Every miss is a `NotAvailableError` with `code` ∈ `NotWatched | BeforeStart |
29
+ AfterHead | NotSynced | NotBootstrapped | BootstrapPending | BootstrapLost |
30
+ Internal`. Numbers are JS numbers (block heights fit); words, slots and
31
+ addresses are 0x-hex strings.
32
+
33
+ ## API
34
+
35
+ | | |
36
+ |---|---|
37
+ | `Archive.open(path, {proofWindow?, fullDetail?, allowUnverified?})` | open or create |
38
+ | `watch(addr, fromBlock)` / `unwatch(addr)` / `watchlist()` / `head()` | watchlist and head |
39
+ | `sync(rpcUrl, bootstrap = true, backupRpc?): Promise<SyncReport>` | one pass to the node's head; `backupRpc` (any archive provider) is asked only when `rpcUrl` lacks a BAL or cannot prove a slot, and is verified the same way |
40
+ | `storageAt(addr, slot, block): StorageValue` | one ordered seek |
41
+ | `history(addr, slot, from, to): HistoryEntry[]` | changes in `[from, to)` |
42
+ | `changedSlots(addr, block): string[]` | from the block index |
43
+ | `bootstrapSlot(rpcUrl, addr, slot, backupRpc?): Promise<void>` | prove a never-changed slot at head |
44
+ | `Layout.fromFile(path)` / `Layout.fromJson(s)` | solc storageLayout |
45
+ | `locate(path)` / `decode(loc, word)` / `describeSlot(slot)` / `fields()` | names ↔ slots |
46
+
47
+ ## Build locally
48
+
49
+ ```
50
+ npm ci
51
+ npx napi build --platform # needs Node >= 20.12 for the CLI; the module itself runs on >= 18
52
+ node test/smoke.mjs # against ../../testbed/balq.redb
53
+ ```
54
+
55
+ Zero logic lives in this crate — see `src/lib.rs`: argument conversion,
56
+ one call into `bal-archive` / `bal-layout`, result conversion.
57
+
58
+ ## Reading by name: `view`
59
+
60
+ ```js
61
+ const view = ar.view(proxy, layout).at(114591); // storage as of the end of block 114591
62
+
63
+ view.counter // 12n
64
+ view.balances[addr] // 37585n
65
+ view.nested[addr][7] // 0x… (raw word if the layout cannot decode it)
66
+ view.totals.index // 5000000000000000146n
67
+ view.items[3] // …
68
+ view.items.length // 8n
69
+ view.c // true
70
+ view.lastPoker // "0x61Cc…"
71
+ ```
72
+
73
+ Integers are `bigint`, bools `boolean`, addresses / bytes / undecodable
74
+ words `string`. A missing value throws `NotAvailableError` (never
75
+ `undefined`); an unknown field throws at access time.
76
+
77
+ Types: `balq typegen out/Playground.sol/Playground.json > Playground.d.ts`
78
+ (or `layout.typescript("PlaygroundView")`), then
79
+ `ar.view(proxy, layout).at<PlaygroundView>(block)` — misspelled fields
80
+ fail at compile time.
package/index.d.ts ADDED
@@ -0,0 +1,165 @@
1
+ /* auto-generated by NAPI-RS */
2
+ /* eslint-disable */
3
+ /** The archive. Safe to read from while `sync()` is in flight. */
4
+ export declare class Archive {
5
+ /** Open (or create) an archive file. */
6
+ static open(path: string, options?: ArchiveOptions | undefined | null): Archive
7
+ /** Start accumulating `address` from `fromBlock` (must be > current head). */
8
+ watch(address: string, fromBlock: number): void
9
+ /** Stop watching and delete the address's data. */
10
+ unwatch(address: string): void
11
+ /** Watched addresses with their start blocks. */
12
+ watchlist(): Array<WatchEntry>
13
+ /** Last applied block, or null if nothing was synced yet. */
14
+ head(): Head | null
15
+ /**
16
+ * Fetch, verify and apply blocks from `rpcUrl` up to its head. Bootstraps
17
+ * first-seen slots via eth_getProof unless `bootstrap === false`.
18
+ * `backupRpc` (an archive endpoint, any provider) is asked only when the
19
+ * primary lacks a block's BAL or cannot prove a slot; its answers are
20
+ * verified exactly like the primary's.
21
+ */
22
+ sync(rpcUrl: string, bootstrap?: boolean | undefined | null, backupRpc?: string | undefined | null): Promise<SyncReport>
23
+ /** Value of `slot` at the end of `block`. Throws NotAvailableError, never returns null. */
24
+ storageAt(address: string, slot: string, block: number): StorageValue
25
+ /** Changes of `slot` in `[from, to)`. */
26
+ history(address: string, slot: string, from: number, to: number): Array<HistoryEntry>
27
+ /** Slots of `address` written in `block`. */
28
+ changedSlots(address: string, block: number): Array<string>
29
+ /** Prove a never-changed slot at the archive head (lazy bootstrap). */
30
+ bootstrapSlot(rpcUrl: string, address: string, slot: string, backupRpc?: string | undefined | null): Promise<void>
31
+ }
32
+
33
+ /** A parsed solc storage layout. */
34
+ export declare class Layout {
35
+ /** From a solc storageLayout JSON string or a whole forge/hardhat artifact. */
36
+ static fromJson(json: string): Layout
37
+ /** From a file containing either of the above. */
38
+ static fromFile(path: string): Layout
39
+ /** Resolve `totals.index`, `balances[0xabc…]`, `items[2]`, `items.length`. */
40
+ locate(path: string): Location
41
+ /**
42
+ * Decode a 32-byte word at a location. Returns the value as a string
43
+ * (decimal for integers, "true"/"false", 0x-address, or the raw word).
44
+ */
45
+ decode(location: Location, wordHex: string): string
46
+ /** Every named field living in a raw slot (mapping entries excluded — keccak is one-way). */
47
+ describeSlot(slot: string): Array<NamedLocation>
48
+ /** Top-level variable names in declaration order. */
49
+ fields(): Array<string>
50
+ /**
51
+ * What a path names: "value:<uint|int|bool|address|bytes|raw>", "struct",
52
+ * "mapping", "array" or "fixedArray". Drives the `view` proxy in lib.js.
53
+ */
54
+ kindOf(path: string): string
55
+ /**
56
+ * Decode a word and say what the text is, so JS can turn it into
57
+ * `bigint` / `boolean` / `string` without guessing.
58
+ */
59
+ decodeValue(location: Location, wordHex: string): DecodedValue
60
+ /** TypeScript interface for this layout as the `view` proxy exposes it. */
61
+ typescript(name: string): string
62
+ }
63
+
64
+ /** Options for `Archive.open`. */
65
+ export interface ArchiveOptions {
66
+ /**
67
+ * Blocks behind head the node still serves eth_getProof
68
+ * (reth: `--rpc.eth-proof-window`). Default 120.
69
+ */
70
+ proofWindow?: number
71
+ /** Store every intra-block change instead of the last one. Fixed at creation. */
72
+ fullDetail?: boolean
73
+ /** Apply blocks without a BAL hash in the header. Debug only. */
74
+ allowUnverified?: boolean
75
+ }
76
+
77
+ /** A decoded word with its kind. */
78
+ export interface DecodedValue {
79
+ /** "uint" | "int" | "bool" | "address" | "bytes" | "raw" */
80
+ kind: string
81
+ /** Decimal for integers, "true"/"false", 0x-hex for addresses/bytes/raw. */
82
+ text: string
83
+ }
84
+
85
+ /** Archive head. */
86
+ export interface Head {
87
+ /** Last applied block. */
88
+ number: number
89
+ /** Its hash. */
90
+ hash: string
91
+ }
92
+
93
+ /** One recorded change. */
94
+ export interface HistoryEntry {
95
+ /** Block of the change. */
96
+ block: number
97
+ /** Position within the block. */
98
+ index: number
99
+ /** 0x-prefixed 32-byte post-value. */
100
+ value: string
101
+ /** "bal" for entries inside a valid range. */
102
+ provenance: string
103
+ }
104
+
105
+ /** Where a value lives. */
106
+ export interface Location {
107
+ /** 0x-prefixed storage slot. */
108
+ slot: string
109
+ /** Byte offset from the low-order end of the word. */
110
+ offset: number
111
+ /** Size in bytes. */
112
+ size: number
113
+ /** solc type id, e.g. `t_uint128`. */
114
+ typeId: string
115
+ }
116
+
117
+ /** A field name with its location. */
118
+ export interface NamedLocation {
119
+ /** Path such as `totals.index` or `items[3]`. */
120
+ name: string
121
+ /** Where it lives. */
122
+ location: Location
123
+ }
124
+
125
+ /** A stored word with its provenance. */
126
+ export interface StorageValue {
127
+ /** 0x-prefixed 32-byte word. */
128
+ value: string
129
+ /** "bal" | "proof" | "imported" | "unverified" */
130
+ provenance: string
131
+ /** Block at which this value was set (watch start - 1 for proven pre-values). */
132
+ setAt: number
133
+ /** Position within `setAt`. */
134
+ index: number
135
+ }
136
+
137
+ /** What one `sync()` did. */
138
+ export interface SyncReport {
139
+ /** First block this pass tried to apply. */
140
+ from?: number
141
+ /** Last block applied. */
142
+ to?: number
143
+ /** Blocks applied. */
144
+ blocksApplied: number
145
+ /** Fork point, if a reorg was rolled back. */
146
+ reorgedTo?: number
147
+ /** Slot records written. */
148
+ slotsWritten: number
149
+ /** Pre-values proven in this pass. */
150
+ bootstrapped: number
151
+ /** Slots still awaiting a proof. */
152
+ bootstrapPending: number
153
+ /** Slots whose pre-value became unobtainable. */
154
+ bootstrapLost: number
155
+ /** Blocks applied without a BAL hash (debug only). */
156
+ unverifiedBlocks: number
157
+ }
158
+
159
+ /** One watchlist entry. */
160
+ export interface WatchEntry {
161
+ /** Watched address. */
162
+ address: string
163
+ /** First block covered. */
164
+ from: number
165
+ }
package/index.js ADDED
@@ -0,0 +1,704 @@
1
+ // prettier-ignore
2
+ /* eslint-disable */
3
+ // @ts-nocheck
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ const { readFileSync } = require('fs')
7
+ let nativeBinding = null
8
+ const loadErrors = []
9
+
10
+ const isMusl = () => {
11
+ let musl = false
12
+ if (process.platform === 'linux') {
13
+ musl = isMuslFromFilesystem()
14
+ if (musl === null) {
15
+ musl = isMuslFromReport()
16
+ }
17
+ if (musl === null) {
18
+ musl = isMuslFromChildProcess()
19
+ }
20
+ }
21
+ return musl
22
+ }
23
+
24
+ const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
25
+
26
+ const isMuslFromFilesystem = () => {
27
+ try {
28
+ return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
29
+ } catch {
30
+ return null
31
+ }
32
+ }
33
+
34
+ const isMuslFromReport = () => {
35
+ let report = null
36
+ if (process.report && typeof process.report.getReport === 'function') {
37
+ process.report.excludeNetwork = true
38
+ report = process.report.getReport()
39
+ }
40
+ if (!report) {
41
+ return null
42
+ }
43
+ if (report.header && report.header.glibcVersionRuntime) {
44
+ return false
45
+ }
46
+ if (Array.isArray(report.sharedObjects)) {
47
+ if (report.sharedObjects.some(isFileMusl)) {
48
+ return true
49
+ }
50
+ }
51
+ return false
52
+ }
53
+
54
+ const isMuslFromChildProcess = () => {
55
+ try {
56
+ return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
57
+ } catch (e) {
58
+ // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
59
+ return false
60
+ }
61
+ }
62
+
63
+ function requireNative() {
64
+ if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
65
+ try {
66
+ return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
67
+ } catch (err) {
68
+ loadErrors.push(err)
69
+ }
70
+ } else if (process.platform === 'android') {
71
+ if (process.arch === 'arm64') {
72
+ try {
73
+ return require('./bal-node.android-arm64.node')
74
+ } catch (e) {
75
+ loadErrors.push(e)
76
+ }
77
+ try {
78
+ const binding = require('balq-android-arm64')
79
+ const bindingPackageVersion = require('balq-android-arm64/package.json').version
80
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
81
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
82
+ }
83
+ return binding
84
+ } catch (e) {
85
+ loadErrors.push(e)
86
+ }
87
+ } else if (process.arch === 'arm') {
88
+ try {
89
+ return require('./bal-node.android-arm-eabi.node')
90
+ } catch (e) {
91
+ loadErrors.push(e)
92
+ }
93
+ try {
94
+ const binding = require('balq-android-arm-eabi')
95
+ const bindingPackageVersion = require('balq-android-arm-eabi/package.json').version
96
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
97
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
98
+ }
99
+ return binding
100
+ } catch (e) {
101
+ loadErrors.push(e)
102
+ }
103
+ } else {
104
+ loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
105
+ }
106
+ } else if (process.platform === 'win32') {
107
+ if (process.arch === 'x64') {
108
+ if ((process.config && process.config.variables && process.config.variables.shlib_suffix === 'dll.a') || (process.config && process.config.variables && process.config.variables.node_target_type === 'shared_library')) {
109
+ try {
110
+ return require('./bal-node.win32-x64-gnu.node')
111
+ } catch (e) {
112
+ loadErrors.push(e)
113
+ }
114
+ try {
115
+ const binding = require('balq-win32-x64-gnu')
116
+ const bindingPackageVersion = require('balq-win32-x64-gnu/package.json').version
117
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
118
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
119
+ }
120
+ return binding
121
+ } catch (e) {
122
+ loadErrors.push(e)
123
+ }
124
+ } else {
125
+ try {
126
+ return require('./bal-node.win32-x64-msvc.node')
127
+ } catch (e) {
128
+ loadErrors.push(e)
129
+ }
130
+ try {
131
+ const binding = require('balq-win32-x64-msvc')
132
+ const bindingPackageVersion = require('balq-win32-x64-msvc/package.json').version
133
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
134
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
135
+ }
136
+ return binding
137
+ } catch (e) {
138
+ loadErrors.push(e)
139
+ }
140
+ }
141
+ } else if (process.arch === 'ia32') {
142
+ try {
143
+ return require('./bal-node.win32-ia32-msvc.node')
144
+ } catch (e) {
145
+ loadErrors.push(e)
146
+ }
147
+ try {
148
+ const binding = require('balq-win32-ia32-msvc')
149
+ const bindingPackageVersion = require('balq-win32-ia32-msvc/package.json').version
150
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
151
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
152
+ }
153
+ return binding
154
+ } catch (e) {
155
+ loadErrors.push(e)
156
+ }
157
+ } else if (process.arch === 'arm64') {
158
+ try {
159
+ return require('./bal-node.win32-arm64-msvc.node')
160
+ } catch (e) {
161
+ loadErrors.push(e)
162
+ }
163
+ try {
164
+ const binding = require('balq-win32-arm64-msvc')
165
+ const bindingPackageVersion = require('balq-win32-arm64-msvc/package.json').version
166
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
167
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
168
+ }
169
+ return binding
170
+ } catch (e) {
171
+ loadErrors.push(e)
172
+ }
173
+ } else {
174
+ loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
175
+ }
176
+ } else if (process.platform === 'darwin') {
177
+ try {
178
+ return require('./bal-node.darwin-universal.node')
179
+ } catch (e) {
180
+ loadErrors.push(e)
181
+ }
182
+ try {
183
+ const binding = require('balq-darwin-universal')
184
+ const bindingPackageVersion = require('balq-darwin-universal/package.json').version
185
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
186
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
187
+ }
188
+ return binding
189
+ } catch (e) {
190
+ loadErrors.push(e)
191
+ }
192
+ if (process.arch === 'x64') {
193
+ try {
194
+ return require('./bal-node.darwin-x64.node')
195
+ } catch (e) {
196
+ loadErrors.push(e)
197
+ }
198
+ try {
199
+ const binding = require('balq-darwin-x64')
200
+ const bindingPackageVersion = require('balq-darwin-x64/package.json').version
201
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
202
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
203
+ }
204
+ return binding
205
+ } catch (e) {
206
+ loadErrors.push(e)
207
+ }
208
+ } else if (process.arch === 'arm64') {
209
+ try {
210
+ return require('./bal-node.darwin-arm64.node')
211
+ } catch (e) {
212
+ loadErrors.push(e)
213
+ }
214
+ try {
215
+ const binding = require('balq-darwin-arm64')
216
+ const bindingPackageVersion = require('balq-darwin-arm64/package.json').version
217
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
218
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
219
+ }
220
+ return binding
221
+ } catch (e) {
222
+ loadErrors.push(e)
223
+ }
224
+ } else {
225
+ loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
226
+ }
227
+ } else if (process.platform === 'freebsd') {
228
+ if (process.arch === 'x64') {
229
+ try {
230
+ return require('./bal-node.freebsd-x64.node')
231
+ } catch (e) {
232
+ loadErrors.push(e)
233
+ }
234
+ try {
235
+ const binding = require('balq-freebsd-x64')
236
+ const bindingPackageVersion = require('balq-freebsd-x64/package.json').version
237
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
238
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
239
+ }
240
+ return binding
241
+ } catch (e) {
242
+ loadErrors.push(e)
243
+ }
244
+ } else if (process.arch === 'arm64') {
245
+ try {
246
+ return require('./bal-node.freebsd-arm64.node')
247
+ } catch (e) {
248
+ loadErrors.push(e)
249
+ }
250
+ try {
251
+ const binding = require('balq-freebsd-arm64')
252
+ const bindingPackageVersion = require('balq-freebsd-arm64/package.json').version
253
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
254
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
255
+ }
256
+ return binding
257
+ } catch (e) {
258
+ loadErrors.push(e)
259
+ }
260
+ } else {
261
+ loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
262
+ }
263
+ } else if (process.platform === 'linux') {
264
+ if (process.arch === 'x64') {
265
+ if (isMusl()) {
266
+ try {
267
+ return require('./bal-node.linux-x64-musl.node')
268
+ } catch (e) {
269
+ loadErrors.push(e)
270
+ }
271
+ try {
272
+ const binding = require('balq-linux-x64-musl')
273
+ const bindingPackageVersion = require('balq-linux-x64-musl/package.json').version
274
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
275
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
276
+ }
277
+ return binding
278
+ } catch (e) {
279
+ loadErrors.push(e)
280
+ }
281
+ } else {
282
+ try {
283
+ return require('./bal-node.linux-x64-gnu.node')
284
+ } catch (e) {
285
+ loadErrors.push(e)
286
+ }
287
+ try {
288
+ const binding = require('balq-linux-x64-gnu')
289
+ const bindingPackageVersion = require('balq-linux-x64-gnu/package.json').version
290
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
291
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
292
+ }
293
+ return binding
294
+ } catch (e) {
295
+ loadErrors.push(e)
296
+ }
297
+ }
298
+ } else if (process.arch === 'arm64') {
299
+ if (isMusl()) {
300
+ try {
301
+ return require('./bal-node.linux-arm64-musl.node')
302
+ } catch (e) {
303
+ loadErrors.push(e)
304
+ }
305
+ try {
306
+ const binding = require('balq-linux-arm64-musl')
307
+ const bindingPackageVersion = require('balq-linux-arm64-musl/package.json').version
308
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
309
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
310
+ }
311
+ return binding
312
+ } catch (e) {
313
+ loadErrors.push(e)
314
+ }
315
+ } else {
316
+ try {
317
+ return require('./bal-node.linux-arm64-gnu.node')
318
+ } catch (e) {
319
+ loadErrors.push(e)
320
+ }
321
+ try {
322
+ const binding = require('balq-linux-arm64-gnu')
323
+ const bindingPackageVersion = require('balq-linux-arm64-gnu/package.json').version
324
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
325
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
326
+ }
327
+ return binding
328
+ } catch (e) {
329
+ loadErrors.push(e)
330
+ }
331
+ }
332
+ } else if (process.arch === 'arm') {
333
+ if (isMusl()) {
334
+ try {
335
+ return require('./bal-node.linux-arm-musleabihf.node')
336
+ } catch (e) {
337
+ loadErrors.push(e)
338
+ }
339
+ try {
340
+ const binding = require('balq-linux-arm-musleabihf')
341
+ const bindingPackageVersion = require('balq-linux-arm-musleabihf/package.json').version
342
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
343
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
344
+ }
345
+ return binding
346
+ } catch (e) {
347
+ loadErrors.push(e)
348
+ }
349
+ } else {
350
+ try {
351
+ return require('./bal-node.linux-arm-gnueabihf.node')
352
+ } catch (e) {
353
+ loadErrors.push(e)
354
+ }
355
+ try {
356
+ const binding = require('balq-linux-arm-gnueabihf')
357
+ const bindingPackageVersion = require('balq-linux-arm-gnueabihf/package.json').version
358
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
359
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
360
+ }
361
+ return binding
362
+ } catch (e) {
363
+ loadErrors.push(e)
364
+ }
365
+ }
366
+ } else if (process.arch === 'loong64') {
367
+ if (isMusl()) {
368
+ try {
369
+ return require('./bal-node.linux-loong64-musl.node')
370
+ } catch (e) {
371
+ loadErrors.push(e)
372
+ }
373
+ try {
374
+ const binding = require('balq-linux-loong64-musl')
375
+ const bindingPackageVersion = require('balq-linux-loong64-musl/package.json').version
376
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
377
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
378
+ }
379
+ return binding
380
+ } catch (e) {
381
+ loadErrors.push(e)
382
+ }
383
+ } else {
384
+ try {
385
+ return require('./bal-node.linux-loong64-gnu.node')
386
+ } catch (e) {
387
+ loadErrors.push(e)
388
+ }
389
+ try {
390
+ const binding = require('balq-linux-loong64-gnu')
391
+ const bindingPackageVersion = require('balq-linux-loong64-gnu/package.json').version
392
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
393
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
394
+ }
395
+ return binding
396
+ } catch (e) {
397
+ loadErrors.push(e)
398
+ }
399
+ }
400
+ } else if (process.arch === 'riscv64') {
401
+ if (isMusl()) {
402
+ try {
403
+ return require('./bal-node.linux-riscv64-musl.node')
404
+ } catch (e) {
405
+ loadErrors.push(e)
406
+ }
407
+ try {
408
+ const binding = require('balq-linux-riscv64-musl')
409
+ const bindingPackageVersion = require('balq-linux-riscv64-musl/package.json').version
410
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
411
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
412
+ }
413
+ return binding
414
+ } catch (e) {
415
+ loadErrors.push(e)
416
+ }
417
+ } else {
418
+ try {
419
+ return require('./bal-node.linux-riscv64-gnu.node')
420
+ } catch (e) {
421
+ loadErrors.push(e)
422
+ }
423
+ try {
424
+ const binding = require('balq-linux-riscv64-gnu')
425
+ const bindingPackageVersion = require('balq-linux-riscv64-gnu/package.json').version
426
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
427
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
428
+ }
429
+ return binding
430
+ } catch (e) {
431
+ loadErrors.push(e)
432
+ }
433
+ }
434
+ } else if (process.arch === 'ppc64') {
435
+ try {
436
+ return require('./bal-node.linux-ppc64-gnu.node')
437
+ } catch (e) {
438
+ loadErrors.push(e)
439
+ }
440
+ try {
441
+ const binding = require('balq-linux-ppc64-gnu')
442
+ const bindingPackageVersion = require('balq-linux-ppc64-gnu/package.json').version
443
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
444
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
445
+ }
446
+ return binding
447
+ } catch (e) {
448
+ loadErrors.push(e)
449
+ }
450
+ } else if (process.arch === 's390x') {
451
+ try {
452
+ return require('./bal-node.linux-s390x-gnu.node')
453
+ } catch (e) {
454
+ loadErrors.push(e)
455
+ }
456
+ try {
457
+ const binding = require('balq-linux-s390x-gnu')
458
+ const bindingPackageVersion = require('balq-linux-s390x-gnu/package.json').version
459
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
460
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
461
+ }
462
+ return binding
463
+ } catch (e) {
464
+ loadErrors.push(e)
465
+ }
466
+ } else {
467
+ loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
468
+ }
469
+ } else if (process.platform === 'openharmony') {
470
+ if (process.arch === 'arm64') {
471
+ try {
472
+ return require('./bal-node.openharmony-arm64.node')
473
+ } catch (e) {
474
+ loadErrors.push(e)
475
+ }
476
+ try {
477
+ const binding = require('balq-openharmony-arm64')
478
+ const bindingPackageVersion = require('balq-openharmony-arm64/package.json').version
479
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
480
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
481
+ }
482
+ return binding
483
+ } catch (e) {
484
+ loadErrors.push(e)
485
+ }
486
+ } else if (process.arch === 'x64') {
487
+ try {
488
+ return require('./bal-node.openharmony-x64.node')
489
+ } catch (e) {
490
+ loadErrors.push(e)
491
+ }
492
+ try {
493
+ const binding = require('balq-openharmony-x64')
494
+ const bindingPackageVersion = require('balq-openharmony-x64/package.json').version
495
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
496
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
497
+ }
498
+ return binding
499
+ } catch (e) {
500
+ loadErrors.push(e)
501
+ }
502
+ } else if (process.arch === 'arm') {
503
+ try {
504
+ return require('./bal-node.openharmony-arm.node')
505
+ } catch (e) {
506
+ loadErrors.push(e)
507
+ }
508
+ try {
509
+ const binding = require('balq-openharmony-arm')
510
+ const bindingPackageVersion = require('balq-openharmony-arm/package.json').version
511
+ if (bindingPackageVersion !== '0.1.1' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
512
+ throw new Error(`Native binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
513
+ }
514
+ return binding
515
+ } catch (e) {
516
+ loadErrors.push(e)
517
+ }
518
+ } else {
519
+ loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))
520
+ }
521
+ } else {
522
+ loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
523
+ }
524
+ }
525
+
526
+ function createLoadErrorChain(errors) {
527
+ return errors.reduce((previous, current) => {
528
+ let message
529
+ try {
530
+ message =
531
+ current && typeof current.message === 'string'
532
+ ? current.message
533
+ : String(current)
534
+ } catch {
535
+ message = 'Unknown error'
536
+ }
537
+ const error = new Error(message)
538
+ error.cause = previous
539
+ return error
540
+ }, null)
541
+ }
542
+
543
+ // NAPI_RS_FORCE_WASI is a tri-state flag:
544
+ // unset / any other value → native binding preferred, WASI is only a fallback
545
+ // 'true' → prefer WASI, but retain native as a lazy fallback
546
+ // 'error' → require WASI without initializing a native fallback
547
+ // Treating any non-empty string as truthy (the historical behavior) meant
548
+ // NAPI_RS_FORCE_WASI=false, NAPI_RS_FORCE_WASI=0, etc. inadvertently triggered
549
+ // the WASI path, causing ENOENT for packages shipped without a .wasi.cjs file.
550
+ //
551
+ // NAPI_RS_WASI_FLAVOR selects one exact generated flavor and implies strict
552
+ // WASI loading. It never crosses into another flavor or falls back to native.
553
+ const __napiWasiFlavors = ["wasm32-wasi"]
554
+ const __napiWasiFlavor = process.env.NAPI_RS_WASI_FLAVOR
555
+ const __napiWasiFlavorRequested =
556
+ typeof __napiWasiFlavor === 'string' && __napiWasiFlavor.length > 0
557
+ if (
558
+ __napiWasiFlavorRequested &&
559
+ __napiWasiFlavors.indexOf(__napiWasiFlavor) === -1
560
+ ) {
561
+ throw new Error(
562
+ 'Unsupported WASI flavor "' +
563
+ __napiWasiFlavor +
564
+ '". Available flavors: ' +
565
+ __napiWasiFlavors.join(', '),
566
+ )
567
+ }
568
+ const forceWasiError = process.env.NAPI_RS_FORCE_WASI === 'error'
569
+ const forceWasi =
570
+ process.env.NAPI_RS_FORCE_WASI === 'true' ||
571
+ forceWasiError ||
572
+ __napiWasiFlavorRequested
573
+
574
+ if (!forceWasi) {
575
+ nativeBinding = requireNative()
576
+ }
577
+
578
+ if (!nativeBinding || forceWasi) {
579
+ let wasiBinding = null
580
+ let wasiBindingLoaded = false
581
+ const wasiBindingErrors = []
582
+ const __napiWasiResolveCandidate = (specifier, isPackage, localArtifacts) => {
583
+ try {
584
+ require.resolve(specifier)
585
+ } catch (resolveError) {
586
+ if (!resolveError || resolveError.code !== 'MODULE_NOT_FOUND') {
587
+ throw resolveError
588
+ }
589
+ if (isPackage) {
590
+ try {
591
+ require.resolve(specifier + '/package.json')
592
+ } catch (packageError) {
593
+ if (packageError && packageError.code === 'MODULE_NOT_FOUND') {
594
+ return resolveError
595
+ }
596
+ // An exports restriction proves the package exists even when its
597
+ // package.json is not public. Preserve the root resolution failure.
598
+ throw resolveError
599
+ }
600
+ // The package exists but its main/export target is broken.
601
+ throw resolveError
602
+ }
603
+ return resolveError
604
+ }
605
+ if (localArtifacts) {
606
+ let artifactError = null
607
+ for (let i = 0; i < localArtifacts.length; i++) {
608
+ try {
609
+ require.resolve(localArtifacts[i])
610
+ return null
611
+ } catch (resolveError) {
612
+ if (!resolveError || resolveError.code !== 'MODULE_NOT_FOUND') {
613
+ throw resolveError
614
+ }
615
+ artifactError = resolveError
616
+ }
617
+ }
618
+ return artifactError
619
+ }
620
+ return null
621
+ }
622
+ if (!wasiBindingLoaded && (!__napiWasiFlavorRequested || __napiWasiFlavor === "wasm32-wasi")) {
623
+ let candidateError = null
624
+ let candidateFailed = false
625
+ try {
626
+ candidateError = __napiWasiResolveCandidate('./bal-node.wasi.cjs', false, ["./bal-node.wasm32-wasi.debug.wasm","./bal-node.wasm32-wasi.wasm"])
627
+ candidateFailed = candidateError !== null
628
+ if (!candidateFailed) {
629
+ wasiBinding = require('./bal-node.wasi.cjs')
630
+ nativeBinding = wasiBinding
631
+ wasiBindingLoaded = true
632
+ }
633
+ } catch (err) {
634
+ candidateError = err
635
+ candidateFailed = true
636
+ }
637
+ if (candidateFailed) {
638
+ wasiBindingErrors.push(candidateError)
639
+ loadErrors.push(candidateError)
640
+ }
641
+ }
642
+ if (!wasiBindingLoaded && (!__napiWasiFlavorRequested || __napiWasiFlavor === "wasm32-wasi")) {
643
+ let candidateError = null
644
+ let candidateFailed = false
645
+ try {
646
+ candidateError = __napiWasiResolveCandidate('balq-wasm32-wasi', true, undefined)
647
+ candidateFailed = candidateError !== null
648
+ if (!candidateFailed) {
649
+ if (process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
650
+ const bindingPackageVersion = require('balq-wasm32-wasi/package.json').version
651
+ if (bindingPackageVersion !== '0.1.1') {
652
+ throw new Error(`WASI binding package version mismatch, expected 0.1.1 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
653
+ }
654
+ }
655
+ wasiBinding = require('balq-wasm32-wasi')
656
+ nativeBinding = wasiBinding
657
+ wasiBindingLoaded = true
658
+ }
659
+ } catch (err) {
660
+ candidateError = err
661
+ candidateFailed = true
662
+ }
663
+ if (candidateFailed) {
664
+ wasiBindingErrors.push(candidateError)
665
+ loadErrors.push(candidateError)
666
+ }
667
+ }
668
+ if (
669
+ !wasiBindingLoaded &&
670
+ forceWasi &&
671
+ !forceWasiError &&
672
+ !__napiWasiFlavorRequested
673
+ ) {
674
+ nativeBinding = requireNative()
675
+ }
676
+ if ((forceWasiError || __napiWasiFlavorRequested) && !wasiBindingLoaded) {
677
+ const error = new Error(
678
+ __napiWasiFlavorRequested
679
+ ? 'WASI binding for flavor "' + __napiWasiFlavor + '" not found'
680
+ : 'WASI binding not found and NAPI_RS_FORCE_WASI is set to error',
681
+ )
682
+ error.cause = createLoadErrorChain(wasiBindingErrors)
683
+ throw error
684
+ }
685
+ }
686
+
687
+ if (!nativeBinding) {
688
+ if (loadErrors.length > 0) {
689
+ const error = new Error(
690
+ `Cannot find native binding. ` +
691
+ `npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +
692
+ 'Please try `npm i` again after removing both package-lock.json and node_modules directory.',
693
+ )
694
+ // assign instead of the `new Error(message, { cause })` options form,
695
+ // which Node < 16.9 silently ignores
696
+ error.cause = createLoadErrorChain(loadErrors)
697
+ throw error
698
+ }
699
+ throw new Error(`Failed to load native binding`)
700
+ }
701
+
702
+ module.exports = nativeBinding
703
+ module.exports.Archive = nativeBinding.Archive
704
+ module.exports.Layout = nativeBinding.Layout
package/lib.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ export * from "./index";
2
+ import { Archive as NativeArchive, Layout } from "./index";
3
+
4
+ /** Thrown by reads when there is no value — never `null`. */
5
+ export class NotAvailableError extends Error {
6
+ name: "NotAvailableError";
7
+ code:
8
+ | "NotWatched"
9
+ | "BeforeStart"
10
+ | "AfterHead"
11
+ | "NotSynced"
12
+ | "InvalidRange"
13
+ | "NotBootstrapped"
14
+ | "BootstrapPending"
15
+ | "BootstrapLost"
16
+ | "Internal";
17
+ }
18
+
19
+ /**
20
+ * Storage of one contract at one block, read by variable name.
21
+ * Generate a precise type with `balq typegen <layout> --name MyContractView`
22
+ * (or `layout.typescript("MyContractView")`) and pass it as `T`.
23
+ */
24
+ export type StorageView<T = any> = T;
25
+
26
+ export interface ContractView {
27
+ /** Variables of the contract at the end of `block`. */
28
+ at<T = any>(block: number): StorageView<T>;
29
+ }
30
+
31
+ declare module "./index" {
32
+ interface Archive {
33
+ /** Read storage by name through a solc layout; see `at()`. */
34
+ view(address: string, layout: Layout): ContractView;
35
+ }
36
+ }
package/lib.js ADDED
@@ -0,0 +1,119 @@
1
+ // Thin wrapper over the generated `index.js`:
2
+ // - turns `[NotAvailable:Code] …` errors from Rust into `NotAvailableError`
3
+ // with a `.code`;
4
+ // - adds `archive.view(address, layout).at(block)`, a proxy that reads the
5
+ // contract's variables by name, the way they are written in Solidity.
6
+ // No archive or layout logic lives here; every read goes through the native
7
+ // `locate` / `kindOf` / `storageAt` / `decodeValue`.
8
+
9
+ const native = require("./index.js");
10
+
11
+ class NotAvailableError extends Error {
12
+ constructor(code, message) {
13
+ super(message);
14
+ this.name = "NotAvailableError";
15
+ this.code = code;
16
+ }
17
+ }
18
+
19
+ const RE = /^\[NotAvailable:([A-Za-z]+)\] (.*)$/s;
20
+
21
+ function convert(e) {
22
+ const m = typeof e?.message === "string" && RE.exec(e.message);
23
+ return m ? new NotAvailableError(m[1], m[2]) : e;
24
+ }
25
+
26
+ function wrap(obj, names) {
27
+ for (const n of names) {
28
+ const orig = obj[n];
29
+ obj[n] = function (...args) {
30
+ try {
31
+ const r = orig.apply(this, args);
32
+ return r instanceof Promise ? r.catch((e) => { throw convert(e); }) : r;
33
+ } catch (e) {
34
+ throw convert(e);
35
+ }
36
+ };
37
+ }
38
+ }
39
+
40
+ wrap(native.Archive.prototype, ["storageAt", "history", "changedSlots", "sync", "bootstrapSlot"]);
41
+
42
+ // ---- view -----------------------------------------------------------------
43
+
44
+ function toJs(decoded) {
45
+ switch (decoded.kind) {
46
+ case "uint":
47
+ case "int":
48
+ return BigInt(decoded.text);
49
+ case "bool":
50
+ return decoded.text === "true";
51
+ default:
52
+ return decoded.text; // address, bytes, raw: 0x-hex
53
+ }
54
+ }
55
+
56
+ // Properties JS itself probes on any object; never storage fields.
57
+ const PASSTHROUGH = new Set(["then", "toJSON", "constructor", "valueOf", "toString", "inspect"]);
58
+
59
+ function readLeaf(archive, address, layout, block, path) {
60
+ const loc = layout.locate(path);
61
+ const v = archive.storageAt(address, loc.slot, block);
62
+ return toJs(layout.decodeValue(loc, v.value));
63
+ }
64
+
65
+ /**
66
+ * A proxy for the container at `path` ("" = the contract itself).
67
+ * Property access extends the path according to the container's kind and
68
+ * either descends (struct / mapping / array) or reads a leaf value.
69
+ */
70
+ function container(archive, address, layout, block, path, kind) {
71
+ const extend = (prop) => {
72
+ if (kind === "struct" || kind === "") return path ? `${path}.${prop}` : prop;
73
+ if (kind === "array" && prop === "length") return `${path}.length`;
74
+ return `${path}[${prop}]`; // mapping, array, fixedArray
75
+ };
76
+ return new Proxy(Object.create(null), {
77
+ get(_, prop) {
78
+ if (typeof prop === "symbol" || PASSTHROUGH.has(prop)) return undefined;
79
+ const next = extend(String(prop));
80
+ const k = layout.kindOf(next); // throws for unknown fields
81
+ if (k.startsWith("value:")) return readLeaf(archive, address, layout, block, next);
82
+ return container(archive, address, layout, block, next, k);
83
+ },
84
+ has(_, prop) {
85
+ if (typeof prop === "symbol") return false;
86
+ try {
87
+ layout.kindOf(extend(String(prop)));
88
+ return true;
89
+ } catch {
90
+ return false;
91
+ }
92
+ },
93
+ ownKeys() {
94
+ return kind === "" || !path ? layout.fields() : [];
95
+ },
96
+ getOwnPropertyDescriptor(_, prop) {
97
+ return { enumerable: true, configurable: true, value: undefined };
98
+ },
99
+ });
100
+ }
101
+
102
+ /**
103
+ * `archive.view(address, layout)` → `{ at(block) }`.
104
+ * `at(block)` returns an object whose properties are the contract's storage
105
+ * variables at the end of `block`: `view.counter`, `view.balances[addr]`,
106
+ * `view.totals.index`, `view.items[3]`, `view.items.length`.
107
+ * Integers are `bigint`, bools `boolean`, addresses/bytes/raw words `string`.
108
+ * A missing value throws `NotAvailableError` — never `undefined`.
109
+ */
110
+ native.Archive.prototype.view = function (address, layout) {
111
+ const archive = this;
112
+ return {
113
+ at(block) {
114
+ return container(archive, address, layout, block, "", "");
115
+ },
116
+ };
117
+ };
118
+
119
+ module.exports = { ...native, NotAvailableError };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@balq/node",
3
+ "version": "0.1.1",
4
+ "description": "Local, verified archive of contract storage built from EIP-7928 BALs — Node.js bindings",
5
+ "main": "lib.js",
6
+ "types": "lib.d.ts",
7
+ "license": "MIT OR Apache-2.0",
8
+ "files": [
9
+ "lib.js",
10
+ "lib.d.ts",
11
+ "index.js",
12
+ "index.d.ts"
13
+ ],
14
+ "napi": {
15
+ "binaryName": "bal-node",
16
+ "targets": [
17
+ "x86_64-pc-windows-msvc",
18
+ "x86_64-unknown-linux-gnu",
19
+ "aarch64-unknown-linux-gnu",
20
+ "x86_64-apple-darwin",
21
+ "aarch64-apple-darwin"
22
+ ]
23
+ },
24
+ "engines": {
25
+ "node": ">= 18"
26
+ },
27
+ "scripts": {
28
+ "build": "napi build --release --platform",
29
+ "build:debug": "napi build --platform",
30
+ "test": "node test/smoke.mjs"
31
+ },
32
+ "devDependencies": {
33
+ "@napi-rs/cli": "^3"
34
+ },
35
+ "optionalDependencies": {
36
+ "@balq/node-win32-x64-msvc": "0.1.1",
37
+ "@balq/node-linux-x64-gnu": "0.1.1",
38
+ "@balq/node-linux-arm64-gnu": "0.1.1",
39
+ "@balq/node-darwin-x64": "0.1.1",
40
+ "@balq/node-darwin-arm64": "0.1.1"
41
+ }
42
+ }