wao 0.40.2 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/accounts-web.js +25 -0
- package/cjs/accounts.js +38 -0
- package/cjs/adaptor-base.js +504 -287
- package/cjs/adaptor-cf.js +42 -0
- package/cjs/ao-loader.js +1 -1
- package/cjs/ao.js +18 -6
- package/cjs/aoconnect-base.js +1010 -546
- package/cjs/aoconnect-cf.js +24 -0
- package/cjs/ar-remote.js +277 -0
- package/cjs/armem-base.js +822 -211
- package/cjs/armem-cf.js +128 -0
- package/cjs/armem.js +11 -5
- package/cjs/bar.js +511 -173
- package/cjs/car.js +37 -0
- package/cjs/cf-env.js +54 -0
- package/cjs/cf.js +76 -0
- package/cjs/cli.js +12 -6
- package/cjs/create.js +1 -1
- package/cjs/devs.js +53 -0
- package/cjs/dodb.js +116 -0
- package/cjs/hb.js +136 -53
- package/cjs/hyperbeam.js +85 -44
- package/cjs/keygen.js +94 -0
- package/cjs/run.js +4 -1
- package/cjs/server.js +40 -9
- package/cjs/storage-multi.js +525 -0
- package/cjs/tgql-d1.js +664 -0
- package/cjs/tgql.js +293 -172
- package/cjs/workspace/.claude/agents/tester.md +2 -2
- package/cjs/workspace/.claude/skills/build/SKILL.md +2 -2
- package/cjs/workspace/.claude/skills/test/SKILL.md +3 -2
- package/cjs/workspace/CLAUDE.md +2 -2
- package/cjs/workspace/README.md +1 -1
- package/cjs/workspace/docs/debug.md +9 -4
- package/cjs/workspace/docs/hyperbeam-devices.md +50 -1
- package/cjs/workspace/package.json +3 -3
- package/esm/accounts-web.js +14 -0
- package/esm/accounts.js +27 -0
- package/esm/adaptor-base.js +129 -31
- package/esm/adaptor-cf.js +11 -0
- package/esm/ao-loader.js +1 -1
- package/esm/ao.js +21 -2
- package/esm/aoconnect-base.js +248 -7
- package/esm/aoconnect-cf.js +9 -0
- package/esm/ar-remote.js +87 -0
- package/esm/armem-base.js +304 -53
- package/esm/armem-cf.js +67 -0
- package/esm/armem.js +7 -2
- package/esm/bar.js +126 -16
- package/esm/car.js +10 -0
- package/esm/cf-env.js +29 -0
- package/esm/cf.js +11 -0
- package/esm/cli.js +6 -2
- package/esm/create.js +1 -1
- package/esm/devs.js +15 -0
- package/esm/dodb.js +26 -0
- package/esm/hb.js +93 -16
- package/esm/hyperbeam.js +68 -30
- package/esm/keygen.js +47 -0
- package/esm/run.js +4 -1
- package/esm/server.js +29 -9
- package/esm/storage-multi.js +183 -0
- package/esm/tgql-d1.js +407 -0
- package/esm/tgql.js +29 -10
- package/esm/workspace/.claude/agents/tester.md +2 -2
- package/esm/workspace/.claude/skills/build/SKILL.md +2 -2
- package/esm/workspace/.claude/skills/test/SKILL.md +3 -2
- package/esm/workspace/CLAUDE.md +2 -2
- package/esm/workspace/README.md +1 -1
- package/esm/workspace/docs/debug.md +9 -4
- package/esm/workspace/docs/hyperbeam-devices.md +50 -1
- package/esm/workspace/package.json +3 -3
- package/package.json +10 -3
- package/postinstall.cjs +84 -0
|
@@ -29,7 +29,7 @@ yarn test # all tests
|
|
|
29
29
|
yarn test test/aos.test.js # specific file
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
The test command runs: `node --
|
|
32
|
+
The test command runs: `node --test --test-concurrency=1` (Node 24+ enables wasm-memory64 by default and rejects the experimental flag; Node 22 users add `--experimental-wasm-memory64` via NODE_OPTIONS).
|
|
33
33
|
|
|
34
34
|
## Systematic Debugging
|
|
35
35
|
|
|
@@ -59,7 +59,7 @@ Report test results with specifics: which tests pass, which fail, and the error
|
|
|
59
59
|
## Common Issues
|
|
60
60
|
|
|
61
61
|
- **Port already in use**: Kill stale `beam.smp` processes
|
|
62
|
-
- **WASM memory error**:
|
|
62
|
+
- **WASM memory error**: On Node 22 set `NODE_OPTIONS=--experimental-wasm-memory64`. On Node 24+ the flag is default-on (and rejected if passed explicitly — "bad option: --experimental-wasm-memory64")
|
|
63
63
|
- **Process not found**: Check that `src_data` path is correct
|
|
64
64
|
- **HyperBEAM timeout**: Ensure `hbeam.kill()` is called in `after()`
|
|
65
65
|
- **Send().receive() hangs**: Does NOT work on genesis-wasm — use fire-and-forget `Send()` + separate Handlers.add calls
|
|
@@ -210,5 +210,5 @@ Never force a task to `done` status with failing tests. The TaskCompleted hook w
|
|
|
210
210
|
- Verify task statuses — a task stuck as `in_progress` blocks progression
|
|
211
211
|
|
|
212
212
|
### Permission denied on yarn test
|
|
213
|
-
- Check `node --version` is
|
|
214
|
-
-
|
|
213
|
+
- Check `node --version` is 22+ (24+ recommended)
|
|
214
|
+
- On Node 22, set `NODE_OPTIONS=--experimental-wasm-memory64`; on Node 24+ the flag is default-on and is rejected if passed explicitly
|
|
@@ -51,5 +51,6 @@ yarn test
|
|
|
51
51
|
- Check `package.json` has `"wao"` in dependencies
|
|
52
52
|
|
|
53
53
|
### WASM memory error
|
|
54
|
-
-
|
|
55
|
-
- Node
|
|
54
|
+
- Node 24+ enables wasm-memory64 by default — DO NOT pass `--experimental-wasm-memory64` (it's rejected with "bad option")
|
|
55
|
+
- Node 22: set `NODE_OPTIONS=--experimental-wasm-memory64` before running
|
|
56
|
+
- Node.js 22+ required
|
package/cjs/workspace/CLAUDE.md
CHANGED
|
@@ -95,7 +95,7 @@ yarn deploy --mainnet # remote HyperBEAM (push-1)
|
|
|
95
95
|
yarn deploy --mainnet --lua # remote HyperBEAM (Lua mode)
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
Runs: `node --
|
|
98
|
+
Runs: `node --test --test-concurrency=1` (Node 24+; on Node 22 prefix with `--experimental-wasm-memory64`)
|
|
99
99
|
|
|
100
100
|
## Deployment Targets
|
|
101
101
|
|
|
@@ -109,7 +109,7 @@ Runs: `node --experimental-wasm-memory64 --test --test-concurrency=1`
|
|
|
109
109
|
- **Remote nodes**: Use `push-1` through `push-10` for full compute. `push.forward.computer` is push-only (no compute).
|
|
110
110
|
- **Lua mode**: Faster but no `receive()` — use `msg.reply()` pattern instead.
|
|
111
111
|
- **Wallet**: Run `yarn keygen` to generate `.wallet.json`.
|
|
112
|
-
- **HyperBEAM fork**: `git clone -b wao-
|
|
112
|
+
- **HyperBEAM fork**: `git clone -b wao-final https://github.com/weavedb/HyperBEAM.git`
|
|
113
113
|
|
|
114
114
|
### Frontend Commands
|
|
115
115
|
|
package/cjs/workspace/README.md
CHANGED
|
@@ -52,7 +52,7 @@ yarn deploy --local-hb --lua src/counter.lua
|
|
|
52
52
|
|
|
53
53
|
**Remote nodes**: Use `push-1` through `push-10` for full compute. `push.forward.computer` is push-only (no compute).
|
|
54
54
|
|
|
55
|
-
**HyperBEAM fork**: `git clone -b wao-
|
|
55
|
+
**HyperBEAM fork**: `git clone -b wao-final https://github.com/weavedb/HyperBEAM.git && cd HyperBEAM && rebar3 compile`
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
@@ -144,13 +144,17 @@ await hb.schedule({
|
|
|
144
144
|
|
|
145
145
|
**Problem:** `WebAssembly.Memory` errors or WASM module fails to load.
|
|
146
146
|
|
|
147
|
-
**Fix:**
|
|
147
|
+
**Fix:** Node 24+ enables wasm-memory64 by default (and rejects the experimental flag at startup with "bad option"). On Node 22 or older, prefix with `--experimental-wasm-memory64`:
|
|
148
148
|
|
|
149
149
|
```bash
|
|
150
|
+
# Node 24+: no flag needed
|
|
151
|
+
node --test test/aos.test.js
|
|
152
|
+
|
|
153
|
+
# Node 22:
|
|
150
154
|
node --experimental-wasm-memory64 --test test/aos.test.js
|
|
151
155
|
```
|
|
152
156
|
|
|
153
|
-
The `yarn test` script
|
|
157
|
+
The `yarn test` script ships without the flag (Node 24+ default); set `NODE_OPTIONS=--experimental-wasm-memory64` if you're on older Node.
|
|
154
158
|
|
|
155
159
|
### Genesis-WASM Server
|
|
156
160
|
|
|
@@ -202,7 +206,8 @@ The `yarn test` script should already include this flag.
|
|
|
202
206
|
| `badarg in list_to_atom` | Atom not pre-registered | Add to preRegisterAtoms |
|
|
203
207
|
| `multiple_matches` | Duplicate message content | Add nonce to messages |
|
|
204
208
|
| `timeout` | Process or server hung | Kill stale processes, increase timeout |
|
|
205
|
-
| `WASM memory error` | Missing memory64
|
|
209
|
+
| `WASM memory error` | Missing memory64 (Node 22) | Set `NODE_OPTIONS=--experimental-wasm-memory64` (Node 22) — Node 24+ has it default-on |
|
|
210
|
+
| `bad option: --experimental-wasm-memory64` | Passing the flag on Node 24+ | Remove the flag — Node 24+ rejects it |
|
|
206
211
|
| `Process not found` | Bad pid or deploy failed | Check deploy result, verify pid |
|
|
207
212
|
| `Insufficient balance` | Payment balance too low | Top up via simple-pay or p4 |
|
|
208
213
|
| `Not signed` | Missing wallet/JWK | Call `.init(jwk)` before operations |
|
|
@@ -215,7 +220,7 @@ When tests fail, check in order:
|
|
|
215
220
|
|
|
216
221
|
1. **Ports clear?** `lsof -ti :10000-10010 | xargs -r kill -9`
|
|
217
222
|
2. **beam.smp killed?** `pkill -f beam.smp`
|
|
218
|
-
3. **WASM flag?** `--experimental-wasm-memory64`
|
|
223
|
+
3. **WASM flag?** Node 24+ default-on; on Node 22 add `--experimental-wasm-memory64` (and on Node 24+ remove it — it's rejected)
|
|
219
224
|
4. **HyperBEAM dir?** `ls ./HyperBEAM` exists
|
|
220
225
|
5. **Wallet exists?** `.wallet.json` present (auto-generated)
|
|
221
226
|
6. **hbeam.kill() in after()?** Always clean up
|
|
@@ -348,7 +348,7 @@ const result = await hb.computeLegacy({ pid, slot })
|
|
|
348
348
|
**Limitations:**
|
|
349
349
|
- External CU is single-pass — **`Send().receive()` does NOT work**
|
|
350
350
|
- Auto-starts CU server at port 6363
|
|
351
|
-
- Requires `--experimental-wasm-memory64`
|
|
351
|
+
- Requires wasm-memory64 (Node 24+ default; Node 22 needs `--experimental-wasm-memory64`)
|
|
352
352
|
|
|
353
353
|
Functions: `init/3`, `compute/3`, `snapshot/3`, `import/3`
|
|
354
354
|
|
|
@@ -540,6 +540,55 @@ Query Arweave via GraphQL from within HyperBEAM.
|
|
|
540
540
|
|
|
541
541
|
---
|
|
542
542
|
|
|
543
|
+
## v0.9-FINAL Devices (new since beta3)
|
|
544
|
+
|
|
545
|
+
### location@1.0 — Scheduler-Location Registry
|
|
546
|
+
|
|
547
|
+
Replaces the old `/~scheduler@1.0/location` endpoint. Stores per-address scheduler-location records (with DNS/IP resolution and a TTL) in a node-local cache, with fallback to the Arweave gateway.
|
|
548
|
+
|
|
549
|
+
- `POST /~location@1.0/node` — operator generates and registers a record (signed)
|
|
550
|
+
- `POST /~location@1.0/known` — cache a peer's record if newer than the local one
|
|
551
|
+
- Read via `dev_whois` / `dev_location_cache`
|
|
552
|
+
|
|
553
|
+
### metering@1.0 — Dynamic P4 Pricing
|
|
554
|
+
|
|
555
|
+
A P4 pricing device that records resource usage per request/response lifecycle.
|
|
556
|
+
|
|
557
|
+
- `estimate/3` opens a process-local session
|
|
558
|
+
- `consume/3` increments usage during the session
|
|
559
|
+
- `price/3` closes the session and returns the integer charge
|
|
560
|
+
- Operator sets `metering-rates` in node message (resource → AO units per resource unit)
|
|
561
|
+
|
|
562
|
+
### blacklist@1.0 — Content Moderation by Blacklist
|
|
563
|
+
|
|
564
|
+
A request hook for filtering. Operator configures `blacklist-providers` (list) in node message; each provider returns a moderation set.
|
|
565
|
+
|
|
566
|
+
### bundler@1.0 — ANS-104 Bundler Integration
|
|
567
|
+
|
|
568
|
+
Outbound bundler for posting ANS-104 transactions to upstream Arweave bundlers (e.g. `up.arweave.net`).
|
|
569
|
+
|
|
570
|
+
### gzip@1.0 — gzip Codec
|
|
571
|
+
|
|
572
|
+
Compress/decompress message bodies via gzip during processing.
|
|
573
|
+
|
|
574
|
+
### rate-limit@1.0 — Per-Address Rate Limiting
|
|
575
|
+
|
|
576
|
+
Operator-configurable per-address rate limits applied as a request hook.
|
|
577
|
+
|
|
578
|
+
### tx@1.0 — Arweave TX Helpers
|
|
579
|
+
|
|
580
|
+
Helpers for fetching and validating Arweave transactions inline.
|
|
581
|
+
|
|
582
|
+
### b32-name@1.0 — Base-32 Name Encoding
|
|
583
|
+
|
|
584
|
+
Helpers for base-32 name encoding/decoding used by other devices.
|
|
585
|
+
|
|
586
|
+
### arweave@2.9 — Arweave Block + GraphQL Integration
|
|
587
|
+
|
|
588
|
+
Renamed in v0.9-FINAL (was `arweave@2.9-pre`). Provides Arweave block cache, offset helpers, and common Arweave protocol primitives.
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
543
592
|
## Multi-Instance Setup
|
|
544
593
|
|
|
545
594
|
Running multiple HyperBEAM nodes requires unique ports and Erlang node names:
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"version": "0.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"test": "node --
|
|
7
|
-
"test-only": "node --
|
|
8
|
-
"test-all": "node --
|
|
6
|
+
"test": "node --test --test-concurrency=1",
|
|
7
|
+
"test-only": "node --test-only --test-concurrency=1",
|
|
8
|
+
"test-all": "node --test --test-concurrency=1 test/**/*.test.js",
|
|
9
9
|
"start": "trap 'kill $(jobs -p)' EXIT; node dashboard/server.js & cd dashboard && npx vite",
|
|
10
10
|
"start:api": "node dashboard/server.js",
|
|
11
11
|
"keygen": "node scripts/keygen.js",
|
package/esm/accounts-web.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as WarpArBundles from "warp-arbundles"
|
|
2
2
|
const pkg = WarpArBundles.default ?? WarpArBundles
|
|
3
3
|
const { createData, ArweaveSigner } = pkg
|
|
4
|
+
import { toAddr } from "./utils.js"
|
|
4
5
|
|
|
5
6
|
function createDataItemSigner(wallet) {
|
|
6
7
|
const signer = async ({ data, tags, target, anchor }) => {
|
|
@@ -107,6 +108,19 @@ let mu = {
|
|
|
107
108
|
addr: "eNaLJLsMiWCSWvQKNbk_YT-9ydeWl9lrWwXxLVp9kcg",
|
|
108
109
|
}
|
|
109
110
|
|
|
111
|
+
if (globalThis.__WAO_WALLETS__) {
|
|
112
|
+
const w = globalThis.__WAO_WALLETS__
|
|
113
|
+
for (let i = 0; i < 3; i++) {
|
|
114
|
+
if (w[`acc${i}`]) {
|
|
115
|
+
const jwk = w[`acc${i}`]
|
|
116
|
+
acc[i] = { jwk, addr: toAddr(jwk.n) }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (w.su) su = { jwk: w.su, addr: toAddr(w.su.n) }
|
|
120
|
+
if (w.cu) cu = { jwk: w.cu, addr: toAddr(w.cu.n) }
|
|
121
|
+
if (w.mu) mu = { jwk: w.mu, addr: toAddr(w.mu.n) }
|
|
122
|
+
}
|
|
123
|
+
|
|
110
124
|
for (const v of acc) v.signer = createDataItemSigner(v.jwk)
|
|
111
125
|
mu.signer = createDataItemSigner(mu.jwk)
|
|
112
126
|
su.signer = createDataItemSigner(su.jwk)
|
package/esm/accounts.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { createDataItemSigner } from "@permaweb/aoconnect-69"
|
|
2
2
|
import { ArweaveSigner } from "arbundles"
|
|
3
|
+
import { readFileSync } from "fs"
|
|
4
|
+
import { fileURLToPath } from "url"
|
|
5
|
+
import { dirname as pathDirname, resolve } from "path"
|
|
6
|
+
import { toAddr } from "./utils.js"
|
|
3
7
|
const acc = [
|
|
4
8
|
{
|
|
5
9
|
jwk: {
|
|
@@ -90,6 +94,29 @@ let mu = {
|
|
|
90
94
|
addr: "eNaLJLsMiWCSWvQKNbk_YT-9ydeWl9lrWwXxLVp9kcg",
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
try {
|
|
98
|
+
const __fn = fileURLToPath(import.meta.url)
|
|
99
|
+
const __dir = pathDirname(__fn)
|
|
100
|
+
const walletsDir = resolve(__dir, "../devnet/.wallets")
|
|
101
|
+
const tryLoadJWK = name => {
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(readFileSync(resolve(walletsDir, `${name}.json`), "utf-8"))
|
|
104
|
+
} catch {
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (let i = 0; i < 3; i++) {
|
|
109
|
+
const jwk = tryLoadJWK(`acc${i}`)
|
|
110
|
+
if (jwk) acc[i] = { jwk, addr: toAddr(jwk.n) }
|
|
111
|
+
}
|
|
112
|
+
const suJwk = tryLoadJWK("su")
|
|
113
|
+
if (suJwk) su = { jwk: suJwk, addr: toAddr(suJwk.n) }
|
|
114
|
+
const cuJwk = tryLoadJWK("cu")
|
|
115
|
+
if (cuJwk) cu = { jwk: cuJwk, addr: toAddr(cuJwk.n) }
|
|
116
|
+
const muJwk = tryLoadJWK("mu")
|
|
117
|
+
if (muJwk) mu = { jwk: muJwk, addr: toAddr(muJwk.n) }
|
|
118
|
+
} catch {}
|
|
119
|
+
|
|
93
120
|
for (const v of acc) v.signer = createDataItemSigner(v.jwk)
|
|
94
121
|
mu.signer = createDataItemSigner(mu.jwk)
|
|
95
122
|
su.signer = createDataItemSigner(su.jwk)
|
package/esm/adaptor-base.js
CHANGED
|
@@ -14,13 +14,37 @@ class Adaptor {
|
|
|
14
14
|
aoconnect,
|
|
15
15
|
log = false,
|
|
16
16
|
db,
|
|
17
|
+
storage,
|
|
17
18
|
connect,
|
|
18
19
|
GQL,
|
|
19
20
|
cu,
|
|
20
21
|
su,
|
|
21
22
|
mu,
|
|
23
|
+
d1,
|
|
24
|
+
r2,
|
|
25
|
+
kv,
|
|
26
|
+
network = "ao.DN.1",
|
|
22
27
|
}) {
|
|
28
|
+
this.network = network
|
|
23
29
|
this.data = {}
|
|
30
|
+
this._dataTimestamps = {}
|
|
31
|
+
// Clean up abandoned chunk uploads every 60 seconds.
|
|
32
|
+
// unref() so this interval doesn't keep the Node event loop alive after
|
|
33
|
+
// the host server closes — without it, test processes that use Server
|
|
34
|
+
// (e.g. test/hyperbeam/relay.test.js) hang indefinitely waiting on this
|
|
35
|
+
// timer even after every other handle is cleaned up.
|
|
36
|
+
this._chunkCleanupInterval = setInterval(() => {
|
|
37
|
+
const now = Date.now()
|
|
38
|
+
for (const key of Object.keys(this._dataTimestamps)) {
|
|
39
|
+
if (now - this._dataTimestamps[key] > 5 * 60 * 1000) { // 5 min timeout
|
|
40
|
+
delete this.data[key]
|
|
41
|
+
delete this._dataTimestamps[key]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}, 60 * 1000)
|
|
45
|
+
if (typeof this._chunkCleanupInterval?.unref === "function") {
|
|
46
|
+
this._chunkCleanupInterval.unref()
|
|
47
|
+
}
|
|
24
48
|
let hb = null
|
|
25
49
|
if (hb_url) hb = new HB({ url: hb_url })
|
|
26
50
|
const {
|
|
@@ -34,13 +58,15 @@ class Adaptor {
|
|
|
34
58
|
monitor,
|
|
35
59
|
unmonitor,
|
|
36
60
|
recover,
|
|
37
|
-
|
|
61
|
+
evaluate,
|
|
62
|
+
} = connect(aoconnect, { log, cache: db, storage, hb, d1, r2, kv })
|
|
38
63
|
this.su_signer = su
|
|
39
64
|
this.cu_signer = cu
|
|
40
65
|
this.mu_signer = mu
|
|
41
66
|
this.recover = recover
|
|
42
67
|
this.monitor = monitor
|
|
43
68
|
this.unmonitor = unmonitor
|
|
69
|
+
this.evaluate = evaluate
|
|
44
70
|
this.spawn = spawn
|
|
45
71
|
this._ar = _ar
|
|
46
72
|
this.message = message
|
|
@@ -48,10 +74,10 @@ class Adaptor {
|
|
|
48
74
|
this.result = result
|
|
49
75
|
this.results = results
|
|
50
76
|
this.mem = mem
|
|
51
|
-
this.gql = new GQL({ mem })
|
|
77
|
+
this.gql = new GQL({ mem, d1: d1 || null })
|
|
52
78
|
}
|
|
53
79
|
async get(req, res) {
|
|
54
|
-
res(await this[req.device](req))
|
|
80
|
+
return res(await this[req.device](req))
|
|
55
81
|
}
|
|
56
82
|
async bd(req) {
|
|
57
83
|
return await this[`bd_${req.method.toLowerCase()}`](req)
|
|
@@ -189,6 +215,8 @@ class Adaptor {
|
|
|
189
215
|
return await this.cu_post_result(req)
|
|
190
216
|
case "/dry-run": // not in the AO spec
|
|
191
217
|
return await this.cu_post_dryrun(req)
|
|
218
|
+
case "/evaluate":
|
|
219
|
+
return await this.cu_post_evaluate(req)
|
|
192
220
|
default:
|
|
193
221
|
return await this.bad()
|
|
194
222
|
}
|
|
@@ -271,7 +299,8 @@ class Adaptor {
|
|
|
271
299
|
|
|
272
300
|
async cu_get_state({ query, params, body, headers, method }) {
|
|
273
301
|
const pid = params.pid
|
|
274
|
-
const
|
|
302
|
+
const p = await this.mem.get("env", pid)
|
|
303
|
+
const memory = p?.memory ?? null
|
|
275
304
|
if (!memory) {
|
|
276
305
|
return {
|
|
277
306
|
status: 404,
|
|
@@ -293,7 +322,8 @@ class Adaptor {
|
|
|
293
322
|
async cu_get_results({ query, params, body, headers, method }) {
|
|
294
323
|
const pid = params.pid
|
|
295
324
|
const { from = null, to = null, sort = "ASC", limit = 25 } = query
|
|
296
|
-
|
|
325
|
+
const p = await this.mem.get("env", pid)
|
|
326
|
+
let results = p?.results ?? []
|
|
297
327
|
if (sort === "DESC") results = reverse(results)
|
|
298
328
|
let _res = []
|
|
299
329
|
let i = 1
|
|
@@ -301,7 +331,8 @@ class Adaptor {
|
|
|
301
331
|
let started = isNil(from)
|
|
302
332
|
for (let v of results) {
|
|
303
333
|
if (started) {
|
|
304
|
-
|
|
334
|
+
const msg = await this.mem.get("msgs", v)
|
|
335
|
+
_res.push({ cursor: v, node: msg?.res })
|
|
305
336
|
count++
|
|
306
337
|
if (!isNil(to) && v === to) break
|
|
307
338
|
if (limit <= count) break
|
|
@@ -316,20 +347,23 @@ class Adaptor {
|
|
|
316
347
|
let message = params.mid
|
|
317
348
|
const process = query["process-id"]
|
|
318
349
|
// check if recovery is ongoing and
|
|
319
|
-
|
|
350
|
+
let p = await this.mem.get("env", process)
|
|
351
|
+
if (isNil(p)) {
|
|
320
352
|
const { success } = await this.recover(process)
|
|
321
353
|
if (!success) {
|
|
322
354
|
console.log("process not found:", query["process-id"])
|
|
323
355
|
return { status: 404, error: "not Found" }
|
|
324
356
|
}
|
|
357
|
+
p = await this.mem.get("env", process)
|
|
325
358
|
}
|
|
326
359
|
const slot = message
|
|
327
360
|
if (!/^[0-9a-zA-Z_-]{43,44}$/.test(message)) {
|
|
328
|
-
message =
|
|
361
|
+
message = p?.results?.[slot]
|
|
329
362
|
}
|
|
330
363
|
if (isNil(message)) {
|
|
331
364
|
await this.recover(process)
|
|
332
|
-
|
|
365
|
+
p = await this.mem.get("env", process)
|
|
366
|
+
message = p?.results?.[slot]
|
|
333
367
|
if (isNil(message)) return { status: 404, error: "not Found" }
|
|
334
368
|
}
|
|
335
369
|
const res2 = await this.result({ message, process })
|
|
@@ -338,34 +372,57 @@ class Adaptor {
|
|
|
338
372
|
async cu_post_result(...args) {
|
|
339
373
|
return await this.cu_get_result(...args)
|
|
340
374
|
}
|
|
375
|
+
async cu_post_evaluate({ query, params, body, headers, method }) {
|
|
376
|
+
const { message, process, data, tags: msgTags, from, is_spawn } = body
|
|
377
|
+
if (!process) return { status: 400, json: { error: "process required" } }
|
|
378
|
+
try {
|
|
379
|
+
const res = await this.evaluate({
|
|
380
|
+
message,
|
|
381
|
+
process,
|
|
382
|
+
data: data || "",
|
|
383
|
+
tags: msgTags,
|
|
384
|
+
from,
|
|
385
|
+
is_spawn,
|
|
386
|
+
module: body.module || (msgTags && tags(msgTags)?.Module),
|
|
387
|
+
scheduler: body.scheduler || (msgTags && tags(msgTags)?.Scheduler),
|
|
388
|
+
})
|
|
389
|
+
return { json: res || {} }
|
|
390
|
+
} catch (e) {
|
|
391
|
+
console.log("cu_post_evaluate error:", e)
|
|
392
|
+
return { status: 500, json: { error: e.message } }
|
|
393
|
+
}
|
|
394
|
+
}
|
|
341
395
|
async su_get_root({ query, params, body, headers, method }) {
|
|
342
396
|
return {
|
|
343
397
|
json: {
|
|
344
398
|
Unit: "Scheduler",
|
|
345
399
|
Timestamp: Date.now(),
|
|
346
400
|
Address: this.su_signer.addr,
|
|
347
|
-
Processes:
|
|
401
|
+
Processes: await this.mem.getFieldKeys("env"),
|
|
348
402
|
},
|
|
349
403
|
}
|
|
350
404
|
}
|
|
351
405
|
|
|
352
406
|
async su_get_timestamp({ query, params, body, headers, method }) {
|
|
353
|
-
return { json: { timestamp: Date.now(), block_height: this.mem.height } }
|
|
407
|
+
return { json: { timestamp: Date.now(), block_height: await this.mem.get("height") } }
|
|
354
408
|
}
|
|
355
409
|
async su_get_pid({ query, params, body, headers, method }) {
|
|
356
410
|
const pid = params.pid
|
|
411
|
+
const p = await this.mem.get("env", pid)
|
|
357
412
|
const edges = map(async v => {
|
|
358
413
|
const tx = await this.mem.getTx(v)
|
|
359
414
|
const _tags = tags(v.tags)
|
|
360
415
|
const mid = _tags.Message
|
|
361
416
|
const mtx = await this.mem.getTx(mid)
|
|
417
|
+
const mtxOwner = await this.mem.get("addrmap", mtx.owner)
|
|
418
|
+
const txOwner = await this.mem.get("addrmap", tx.owner)
|
|
362
419
|
return {
|
|
363
420
|
cursor: v,
|
|
364
421
|
node: {
|
|
365
422
|
message: {
|
|
366
423
|
id: mtx.id,
|
|
367
424
|
tags: mtx.tags,
|
|
368
|
-
owner:
|
|
425
|
+
owner: mtxOwner,
|
|
369
426
|
anchor: mtx.anchor ?? null,
|
|
370
427
|
target: pid,
|
|
371
428
|
signature: mtx.signature,
|
|
@@ -374,14 +431,14 @@ class Adaptor {
|
|
|
374
431
|
assignment: {
|
|
375
432
|
id: tx.id,
|
|
376
433
|
tags: tx.tags,
|
|
377
|
-
owner:
|
|
434
|
+
owner: txOwner,
|
|
378
435
|
anchor: tx.anchor ?? null,
|
|
379
436
|
target: null,
|
|
380
437
|
signature: tx.signature,
|
|
381
438
|
},
|
|
382
439
|
},
|
|
383
440
|
}
|
|
384
|
-
})(reverse(
|
|
441
|
+
})(reverse(p?.results ?? [])) // need mod
|
|
385
442
|
return { json: { page_info: { has_next_page: false }, edges } }
|
|
386
443
|
}
|
|
387
444
|
|
|
@@ -398,23 +455,48 @@ class Adaptor {
|
|
|
398
455
|
try {
|
|
399
456
|
valid = await DataItem.verify(body)
|
|
400
457
|
} catch (e) {
|
|
401
|
-
|
|
458
|
+
// verification may fail for cross-env items (node→browser); proceed anyway
|
|
402
459
|
}
|
|
403
460
|
let type = null
|
|
404
461
|
let item = null
|
|
405
462
|
if (valid || true) item = new DataItem(body)
|
|
406
463
|
await item.setSignature(item.rawSignature)
|
|
407
464
|
const _tags = tags(item.tags)
|
|
465
|
+
// Allow both ao.TN.1 (legacy testnet) and ao.DN.1 (WAO Devnet) variants
|
|
466
|
+
// through the local emulator. The variant is informational here; the
|
|
467
|
+
// local emulator doesn't actually need to enforce a strict network.
|
|
468
|
+
const _allowedNetworks = new Set([this.network, "ao.TN.1", "ao.DN.1"])
|
|
469
|
+
if (_tags.Variant && !_allowedNetworks.has(_tags.Variant)) {
|
|
470
|
+
return { status: 400, json: { error: `Variant mismatch: expected ${this.network}, got ${_tags.Variant}` } }
|
|
471
|
+
}
|
|
408
472
|
let err = null
|
|
409
473
|
if (_tags.Type === "Process") {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
474
|
+
try {
|
|
475
|
+
const res = await this.spawn({
|
|
476
|
+
item,
|
|
477
|
+
module: _tags.Module,
|
|
478
|
+
scheduler: _tags.Scheduler,
|
|
479
|
+
_cu_url: _tags["CU-URL"] || undefined,
|
|
480
|
+
})
|
|
481
|
+
if (!res) err = "bad requrest"
|
|
482
|
+
} catch (e) {
|
|
483
|
+
console.error("MU spawn error:", e.message, e.stack)
|
|
484
|
+
return { status: 500, json: { error: e.message, stack: (e.stack || "").split("\n").slice(0, 8) } }
|
|
485
|
+
}
|
|
416
486
|
} else if (_tags.Type === "Message") {
|
|
417
|
-
|
|
487
|
+
try {
|
|
488
|
+
await this.message({ item, process: item.target })
|
|
489
|
+
} catch (e) {
|
|
490
|
+
console.error("MU message error:", e.message, e.stack)
|
|
491
|
+
err = e.message
|
|
492
|
+
}
|
|
493
|
+
} else if (_tags.Type === "Scheduler-Location" || _tags.Type === "Scheduler-Transfer") {
|
|
494
|
+
try {
|
|
495
|
+
await this._ar.postItems(item, this.su_signer.jwk)
|
|
496
|
+
} catch (e) {
|
|
497
|
+
console.error(`MU ${_tags.Type} error:`, e.message, e.stack)
|
|
498
|
+
err = e.message
|
|
499
|
+
}
|
|
418
500
|
} else err = true
|
|
419
501
|
|
|
420
502
|
if (err) return { status: 400, error: err }
|
|
@@ -435,8 +517,8 @@ class Adaptor {
|
|
|
435
517
|
json: {
|
|
436
518
|
version: 1,
|
|
437
519
|
timestamp: Date.now(),
|
|
438
|
-
height: this.mem.height,
|
|
439
|
-
network:
|
|
520
|
+
height: await this.mem.get("height"),
|
|
521
|
+
network: this.network,
|
|
440
522
|
current: this.mem.getAnchor(),
|
|
441
523
|
},
|
|
442
524
|
}
|
|
@@ -459,7 +541,20 @@ class Adaptor {
|
|
|
459
541
|
async ar_get_id({ query, params, body, headers, method }) {
|
|
460
542
|
const _data = await this._ar.data(params.id)
|
|
461
543
|
if (!_data) return { status: 404, send: null }
|
|
462
|
-
|
|
544
|
+
const tx = await this.mem.getTx(params.id)
|
|
545
|
+
let contentType = tx?._data?.type || ""
|
|
546
|
+
if (!contentType && tx?.tags) {
|
|
547
|
+
const ctTag = tx.tags.find(t => t.name === "Content-Type")
|
|
548
|
+
if (ctTag) contentType = ctTag.value
|
|
549
|
+
}
|
|
550
|
+
if (!contentType) contentType = "application/octet-stream"
|
|
551
|
+
const send = Buffer.isBuffer(_data) || _data instanceof Uint8Array
|
|
552
|
+
? _data
|
|
553
|
+
: Buffer.from(String(_data), "base64")
|
|
554
|
+
return {
|
|
555
|
+
send,
|
|
556
|
+
headers: { "Content-Type": contentType },
|
|
557
|
+
}
|
|
463
558
|
}
|
|
464
559
|
async ar_get_price({ query, params, body, headers, method }) {
|
|
465
560
|
return { send: "0" }
|
|
@@ -468,16 +563,16 @@ class Adaptor {
|
|
|
468
563
|
try {
|
|
469
564
|
const { query, variables } = body
|
|
470
565
|
const { tar, args } = toGraphObj({ query, variables })
|
|
566
|
+
const first = args.first ?? 10
|
|
471
567
|
let res2 = null
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}
|
|
568
|
+
const fn = tar === "blocks" ? "blocks" : "txs"
|
|
569
|
+
res2 = await this.gql[fn]({ ...args, first: first + 1 })
|
|
570
|
+
const hasNextPage = res2.length > first
|
|
571
|
+
if (hasNextPage) res2 = res2.slice(0, first)
|
|
477
572
|
const edges = map(v => ({ node: v, cursor: v.cursor }), res2)
|
|
478
573
|
return {
|
|
479
574
|
json: {
|
|
480
|
-
data: { [tar]: { pageInfo: { hasNextPage
|
|
575
|
+
data: { [tar]: { pageInfo: { hasNextPage }, edges } },
|
|
481
576
|
},
|
|
482
577
|
}
|
|
483
578
|
} catch (e) {
|
|
@@ -489,6 +584,7 @@ class Adaptor {
|
|
|
489
584
|
// id = "tx" | "chunk"
|
|
490
585
|
if (body.chunk) {
|
|
491
586
|
if (this.data[body.data_root]) {
|
|
587
|
+
this._dataTimestamps[body.data_root] = Date.now()
|
|
492
588
|
this.data[body.data_root].data += body.chunk
|
|
493
589
|
const buf = Buffer.from(body.chunk, "base64")
|
|
494
590
|
if (!this.data[body.data_root].chunks) {
|
|
@@ -508,12 +604,14 @@ class Adaptor {
|
|
|
508
604
|
this.data[body.data_root].chunks.toString("base64")
|
|
509
605
|
await this._ar.postTx(this.data[body.data_root])
|
|
510
606
|
delete this.data[body.data_root]
|
|
607
|
+
delete this._dataTimestamps[body.data_root]
|
|
511
608
|
}
|
|
512
609
|
}
|
|
513
610
|
return { json: { id: body.id } }
|
|
514
611
|
} else {
|
|
515
612
|
if (body.data_root && body.data === "") {
|
|
516
613
|
this.data[body.data_root] = body
|
|
614
|
+
this._dataTimestamps[body.data_root] = Date.now()
|
|
517
615
|
} else {
|
|
518
616
|
await this._ar.postTx(body)
|
|
519
617
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { connect } from "./aoconnect-cf.js"
|
|
2
|
+
import { cu, su, mu } from "./accounts-web.js"
|
|
3
|
+
import GQL from "./tgql.js"
|
|
4
|
+
import Base from "./adaptor-base.js"
|
|
5
|
+
|
|
6
|
+
class Adaptor extends Base {
|
|
7
|
+
constructor(obj) {
|
|
8
|
+
super({ ...obj, GQL, cu, su, mu, connect })
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export default Adaptor
|
package/esm/ao-loader.js
CHANGED
|
@@ -24000,7 +24000,7 @@ var require_wasm64_emscripten = __commonJS({
|
|
|
24000
24000
|
_emscripten_get_now = () => deterministicNow()
|
|
24001
24001
|
var growMemory = size => {
|
|
24002
24002
|
var b = wasmMemory.buffer
|
|
24003
|
-
var pages = (size - b.byteLength
|
|
24003
|
+
var pages = BigInt(Math.ceil((size - b.byteLength) / 65536))
|
|
24004
24004
|
try {
|
|
24005
24005
|
wasmMemory.grow(pages)
|
|
24006
24006
|
updateMemoryViews()
|
package/esm/ao.js
CHANGED
|
@@ -99,7 +99,15 @@ class AO {
|
|
|
99
99
|
if (_hb) {
|
|
100
100
|
this.format = _hb === "ans104" ? _hb : "httpsig"
|
|
101
101
|
const _hbOpt = { format: this.format }
|
|
102
|
-
|
|
102
|
+
// `hb` can be "ans104" / "httpsig" (format selector with default URL),
|
|
103
|
+
// or an actual URL string. Only treat it as a URL when it looks like one.
|
|
104
|
+
if (
|
|
105
|
+
typeof _hb === "string" &&
|
|
106
|
+
_hb !== "ans104" &&
|
|
107
|
+
_hb !== "httpsig" &&
|
|
108
|
+
/^(https?:\/\/|\/\/|[a-zA-Z0-9_.-]+:\d+)/.test(_hb)
|
|
109
|
+
)
|
|
110
|
+
_hbOpt.url = _hb
|
|
103
111
|
this.hb = new HB(_hbOpt)
|
|
104
112
|
this.mode = mode ?? "legacy"
|
|
105
113
|
}
|
|
@@ -679,8 +687,19 @@ class AO {
|
|
|
679
687
|
mid = (await this.hb.scheduleLegacy(schedArgs)).slot
|
|
680
688
|
try {
|
|
681
689
|
res = await this.hb.computeLegacy({ pid, slot: mid })
|
|
690
|
+
// v0.9-FINAL's push device can enter long-running recursive loops
|
|
691
|
+
// for cross-process Send().receive() coroutines. Race the push
|
|
692
|
+
// call against a 20s timeout so msg() can return rather than
|
|
693
|
+
// wait forever; the now/results probe below still has a chance
|
|
694
|
+
// to pick up a partial reply.
|
|
695
|
+
const _pushTimeoutMs = 20000
|
|
682
696
|
try {
|
|
683
|
-
await
|
|
697
|
+
await Promise.race([
|
|
698
|
+
this.hb.get({ path: `/${pid}/push`, slot: mid }),
|
|
699
|
+
new Promise((_r, rej) =>
|
|
700
|
+
setTimeout(() => rej(new Error("push timeout")), _pushTimeoutMs)
|
|
701
|
+
),
|
|
702
|
+
])
|
|
684
703
|
} catch (_e2) {}
|
|
685
704
|
// Push updates now/ to its recursive resolution result, which may
|
|
686
705
|
// include errors from other processes (cross-process trust errors).
|